From 87703534836168ecca2bcf6bc1719460e6b5b9bd Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 19 Sep 2006 22:06:50 +0000 Subject: Import of qpid from etp: URL: https://etp.108.redhat.com/svn/etp/trunk/blaze Repository Root: https://etp.108.redhat.com/svn/etp Repository UUID: 06e15bec-b515-0410-bef0-cc27a458cf48 Revision: 608 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@447994 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/README.txt | 24 +++ qpid/python/amqp-doc | 75 ++++++++ qpid/python/cpp_failing.txt | 0 qpid/python/doc/test-requirements.txt | 10 + qpid/python/java_failing.txt | 13 ++ qpid/python/pal2py | 255 +++++++++++++++++++++++++ qpid/python/qpid/__init__.py | 17 ++ qpid/python/qpid/client.py | 111 +++++++++++ qpid/python/qpid/codec.py | 221 +++++++++++++++++++++ qpid/python/qpid/connection.py | 265 ++++++++++++++++++++++++++ qpid/python/qpid/content.py | 47 +++++ qpid/python/qpid/delegate.py | 52 +++++ qpid/python/qpid/message.py | 81 ++++++++ qpid/python/qpid/peer.py | 209 ++++++++++++++++++++ qpid/python/qpid/queue.py | 42 ++++ qpid/python/qpid/spec.py | 349 ++++++++++++++++++++++++++++++++++ qpid/python/qpid/testlib.py | 221 +++++++++++++++++++++ qpid/python/qpid/xmlutil.py | 116 +++++++++++ qpid/python/rule2test | 89 +++++++++ qpid/python/run-tests | 24 +++ qpid/python/tests/__init__.py | 1 + qpid/python/tests/basic.py | 115 +++++++++++ qpid/python/tests/broker.py | 84 ++++++++ qpid/python/tests/example.py | 91 +++++++++ qpid/python/tests/exchange.py | 234 +++++++++++++++++++++++ qpid/python/tests/queue.py | 254 +++++++++++++++++++++++++ 26 files changed, 3000 insertions(+) create mode 100644 qpid/python/README.txt create mode 100755 qpid/python/amqp-doc create mode 100644 qpid/python/cpp_failing.txt create mode 100644 qpid/python/doc/test-requirements.txt create mode 100644 qpid/python/java_failing.txt create mode 100755 qpid/python/pal2py create mode 100644 qpid/python/qpid/__init__.py create mode 100644 qpid/python/qpid/client.py create mode 100644 qpid/python/qpid/codec.py create mode 100644 qpid/python/qpid/connection.py create mode 100644 qpid/python/qpid/content.py create mode 100644 qpid/python/qpid/delegate.py create mode 100644 qpid/python/qpid/message.py create mode 100644 qpid/python/qpid/peer.py create mode 100644 qpid/python/qpid/queue.py create mode 100644 qpid/python/qpid/spec.py create mode 100644 qpid/python/qpid/testlib.py create mode 100644 qpid/python/qpid/xmlutil.py create mode 100755 qpid/python/rule2test create mode 100755 qpid/python/run-tests create mode 100644 qpid/python/tests/__init__.py create mode 100644 qpid/python/tests/basic.py create mode 100644 qpid/python/tests/broker.py create mode 100644 qpid/python/tests/example.py create mode 100644 qpid/python/tests/exchange.py create mode 100644 qpid/python/tests/queue.py (limited to 'qpid/python') diff --git a/qpid/python/README.txt b/qpid/python/README.txt new file mode 100644 index 0000000000..0a64f0e2f2 --- /dev/null +++ b/qpid/python/README.txt @@ -0,0 +1,24 @@ += RUNNING THE PYTHON TESTS = + +The tests/ directory contains a collection of python unit tests to +exercise functions of a broker. + +Simplest way to run the tests: + + * Run a broker on the default port + + * ./run_tests + +For additional options: ./run_tests --help + + +== Expected failures == + +Until we complete functionality, tests may fail because the tested +functionality is missing in the broker. To skip expected failures +in the C++ or Java brokers: + + ./run_tests -I cpp_failing.txt + ./run_tests -I java_failing.txt + +If you fix a failure, please remove it from the corresponding list. diff --git a/qpid/python/amqp-doc b/qpid/python/amqp-doc new file mode 100755 index 0000000000..a5b785fd73 --- /dev/null +++ b/qpid/python/amqp-doc @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# +import sys, re +from qpid.spec import load, pythonize +from getopt import gnu_getopt as getopt, GetoptError +from fnmatch import fnmatchcase as fnmatch + +def die(msg): + print >> sys.stderr, msg + sys.exit(1) + +def usage(msg = ""): + return ("""%s + +Usage %s [] [ ... ] + +Options: + -e, --regexp use regex instead of glob when matching + -s, --spec location of amqp.xml +""" % (msg, sys.argv[0])).strip() + +try: + opts, args = getopt(sys.argv[1:], "s:e", ["regexp", "spec="]) +except GetoptError, e: + die(str(e)) + +regexp = False +spec = "../specs/amqp-8.0.xml" +for k, v in opts: + if k == "-e" or k == "--regexp": regexp = True + if k == "-s" or k == "--spec": spec = v + +if regexp: + def match(pattern, value): + try: + return re.match(pattern, value) + except Exception, e: + die("error: '%s': %s" % (pattern, e)) +else: + def match(pattern, value): + return fnmatch(value, pattern) + +spec = load(spec) +methods = {} +patterns = args +for pattern in patterns: + for c in spec.classes: + for m in c.methods: + name = pythonize("%s_%s" % (c.name, m.name)) + if match(pattern, name): + methods[name] = m.define_method(name) + +if patterns: + if methods: + AMQP = type("AMQP[%s]" % ", ".join(patterns), (), methods) + else: + die("no matches") +else: + AMQP = spec.define_class("AMQP") + +help(AMQP) diff --git a/qpid/python/cpp_failing.txt b/qpid/python/cpp_failing.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qpid/python/doc/test-requirements.txt b/qpid/python/doc/test-requirements.txt new file mode 100644 index 0000000000..a1ba414eb2 --- /dev/null +++ b/qpid/python/doc/test-requirements.txt @@ -0,0 +1,10 @@ + * start and stop server, possibly in different configurations, should + at least be able to specify host and port + + * initiate multiple connections/server + + * initiate multiple channels/connection + + * enable positive and negative tests for any protocol interaction + + * test harness must be as robust as possible to spec changes diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt new file mode 100644 index 0000000000..2e61363817 --- /dev/null +++ b/qpid/python/java_failing.txt @@ -0,0 +1,13 @@ +tests.basic.BasicTests.test_consume_exclusive +tests.basic.BasicTests.test_consume_no_local +tests.basic.BasicTests.test_consume_queue_errors +tests.basic.BasicTests.test_consume_unique_consumers +tests.exchange.RecommendedTypesRuleTests.testFanout +tests.exchange.RequiredInstancesRuleTests.testAmqFanOut +tests.queue.QueueTests.test_declare_exclusive +tests.queue.QueueTests.test_declare_passive +tests.queue.QueueTests.test_delete_ifempty +tests.queue.QueueTests.test_delete_ifunused +tests.queue.QueueTests.test_delete_simple +tests.queue.QueueTests.test_purge +tests.queue.QueueTests.test_bind diff --git a/qpid/python/pal2py b/qpid/python/pal2py new file mode 100755 index 0000000000..48fa0111de --- /dev/null +++ b/qpid/python/pal2py @@ -0,0 +1,255 @@ +#!/usr/bin/env python +import sys, os, xml + +from qpid.spec import load, pythonize +from textwrap import TextWrapper +from xml.sax.handler import ContentHandler + +class Block: + + def __init__(self, children): + self.children = children + + def emit(self, out): + for child in self.children: + if not hasattr(child, "emit"): + raise ValueError(child) + child.emit(out) + + if not self.children: + out.line("pass") + +class If: + + def __init__(self, expr, cons, alt = None): + self.expr = expr + self.cons = cons + self.alt = alt + + def emit(self, out): + out.line("if ") + self.expr.emit(out) + out.write(":") + out.level += 1 + self.cons.emit(out) + out.level -= 1 + if self.alt: + out.line("else:") + out.level += 1 + self.alt.emit(out) + out.level -= 1 + +class Stmt: + + def __init__(self, code): + self.code = code + + def emit(self, out): + out.line(self.code) + +class Expr: + + def __init__(self, code): + self.code = code + + def emit(self, out): + out.write(self.code) + +class Abort: + + def __init__(self, expr): + self.expr = expr + + def emit(self, out): + out.line("assert False, ") + self.expr.emit(out) + +WRAPPER = TextWrapper() + +def wrap(text): + return WRAPPER.wrap(" ".join(text.split())) + +class Doc: + + def __init__(self, text): + self.text = text + + def emit(self, out): + out.line('"""') + for line in wrap(self.text): + out.line(line) + out.line('"""') + +class Frame: + + def __init__(self, attrs): + self.attrs = attrs + self.children = [] + self.text = None + + def __getattr__(self, attr): + return self.attrs[attr] + +def isunicode(s): + if isinstance(s, str): + return False + for ch in s: + if ord(ch) > 127: + return True + return False + +def string_literal(s): + if s == None: + return None + if isunicode(s): + return "%r" % s + else: + return "%r" % str(s) + +TRUTH = { + "1": True, + "0": False, + "true": True, + "false": False + } + +LITERAL = { + "shortstr": string_literal, + "longstr": string_literal, + "bit": lambda s: TRUTH[s.lower()], + "longlong": lambda s: "%r" % long(s) + } + +def literal(s, field): + return LITERAL[field.type](s) + +def palexpr(s, field): + if s.startswith("$"): + return "msg.%s" % s[1:] + else: + return literal(s, field) + +class Translator(ContentHandler): + + def __init__(self, spec): + self.spec = spec + self.stack = [] + self.content = None + self.root = Frame(None) + self.push(self.root) + + def emit(self, out): + blk = Block(self.root.children) + blk.emit(out) + out.write("\n") + + def peek(self): + return self.stack[-1] + + def pop(self): + return self.stack.pop() + + def push(self, frame): + self.stack.append(frame) + + def startElement(self, name, attrs): + self.push(Frame(attrs)) + + def endElement(self, name): + frame = self.pop() + if hasattr(self, name): + child = getattr(self, name)(frame) + else: + child = self.handle(name, frame) + + if child: + self.peek().children.append(child) + + def characters(self, text): + frame = self.peek() + if frame.text: + frame.text += text + else: + frame.text = text + + def handle(self, name, frame): + for klass in self.spec.classes: + pyklass = pythonize(klass.name) + if name.startswith(pyklass): + name = name[len(pyklass) + 1:] + break + else: + raise ValueError("unknown class: %s" % name) + + for method in klass.methods: + pymethod = pythonize(method.name) + if name == pymethod: + break + else: + raise ValueError("unknown method: %s" % name) + + args = ["%s = %s" % (key, palexpr(val, method.fields.bypyname[key])) + for key, val in frame.attrs.items()] + if method.content and self.content: + args.append("content = %r" % string_literal(self.content)) + code = "ssn.%s_%s(%s)" % (pyklass, pymethod, ", ".join(args)) + if pymethod == "consume": + code = "consumer_tag = %s.consumer_tag" % code + return Stmt(code) + + def pal(self, frame): + return Block([Doc(frame.text)] + frame.children) + + def include(self, frame): + base, ext = os.path.splitext(frame.filename) + return Stmt("from %s import *" % base) + + def session(self, frame): + return Block([Stmt("cli = open()"), Stmt("ssn = cli.channel(0)"), + Stmt("ssn.channel_open()")] + frame.children) + + def empty(self, frame): + return If(Expr("msg == None"), Block(frame.children)) + + def abort(self, frame): + return Abort(Expr(string_literal(frame.text))) + + def wait(self, frame): + return Stmt("msg = ssn.queue(consumer_tag).get(timeout=%r)" % + (int(frame.timeout)/1000)) + + def basic_arrived(self, frame): + if frame.children: + return If(Expr("msg != None"), Block(frame.children)) + + def basic_content(self, frame): + self.content = frame.text + +class Emitter: + + def __init__(self, out): + self.out = out + self.level = 0 + + def write(self, code): + self.out.write(code) + + def line(self, code): + self.write("\n%s%s" % (" "*self.level, code)) + + def flush(self): + self.out.flush() + + def close(self): + self.out.close() + + +for f in sys.argv[2:]: + base, ext = os.path.splitext(f) + spec = load(sys.argv[1]) + t = Translator(spec) + xml.sax.parse(f, t) +# out = Emitter(open("%s.py" % base)) + out = Emitter(sys.stdout) + t.emit(out) + out.close() diff --git a/qpid/python/qpid/__init__.py b/qpid/python/qpid/__init__.py new file mode 100644 index 0000000000..3f69e88e24 --- /dev/null +++ b/qpid/python/qpid/__init__.py @@ -0,0 +1,17 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +import spec, codec, connection, content, peer, delegate, client diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py new file mode 100644 index 0000000000..cef10622ac --- /dev/null +++ b/qpid/python/qpid/client.py @@ -0,0 +1,111 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +An AQMP client implementation that uses a custom delegate for +interacting with the server. +""" + +import threading +from peer import Peer, Closed +from delegate import Delegate +from connection import Connection, Frame +from spec import load +from queue import Queue + + +class Client: + + def __init__(self, host, port, spec, vhost = None): + self.host = host + self.port = port + self.spec = spec + + self.mechanism = None + self.response = None + self.locale = None + + self.vhost = vhost + if self.vhost == None: + self.vhost = self.host + + self.queues = {} + self.lock = threading.Lock() + + self.closed = False + self.started = threading.Event() + + self.conn = Connection(self.host, self.port, self.spec) + self.peer = Peer(self.conn, ClientDelegate(self)) + + def wait(self): + self.started.wait() + if self.closed: + raise EOFError() + + def queue(self, key): + self.lock.acquire() + try: + try: + q = self.queues[key] + except KeyError: + q = Queue(0) + self.queues[key] = q + finally: + self.lock.release() + return q + + def start(self, response, mechanism="AMQPLAIN", locale="en_US"): + self.mechanism = mechanism + self.response = response + self.locale = locale + + self.conn.connect() + self.conn.init() + self.peer.start() + self.wait() + self.channel(0).connection_open(self.vhost) + + def channel(self, id): + return self.peer.channel(id) + +class ClientDelegate(Delegate): + + def __init__(self, client): + Delegate.__init__(self) + self.client = client + + def connection_start(self, ch, msg): + ch.connection_start_ok(mechanism=self.client.mechanism, + response=self.client.response, + locale=self.client.locale) + + def connection_tune(self, ch, msg): + ch.connection_tune_ok(*msg.fields) + self.client.started.set() + + def basic_deliver(self, ch, msg): + self.client.queue(msg.consumer_tag).put(msg) + + def channel_close(self, ch, msg): + ch.close(msg) + + def connection_close(self, ch, msg): + self.client.peer.close(msg) + + def close(self, reason): + self.client.closed = True + self.client.started.set() diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py new file mode 100644 index 0000000000..c4bbe91f32 --- /dev/null +++ b/qpid/python/qpid/codec.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +Utility code to translate between python objects and AMQP encoded data +fields. +""" + +from cStringIO import StringIO +from struct import * + +class EOF(Exception): + pass + +class Codec: + + def __init__(self, stream): + self.stream = stream + self.nwrote = 0 + self.nread = 0 + self.incoming_bits = [] + self.outgoing_bits = [] + + def read(self, n): + data = self.stream.read(n) + if n > 0 and len(data) == 0: + raise EOF() + self.nread += len(data) + return data + + def write(self, s): + self.flushbits() + self.stream.write(s) + self.nwrote += len(s) + + def flush(self): + self.flushbits() + self.stream.flush() + + def flushbits(self): + if len(self.outgoing_bits) > 0: + bytes = [] + index = 0 + for b in self.outgoing_bits: + if index == 0: bytes.append(0) + if b: bytes[-1] |= 1 << index + index = (index + 1) % 8 + del self.outgoing_bits[:] + for byte in bytes: + self.encode_octet(byte) + + def pack(self, fmt, *args): + self.write(pack(fmt, *args)) + + def unpack(self, fmt): + size = calcsize(fmt) + data = self.read(size) + values = unpack(fmt, data) + if len(values) == 1: + return values[0] + else: + return values + + def encode(self, type, value): + getattr(self, "encode_" + type)(value) + + def decode(self, type): + return getattr(self, "decode_" + type)() + + # bit + def encode_bit(self, o): + if o: + self.outgoing_bits.append(True) + else: + self.outgoing_bits.append(False) + + def decode_bit(self): + if len(self.incoming_bits) == 0: + bits = self.decode_octet() + for i in range(8): + self.incoming_bits.append(bits >> i & 1 != 0) + return self.incoming_bits.pop(0) + + # octet + def encode_octet(self, o): + self.pack("!B", o) + + def decode_octet(self): + return self.unpack("!B") + + # short + def encode_short(self, o): + self.pack("!H", o) + + def decode_short(self): + return self.unpack("!H") + + # long + def encode_long(self, o): + self.pack("!L", o) + + def decode_long(self): + return self.unpack("!L") + + # longlong + def encode_longlong(self, o): + self.pack("!Q", o) + + def decode_longlong(self): + return self.unpack("!Q") + + def enc_str(self, fmt, s): + size = len(s) + self.pack(fmt, size) + self.write(s) + + def dec_str(self, fmt): + size = self.unpack(fmt) + return self.read(size) + + # shortstr + def encode_shortstr(self, s): + self.enc_str("!B", s) + + def decode_shortstr(self): + return self.dec_str("!B") + + # longstr + def encode_longstr(self, s): + if isinstance(s, dict): + self.encode_table(s) + else: + self.enc_str("!L", s) + + def decode_longstr(self): + return self.dec_str("!L") + + # table + def encode_table(self, tbl): + enc = StringIO() + codec = Codec(enc) + for key, value in tbl.items(): + codec.encode_shortstr(key) + if isinstance(value, basestring): + codec.write("S") + codec.encode_longstr(value) + else: + codec.write("I") + codec.encode_long(value) + s = enc.getvalue() + self.encode_long(len(s)) + self.write(s) + + def decode_table(self): + size = self.decode_long() + start = self.nread + result = {} + while self.nread - start < size: + key = self.decode_shortstr() + type = self.read(1) + if type == "S": + value = self.decode_longstr() + elif type == "I": + value = self.decode_long() + else: + raise ValueError(repr(type)) + result[key] = value + return result + +def test(type, value): + if isinstance(value, (list, tuple)): + values = value + else: + values = [value] + stream = StringIO() + codec = Codec(stream) + for v in values: + codec.encode(type, v) + codec.flush() + enc = stream.getvalue() + stream.reset() + dup = [] + for i in xrange(len(values)): + dup.append(codec.decode(type)) + if values != dup: + raise AssertionError("%r --> %r --> %r" % (values, enc, dup)) + +if __name__ == "__main__": + def dotest(type, value): + args = (type, value) + test(*args) + + for value in ("1", "0", "110", "011", "11001", "10101", "10011"): + for i in range(10): + dotest("bit", map(lambda x: x == "1", value*i)) + + for value in ({}, {"asdf": "fdsa", "fdsa": 1, "three": 3}, {"one": 1}): + dotest("table", value) + + for type in ("octet", "short", "long", "longlong"): + for value in range(0, 256): + dotest(type, value) + + for type in ("shortstr", "longstr"): + for value in ("", "a", "asdf"): + dotest(type, value) diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py new file mode 100644 index 0000000000..f4d0817e60 --- /dev/null +++ b/qpid/python/qpid/connection.py @@ -0,0 +1,265 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +A Connection class containing socket code that uses the spec metadata +to read and write Frame objects. This could be used by a client, +server, or even a proxy implementation. +""" + +import socket, codec +from cStringIO import StringIO +from spec import load, pythonize +from codec import EOF + +class SockIO: + + def __init__(self, sock): + self.sock = sock + + def write(self, buf): +# print "OUT: %r" % buf + self.sock.sendall(buf) + + def read(self, n): + data = "" + while len(data) < n: + try: + s = self.sock.recv(n - len(data)) + except socket.error: + break + if len(s) == 0: + break +# print "IN: %r" % s + data += s + return data + + def flush(self): + pass + +class Connection: + + def __init__(self, host, port, spec): + self.host = host + self.port = port + self.spec = spec + self.FRAME_END = self.spec.constants.byname["frame end"].id + + def connect(self): + sock = socket.socket() + sock.connect((self.host, self.port)) + sock.setblocking(1) + self.codec = codec.Codec(SockIO(sock)) + + def flush(self): + self.codec.flush() + + INIT="!4s4B" + + def init(self): + self.codec.pack(Connection.INIT, "AMQP", 1, 1, self.spec.major, + self.spec.minor) + + def write(self, frame): + c = self.codec + c.encode_octet(self.spec.constants.byname[frame.payload.type].id) + c.encode_short(frame.channel) + frame.payload.encode(c) + c.encode_octet(self.FRAME_END) + + def read(self): + c = self.codec + type = self.spec.constants.byid[c.decode_octet()].name + channel = c.decode_short() + payload = Frame.DECODERS[type].decode(self.spec, c) + end = c.decode_octet() + if end != self.FRAME_END: + raise "frame error: expected %r, got %r" % (self.FRAME_END, end) + frame = Frame(channel, payload) + return frame + +class Frame: + + METHOD = "frame method" + HEADER = "frame header" + BODY = "frame body" + OOB_METHOD = "frame oob method" + OOB_HEADER = "frame oob header" + OOB_BODY = "frame oob body" + TRACE = "frame trace" + HEARTBEAT = "frame heartbeat" + + DECODERS = {} + + def __init__(self, channel, payload): + self.channel = channel + self.payload = payload + + def __str__(self): + return "[%d] %s" % (self.channel, self.payload) + +class Payload: + + class __metaclass__(type): + + def __new__(cls, name, bases, dict): + for req in ("encode", "decode", "type"): + if not dict.has_key(req): + raise TypeError("%s must define %s" % (name, req)) + dict["decode"] = staticmethod(dict["decode"]) + t = type.__new__(cls, name, bases, dict) + if t.type != None: + Frame.DECODERS[t.type] = t + return t + + type = None + + def encode(self, enc): abstract + + def decode(spec, dec): abstract + +class Method(Payload): + + type = Frame.METHOD + + def __init__(self, method, *args): + if len(args) != len(method.fields): + argspec = ["%s: %s" % (pythonize(f.name), f.type) + for f in method.fields] + raise TypeError("%s.%s expecting (%s), got %s" % + (pythonize(method.klass.name), + pythonize(method.name), ", ".join(argspec), args)) + self.method = method + self.args = args + + def encode(self, enc): + buf = StringIO() + c = codec.Codec(buf) + c.encode_short(self.method.klass.id) + c.encode_short(self.method.id) + for field, arg in zip(self.method.fields, self.args): + c.encode(field.type, arg) + c.flush() + enc.encode_longstr(buf.getvalue()) + + def decode(spec, dec): + enc = dec.decode_longstr() + c = codec.Codec(StringIO(enc)) + klass = spec.classes.byid[c.decode_short()] + meth = klass.methods.byid[c.decode_short()] + args = tuple([c.decode(f.type) for f in meth.fields]) + return Method(meth, *args) + + def __str__(self): + return "%s %s" % (self.method, ", ".join([str(a) for a in self.args])) + +class Header(Payload): + + type = Frame.HEADER + + def __init__(self, klass, weight, size, **properties): + self.klass = klass + self.weight = weight + self.size = size + self.properties = properties + + def __getitem__(self, name): + return self.properties[name] + + def __setitem__(self, name, value): + self.properties[name] = value + + def __delitem__(self, name): + del self.properties[name] + + def encode(self, enc): + buf = StringIO() + c = codec.Codec(buf) + c.encode_short(self.klass.id) + c.encode_short(self.weight) + c.encode_longlong(self.size) + + # property flags + nprops = len(self.klass.fields) + flags = 0 + for i in range(nprops): + f = self.klass.fields.items[i] + flags <<= 1 + if self.properties.get(f.name) != None: + flags |= 1 + # the last bit indicates more flags + if i > 0 and (i % 15) == 0: + flags <<= 1 + if nprops > (i + 1): + flags |= 1 + c.encode_short(flags) + flags = 0 + flags <<= ((16 - (nprops % 15)) % 16) + c.encode_short(flags) + + # properties + for f in self.klass.fields: + v = self.properties.get(f.name) + if v != None: + c.encode(f.type, v) + c.flush() + enc.encode_longstr(buf.getvalue()) + + def decode(spec, dec): + c = codec.Codec(StringIO(dec.decode_longstr())) + klass = spec.classes.byid[c.decode_short()] + weight = c.decode_short() + size = c.decode_longlong() + + # property flags + bits = [] + while True: + flags = c.decode_short() + for i in range(15, 0, -1): + if flags >> i & 0x1 != 0: + bits.append(True) + else: + bits.append(False) + if flags & 0x1 == 0: + break + + # properties + properties = {} + for b, f in zip(bits, klass.fields): + if b: + properties[f.name] = c.decode(f.type) + + return Header(klass, weight, size, **properties) + + def __str__(self): + return "%s %s %s %s" % (self.klass, self.weight, self.size, + self.properties) + +class Body(Payload): + + type = Frame.BODY + + def __init__(self, content): + self.content = content + + def encode(self, enc): + enc.encode_longstr(self.content) + + def decode(spec, dec): + return Body(dec.decode_longstr()) + + def __str__(self): + return "Body(%r)" % self.content diff --git a/qpid/python/qpid/content.py b/qpid/python/qpid/content.py new file mode 100644 index 0000000000..33c9ec35f4 --- /dev/null +++ b/qpid/python/qpid/content.py @@ -0,0 +1,47 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +A simple python representation for AMQP content. +""" + +def default(val, defval): + if val == None: + return defval + else: + return val + +class Content: + + def __init__(self, body = "", children = None, properties = None): + self.body = body + self.children = default(children, []) + self.properties = default(properties, {}) + + def size(self): + return len(self.body) + + def weight(self): + return len(self.children) + + def __getitem__(self, name): + return self.properties[name] + + def __setitem__(self, name, value): + self.properties[name] = value + + def __delitem__(self, name): + del self.properties[name] diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py new file mode 100644 index 0000000000..0467162498 --- /dev/null +++ b/qpid/python/qpid/delegate.py @@ -0,0 +1,52 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +Delegate implementation intended for use with the peer module. +""" + +import threading, inspect +from spec import pythonize + +class Delegate: + + def __init__(self): + self.handlers = {} + self.invokers = {} + # initialize all the mixins + self.invoke_all("init") + + def invoke_all(self, meth, *args, **kwargs): + for cls in inspect.getmro(self.__class__): + if hasattr(cls, meth): + getattr(cls, meth)(self, *args, **kwargs) + + def dispatch(self, channel, message): + method = message.method + spec = method.klass.spec + + try: + handler = self.handlers[method] + except KeyError: + name = "%s_%s" % (pythonize(method.klass.name), + pythonize(method.name)) + handler = getattr(self, name) + self.handlers[method] = handler + + return handler(channel, message) + + def close(self, reason): + self.invoke_all("close", reason) diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py new file mode 100644 index 0000000000..08b3e70c0b --- /dev/null +++ b/qpid/python/qpid/message.py @@ -0,0 +1,81 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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 sets import Set + +class Message: + + COMMON_FIELDS = Set(("content", "method", "fields")) + + def __init__(self, method, fields, content = None): + self.method = method + self.fields = fields + self.content = content + + def __len__(self): + l = len(self.fields) + if method.content: + l += 1 + return len(self.fields) + + def _idx(self, idx): + if idx < 0: idx += len(self) + if idx < 0 or idx > len(self): + raise IndexError(idx) + return idx + + def __getitem__(self, idx): + idx = self._idx(idx) + if idx == len(self.fields): + return self.content + else: + return self.fields[idx] + + def __setitem__(self, idx, value): + idx = self._idx(idx) + if idx == len(self.fields): + self.content = value + else: + self.fields[idx] = value + + def _slot(self, attr): + if attr in Message.COMMON_FIELDS: + env = self.__dict__ + key = attr + else: + env = self.fields + try: + field = self.method.fields.bypyname[attr] + key = self.method.fields.index(field) + except KeyError: + raise AttributeError(attr) + return env, key + + def __getattr__(self, attr): + env, key = self._slot(attr) + return env[key] + + def __setattr__(self, attr, value): + env, key = self._slot(attr) + env[attr] = value + + STR = "%s %s content = %s" + REPR = STR.replace("%s", "%r") + + def __str__(self): + return Message.STR % (self.method, self.fields, self.content) + + def __repr__(self): + return Message.REPR % (self.method, self.fields, self.content) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py new file mode 100644 index 0000000000..4179a05568 --- /dev/null +++ b/qpid/python/qpid/peer.py @@ -0,0 +1,209 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +This module contains a skeletal peer implementation useful for +implementing an AMQP server, client, or proxy. The peer implementation +sorts incoming frames to their intended channels, and dispatches +incoming method frames to a delegate. +""" + +import thread, traceback, socket, sys, logging +from connection import Frame, EOF, Method, Header, Body +from message import Message +from queue import Queue, Closed as QueueClosed +from content import Content +from cStringIO import StringIO + +class Peer: + + def __init__(self, conn, delegate): + self.conn = conn + self.delegate = delegate + self.outgoing = Queue(0) + self.work = Queue(0) + self.channels = {} + self.Channel = type("Channel%s" % conn.spec.klass.__name__, + (Channel, conn.spec.klass), {}) + self.lock = thread.allocate_lock() + + def channel(self, id): + self.lock.acquire() + try: + try: + ch = self.channels[id] + except KeyError: + ch = self.Channel(id, self.outgoing) + self.channels[id] = ch + finally: + self.lock.release() + return ch + + def start(self): + thread.start_new_thread(self.writer, ()) + thread.start_new_thread(self.reader, ()) + thread.start_new_thread(self.worker, ()) + + def fatal(message=None): + """Call when an unexpected exception occurs that will kill a thread. + + In this case it's better to crash the process than to continue in + an invalid state with a missing thread.""" + if message: print >> sys.stderr, message + traceback.print_exc() + + def reader(self): + try: + while True: + try: + frame = self.conn.read() + except EOF, e: + self.close(e) + break + ch = self.channel(frame.channel) + ch.dispatch(frame, self.work) + except: + self.fatal() + + def close(self, reason): + for ch in self.channels.values(): + ch.close(reason) + self.delegate.close(reason) + + def writer(self): + try: + while True: + try: + message = self.outgoing.get() + self.conn.write(message) + except socket.error, e: + self.close(e) + break + self.conn.flush() + except: + self.fatal() + + def worker(self): + try: + while True: + self.dispatch(self.work.get()) + except: + self.fatal() + + def dispatch(self, queue): + frame = queue.get() + channel = self.channel(frame.channel) + payload = frame.payload + if payload.method.content: + content = read_content(queue) + else: + content = None + # Let the caller deal with exceptions thrown here. + message = Message(payload.method, payload.args, content) + self.delegate.dispatch(channel, message) + +class Closed(Exception): pass + +class Channel: + + def __init__(self, id, outgoing): + self.id = id + self.outgoing = outgoing + self.incoming = Queue(0) + self.responses = Queue(0) + self.queue = None + self.closed = False + self.reason = None + + def close(self, reason): + if self.closed: + return + self.closed = True + self.reason = reason + self.incoming.close() + self.responses.close() + + def dispatch(self, frame, work): + payload = frame.payload + if isinstance(payload, Method): + if payload.method.response: + self.queue = self.responses + else: + self.queue = self.incoming + work.put(self.incoming) + self.queue.put(frame) + + def invoke(self, method, args, content = None): + if self.closed: + raise Closed(self.reason) + + frame = Frame(self.id, Method(method, *args)) + self.outgoing.put(frame) + + if method.content: + if content == None: + content = Content() + self.write_content(method.klass, content, self.outgoing) + + try: + # here we depend on all nowait fields being named nowait + f = method.fields.byname["nowait"] + nowait = args[method.fields.index(f)] + except KeyError: + nowait = False + + try: + if not nowait and method.responses: + resp = self.responses.get().payload + if resp.method.content: + content = read_content(self.responses) + else: + content = None + if resp.method in method.responses: + return Message(resp.method, resp.args, content) + else: + raise ValueError(resp) + except QueueClosed, e: + if self.closed: + raise Closed(self.reason) + else: + raise e + + def write_content(self, klass, content, queue): + size = content.size() + header = Frame(self.id, Header(klass, content.weight(), size)) + queue.put(header) + for child in content.children: + self.write_content(klass, child, queue) + # should split up if content.body exceeds max frame size + if size > 0: + queue.put(Frame(self.id, Body(content.body))) + +def read_content(queue): + frame = queue.get() + header = frame.payload + children = [] + for i in range(header.weight): + children.append(read_content(queue)) + size = header.size + read = 0 + buf = StringIO() + while read < size: + body = queue.get() + content = body.payload.content + buf.write(content) + read += len(content) + return Content(buf.getvalue(), children, header.properties.copy()) diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py new file mode 100644 index 0000000000..491cc3947d --- /dev/null +++ b/qpid/python/qpid/queue.py @@ -0,0 +1,42 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +This module augments the standard python multithreaded Queue +implementation to add a close() method so that threads blocking on the +content of a queue can be notified if the queue is no longer in use. +""" + +from Queue import Queue as BaseQueue, Empty, Full + +class Closed(Exception): pass + +class Queue(BaseQueue): + + END = object() + + def close(self): + self.put(Queue.END) + + def get(self, block = True, timeout = None): + result = BaseQueue.get(self, block, timeout) + if result == Queue.END: + # this guarantees that any other waiting threads or any future + # calls to get will also result in a Closed exception + self.put(Queue.END) + raise Closed() + else: + return result diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py new file mode 100644 index 0000000000..70e09aa1e9 --- /dev/null +++ b/qpid/python/qpid/spec.py @@ -0,0 +1,349 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +This module loads protocol metadata into python objects. It provides +access to spec metadata via a python object model, and can also +dynamically creating python methods, classes, and modules based on the +spec metadata. All the generated methods have proper signatures and +doc strings based on the spec metadata so the python help system can +be used to browse the spec documentation. The generated methods all +dispatch to the self.invoke(meth, args) callback of the containing +class so that the generated code can be reused in a variety of +situations. +""" + +import re, textwrap, new, xmlutil + +class SpecContainer: + + def __init__(self): + self.items = [] + self.byname = {} + self.byid = {} + self.indexes = {} + self.bypyname = {} + + def add(self, item): + if self.byname.has_key(item.name): + raise ValueError("duplicate name: %s" % item) + if self.byid.has_key(item.id): + raise ValueError("duplicate id: %s" % item) + pyname = pythonize(item.name) + if self.bypyname.has_key(pyname): + raise ValueError("duplicate pyname: %s" % item) + self.indexes[item] = len(self.items) + self.items.append(item) + self.byname[item.name] = item + self.byid[item.id] = item + self.bypyname[pyname] = item + + def index(self, item): + try: + return self.indexes[item] + except KeyError: + raise ValueError(item) + + def __iter__(self): + return iter(self.items) + + def __len__(self): + return len(self.items) + +class Metadata: + + PRINT = [] + + def __init__(self): + pass + + def __str__(self): + args = map(lambda f: "%s=%s" % (f, getattr(self, f)), self.PRINT) + return "%s(%s)" % (self.__class__.__name__, ", ".join(args)) + + def __repr__(self): + return str(self) + +class Spec(Metadata): + + PRINT=["major", "minor", "file"] + + def __init__(self, major, minor, file): + Metadata.__init__(self) + self.major = major + self.minor = minor + self.file = file + self.constants = SpecContainer() + self.classes = SpecContainer() + + def post_load(self): + self.module = self.define_module("amqp%s%s" % (self.major, self.minor)) + self.klass = self.define_class("Amqp%s%s" % (self.major, self.minor)) + + def parse_method(self, name): + parts = re.split(r"\s*\.\s*", name) + if len(parts) != 2: + raise ValueError(name) + klass, meth = parts + return self.classes.byname[klass].methods.byname[meth] + + def define_module(self, name, doc = None): + module = new.module(name, doc) + module.__file__ = self.file + for c in self.classes: + classname = pythonize(c.name) + cls = c.define_class(classname) + cls.__module__ = module.__name__ + setattr(module, classname, cls) + return module + + def define_class(self, name): + methods = {} + for c in self.classes: + for m in c.methods: + meth = pythonize(m.klass.name + "_" + m.name) + methods[meth] = m.define_method(meth) + return type(name, (), methods) + +class Constant(Metadata): + + PRINT=["name", "id"] + + def __init__(self, spec, name, id, klass, docs): + Metadata.__init__(self) + self.spec = spec + self.name = name + self.id = id + self.klass = klass + self.docs = docs + +class Class(Metadata): + + PRINT=["name", "id"] + + def __init__(self, spec, name, id, handler, docs): + Metadata.__init__(self) + self.spec = spec + self.name = name + self.id = id + self.handler = handler + self.fields = SpecContainer() + self.methods = SpecContainer() + self.docs = docs + + def define_class(self, name): + methods = {} + for m in self.methods: + meth = pythonize(m.name) + methods[meth] = m.define_method(meth) + return type(name, (), methods) + +class Method(Metadata): + + PRINT=["name", "id"] + + def __init__(self, klass, name, id, content, responses, synchronous, + description, docs): + Metadata.__init__(self) + self.klass = klass + self.name = name + self.id = id + self.content = content + self.responses = responses + self.synchronous = synchronous + self.fields = SpecContainer() + self.description = description + self.docs = docs + self.response = False + + def docstring(self): + s = "\n\n".join([fill(d, 2) for d in [self.description] + self.docs]) + for f in self.fields: + if f.docs: + s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, pythonize(f.name))] + + [fill(d, 4) for d in f.docs[1:]]) + return s + + METHOD = "__method__" + DEFAULTS = {"bit": False, + "shortstr": "", + "longstr": "", + "table": {}, + "octet": 0, + "short": 0, + "long": 0, + "longlong": 0} + + def define_method(self, name): + g = {Method.METHOD: self} + l = {} + args = [(pythonize(f.name), Method.DEFAULTS[f.type]) for f in self.fields] + if self.content: + args += [("content", None)] + code = "def %s(self, %s):\n" % \ + (name, ", ".join(["%s = %r" % a for a in args])) + code += " %r\n" % self.docstring() + if self.content: + methargs = args[:-1] + else: + methargs = args + argnames = ", ".join([a[0] for a in methargs]) + code += " return self.invoke(%s" % Method.METHOD + if argnames: + code += ", (%s,)" % argnames + if self.content: + code += ", content" + code += ")" + exec code in g, l + return l[name] + +class Field(Metadata): + + PRINT=["name", "id", "type"] + + def __init__(self, name, id, type, docs): + Metadata.__init__(self) + self.name = name + self.id = id + self.type = type + self.docs = docs + +def get_docs(nd): + return [n.text for n in nd["doc"]] + +def load_fields(nd, l, domains): + for f_nd in nd["field"]: + try: + type = f_nd["@type"] + except KeyError: + type = domains[f_nd["@domain"]] + l.add(Field(f_nd["@name"], f_nd.index(), type, get_docs(f_nd))) + +def load(specfile): + doc = xmlutil.parse(specfile) + root = doc["amqp"][0] + spec = Spec(int(root["@major"]), int(root["@minor"]), specfile) + + # constants + for nd in root["constant"]: + const = Constant(spec, nd["@name"], int(nd["@value"]), nd.get("@class"), + get_docs(nd)) + spec.constants.add(const) + + # domains are typedefs + domains = {} + for nd in root["domain"]: + domains[nd["@name"]] = nd["@type"] + + # classes + for c_nd in root["class"]: + klass = Class(spec, c_nd["@name"], int(c_nd["@index"]), c_nd["@handler"], + get_docs(c_nd)) + load_fields(c_nd, klass.fields, domains) + for m_nd in c_nd["method"]: + meth = Method(klass, m_nd["@name"], + int(m_nd["@index"]), + m_nd.get_bool("@content", False), + [nd["@name"] for nd in m_nd["response"]], + m_nd.get_bool("@synchronous", False), + m_nd.text, + get_docs(m_nd)) + load_fields(m_nd, meth.fields, domains) + klass.methods.add(meth) + # resolve the responses + for m in klass.methods: + m.responses = [klass.methods.byname[r] for r in m.responses] + for resp in m.responses: + resp.response = True + spec.classes.add(klass) + spec.post_load() + return spec + +REPLACE = {" ": "_", "-": "_"} +KEYWORDS = {"global": "global_", + "return": "return_"} + +def pythonize(name): + name = str(name) + for key, val in REPLACE.items(): + name = name.replace(key, val) + try: + name = KEYWORDS[name] + except KeyError: + pass + return name + +def fill(text, indent, heading = None): + sub = indent * " " + if heading: + init = (indent - 2) * " " + heading + " -- " + else: + init = sub + w = textwrap.TextWrapper(initial_indent = init, subsequent_indent = sub) + return w.fill(" ".join(text.split())) + +class Rule(Metadata): + + PRINT = ["text", "implement", "tests"] + + def __init__(self, text, implement, tests, path): + self.text = text + self.implement = implement + self.tests = tests + self.path = path + +def find_rules(node, rules): + if node.name == "rule": + rules.append(Rule(node.text, node.get("@implement"), + [ch.text for ch in node if ch.name == "test"], + node.path())) + if node.name == "doc" and node.get("@name") == "rule": + tests = [] + if node.has("@test"): + tests.append(node["@test"]) + rules.append(Rule(node.text, None, tests, node.path())) + for child in node: + find_rules(child, rules) + +def load_rules(specfile): + rules = [] + find_rules(xmlutil.parse(specfile), rules) + return rules + +def test_summary(): + template = """ + AMQP Tests + + + %s +
+ + + """ + rows = [] + for rule in load_rules("amqp.org/specs/amqp7.xml"): + if rule.tests: + tests = ", ".join(rule.tests) + else: + tests = " " + rows.append('Path: %s' + 'Implement: %s' + 'Tests: %s' % + (rule.path[len("/root/amqp"):], rule.implement, tests)) + rows.append('%s' % rule.text) + rows.append(' ') + + print template % "\n".join(rows) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py new file mode 100644 index 0000000000..ff9ecbee8a --- /dev/null +++ b/qpid/python/qpid/testlib.py @@ -0,0 +1,221 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +# Support library for qpid python tests. +# + +import sys, re, unittest, os, random, logging +import qpid.client, qpid.spec +from getopt import getopt, GetoptError + + +def findmodules(root): + """Find potential python modules under directory root""" + found = [] + for dirpath, subdirs, files in os.walk(root): + modpath = dirpath.replace(os.sep, '.') + if not re.match(r'\.svn$', dirpath): # Avoid SVN directories + for f in files: + match = re.match(r'(.+)\.py$', f) + if match and f != '__init__.py': + found.append('.'.join([modpath, match.group(1)])) + return found + +def default(value, default): + if (value == None): return default + else: return value + +class TestRunner: + """Runs unit tests. + + Parses command line arguments, provides utility functions for tests, + runs the selected test suite. + """ + + def _die(self, message = None): + if message: print message + print """ +run-tests [options] [test*] +The name of a test is package.module.ClassName.testMethod +Options: + -?/-h/--help : this message + -s/--spec : file containing amqp XML spec + -b/--broker [[/]@][:] : broker to connect to + -v/--verbose : verbose - lists tests as they are run. + -d/--debug : enable debug logging. + -i/--ignore : ignore the named test. + -I/--ignore-file : file containing patterns to ignore. + """ + sys.exit(1) + + def setBroker(self, broker): + rex = re.compile(r""" + # [ [ / ] @] [ : ] + ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) + match = rex.match(broker) + if not match: self._die("'%s' is not a valid broker" % (broker)) + self.user, self.password, self.host, self.port = match.groups() + self.port = int(default(self.port, 5672)) + self.user = default(self.user, "guest") + self.password = default(self.password, "guest") + + def __init__(self): + # Defaults + self.setBroker("localhost") + self.spec = "../specs/amqp-8.0.xml" + self.verbose = 1 + self.ignore = [] + + def ignoreFile(self, filename): + f = file(filename) + for line in f.readlines(): self.ignore.append(line.strip()) + f.close() + + def _parseargs(self, args): + try: + opts, self.tests = getopt(args, "s:b:h?dvi:I:", ["help", "spec", "server", "verbose", "ignore", "ignore-file"]) + except GetoptError, e: + self._die(str(e)) + for opt, value in opts: + if opt in ("-?", "-h", "--help"): self._die() + if opt in ("-s", "--spec"): self.spec = value + if opt in ("-b", "--broker"): self.setBroker(value) + if opt in ("-v", "--verbose"): self.verbose = 2 + if opt in ("-d", "--debug"): logging.basicConfig(level=logging.DEBUG) + if opt in ("-i", "--ignore"): self.ignore.append(value) + if opt in ("-I", "--ignore-file"): self.ignoreFile(value) + + if len(self.tests) == 0: self.tests=findmodules("tests") + + def testSuite(self): + class IgnoringTestSuite(unittest.TestSuite): + def addTest(self, test): + if isinstance(test, unittest.TestCase) and test.id() in testrunner.ignore: + return + unittest.TestSuite.addTest(self, test) + + # Use our IgnoringTestSuite in the test loader. + unittest.TestLoader.suiteClass = IgnoringTestSuite + return unittest.defaultTestLoader.loadTestsFromNames(self.tests) + + def run(self, args=sys.argv[1:]): + self._parseargs(args) + runner = unittest.TextTestRunner(descriptions=False, + verbosity=self.verbose) + result = runner.run(self.testSuite()) + if (self.ignore): + print "=======================================" + print "NOTE: the following tests were ignored:" + for t in self.ignore: print t + print "=======================================" + return result.wasSuccessful() + + def connect(self, host=None, port=None, spec=None, user=None, password=None): + """Connect to the broker, returns a qpid.client.Client""" + host = host or self.host + port = port or self.port + spec = spec or self.spec + user = user or self.user + password = password or self.password + client = qpid.client.Client(host, port, qpid.spec.load(spec)) + client.start({"LOGIN": user, "PASSWORD": password}) + return client + + +# Global instance for tests to call connect. +testrunner = TestRunner() + + +class TestBase(unittest.TestCase): + """Base class for Qpid test cases. + + self.client is automatically connected with channel 1 open before + the test methods are run. + + Deletes queues and exchanges after. Tests call + self.queue_declare(channel, ...) and self.exchange_declare(chanel, + ...) which are wrappers for the Channel functions that note + resources to clean up later. + """ + + def setUp(self): + self.queues = [] + self.exchanges = [] + self.client = self.connect() + self.channel = self.client.channel(1) + self.channel.channel_open() + + def tearDown(self): + # TODO aconway 2006-09-05: Wrong behaviour here, we should + # close all open channels (checking for exceptions on the + # channesl) then open a channel to clean up qs and exs, + # finally close that channel. + for ch, q in self.queues: + ch.queue_delete(queue=q) + for ch, ex in self.exchanges: + ch.exchange_delete(exchange=ex) + + def connect(self, *args, **keys): + """Create a new connction, return the Client object""" + return testrunner.connect(*args, **keys) + + def queue_declare(self, channel=None, *args, **keys): + channel = channel or self.channel + reply = channel.queue_declare(*args, **keys) + self.queues.append((channel, reply.queue)) + return reply + + def exchange_declare(self, channel=None, ticket=0, exchange='', + type='', passive=False, durable=False, + auto_delete=False, internal=False, nowait=False, + arguments={}): + channel = channel or self.channel + reply = channel.exchange_declare(ticket, exchange, type, passive, durable, auto_delete, internal, nowait, arguments) + # TODO aconway 2006-09-14: Don't add exchange on failure. + self.exchanges.append((channel,exchange)) + return reply + + def assertPublishConsume(self, queue="", exchange="", routing_key=""): + """ + Publish a message and consume it, assert it comes back intact. + + queue can be a single queue name or a list of queue names. + For a list assert the message appears on all queues. + Crude attempt to make unique messages so we can't consume + a message not really meant for us. + """ + body = "TestMessage("+str(random.randint(999999, 1000000))+")" + self.channel.basic_publish(exchange=exchange, + content=qpid.content.Content(body), + routing_key=routing_key) + if not isinstance(queue, list): queue = [queue] + for q in queue: + reply = self.channel.basic_consume(queue=q, no_ack=True) + msg = self.client.queue(reply.consumer_tag).get(timeout=2) + self.assertEqual(body, msg.content.body) + + + def assertChannelException(self, expectedCode, message): + self.assertEqual(message.method.klass.name, "channel") + self.assertEqual(message.method.name, "close") + self.assertEqual(message.reply_code, expectedCode) + + + def assertConnectionException(self, expectedCode, message): + self.assertEqual(message.method.klass.name, "connection") + self.assertEqual(message.method.name, "close") + self.assertEqual(message.reply_code, expectedCode) + diff --git a/qpid/python/qpid/xmlutil.py b/qpid/python/qpid/xmlutil.py new file mode 100644 index 0000000000..8f8a7116f5 --- /dev/null +++ b/qpid/python/qpid/xmlutil.py @@ -0,0 +1,116 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +XML utilities used by spec.py +""" + +import xml.sax +from xml.sax.handler import ContentHandler + +def parse(file): + doc = Node("root") + xml.sax.parse(file, Builder(doc)) + return doc + +class Node: + + def __init__(self, name, attrs = None, text = None, parent = None): + self.name = name + self.attrs = attrs + self.text = text + self.parent = parent + self.children = [] + if parent != None: + parent.children.append(self) + + def get_bool(self, key, default = False): + v = self.get(key) + if v == None: + return default + else: + return bool(int(v)) + + def index(self): + if self.parent: + return self.parent.children.index(self) + else: + return 0 + + def has(self, key): + try: + result = self[key] + return True + except KeyError: + return False + except IndexError: + return False + + def get(self, key, default = None): + if self.has(key): + return self[key] + else: + return default + + def __getitem__(self, key): + if callable(key): + return filter(key, self.children) + else: + t = key.__class__ + meth = "__get%s__" % t.__name__ + if hasattr(self, meth): + return getattr(self, meth)(key) + else: + raise KeyError(key) + + def __getstr__(self, name): + if name[:1] == "@": + return self.attrs[name[1:]] + else: + return self[lambda nd: nd.name == name] + + def __getint__(self, index): + return self.children[index] + + def __iter__(self): + return iter(self.children) + + def path(self): + if self.parent == None: + return "/%s" % self.name + else: + return "%s/%s" % (self.parent.path(), self.name) + +class Builder(ContentHandler): + + def __init__(self, start = None): + self.node = start + + def __setitem__(self, element, type): + self.types[element] = type + + def startElement(self, name, attrs): + self.node = Node(name, attrs, None, self.node) + + def endElement(self, name): + self.node = self.node.parent + + def characters(self, content): + if self.node.text == None: + self.node.text = content + else: + self.node.text += content + diff --git a/qpid/python/rule2test b/qpid/python/rule2test new file mode 100755 index 0000000000..b57ea9e24e --- /dev/null +++ b/qpid/python/rule2test @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +# +# Convert rules to tests +# +import sys, re, os.path +from getopt import getopt, GetoptError +from string import capitalize +from xml import dom +from xml.dom.minidom import parse + +def camelcase(s): + """Convert 'string like this' to 'StringLikeThis'""" + return "".join([capitalize(w) for w in re.split(re.compile("\W*"), s)]) + +def uncapitalize(s): return s[0].lower()+s[1:] + +def ancestors(node): + "Return iterator of ancestors from top-level element to node" + def generator(node): + while node and node.parentNode: + yield node + node = node.parentNode + return reversed(list(generator(node))) + +def tagAndName(element): + nameAttr = element.getAttribute("name"); + if (nameAttr) : return camelcase(nameAttr) + camelcase(element.tagName) + else: return camelcase(element.tagName) + +def nodeText(n): + """Recursively collect text from all text nodes under n""" + if n.nodeType == dom.Node.TEXT_NODE: + return n.data + if n.childNodes: + return reduce(lambda t, c: t + nodeText(c), n.childNodes, "") + return "" + +def cleanup(docString, level=8): + unindent = re.sub("\n[ \t]*", "\n", docString.strip()) + emptyLines = re.sub("\n\n\n", "\n\n", unindent) + indented = re.sub("\n", "\n"+level*" ", emptyLines) + return level*" " + indented + +def printTest(test, docstring): + print "class %s(TestBase):" % test + print ' """' + print docstring + print ' """' + print + print + +def printTests(doc, module): + """Returns dictionary { classname : [ (methodname, docstring)* ] * }""" + tests = {} + rules = doc.getElementsByTagName("rule") + for r in rules: + path = list(ancestors(r)) + if module == path[1].getAttribute("name").lower(): + test = "".join(map(tagAndName, path[2:])) + "Tests" + docstring = cleanup(nodeText(r), 4) + printTest(test, docstring) + +def usage(message=None): + if message: print >>sys.stderr, message + print >>sys.stderr, """ +rule2test [options] + +Print test classes for each rule for the amqpclass in amqp.xml. + +Options: + -?/-h/--help : this message + -s/--spec : file containing amqp XML spec +""" + return 1 + +def main(argv): + try: opts, args = getopt(argv[1:], "h?s:", ["help", "spec="]) + except GetoptError, e: return usage(e) + spec = "../specs/amqp.xml" # Default + for opt, val in opts: + if (opt in ("-h", "-?", "--help")): return usage() + if (opt in ("-s", "--spec")): spec = val + doc = parse(spec) + if len(args) == 0: return usage() + printTests(doc, args[0]) + return 0 + +if (__name__ == "__main__"): sys.exit(main(sys.argv)) diff --git a/qpid/python/run-tests b/qpid/python/run-tests new file mode 100755 index 0000000000..c49bd32a96 --- /dev/null +++ b/qpid/python/run-tests @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +import sys +from qpid.testlib import testrunner + +if not testrunner.run(): sys.exit(1) + + + diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py new file mode 100644 index 0000000000..d55ff3fd85 --- /dev/null +++ b/qpid/python/tests/__init__.py @@ -0,0 +1 @@ +# Do not delete - marks this directory as a python package. diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py new file mode 100644 index 0000000000..b912fc40be --- /dev/null +++ b/qpid/python/tests/basic.py @@ -0,0 +1,115 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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 qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BasicTests(TestBase): + """Tests for 'methods' on the amqp basic 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a") + channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.basic_publish(exchange="amq.direct", routing_key="test-queue-1a", content=Content("consume_no_local")) + channel.basic_publish(exchange="amq.direct", routing_key="test-queue-1b", content=Content("consume_no_local")) + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True) + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.basic_consume(consumer_tag="first", queue="test-queue-2") + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.basic_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.basic_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + try: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py new file mode 100644 index 0000000000..1345076604 --- /dev/null +++ b/qpid/python/tests/broker.py @@ -0,0 +1,84 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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 qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BrokerTests(TestBase): + """Tests for basic Broker functionality""" + + def test_amqp_basic_13(self): + """ + First, this test tries to receive a message with a no-ack + consumer. Second, this test tries to explicitely receive and + acknowledge a message with an acknowledging consumer. + """ + ch = self.channel + self.queue_declare(ch, queue = "myqueue") + + # No ack consumer + ctag = ch.basic_consume(queue = "myqueue", no_ack = True).consumer_tag + body = "test no-ack" + ch.basic_publish(routing_key = "myqueue", content = Content(body)) + msg = self.client.queue(ctag).get(timeout = 5) + self.assert_(msg.content.body == body) + + # Acknowleding consumer + self.queue_declare(ch, queue = "otherqueue") + ctag = ch.basic_consume(queue = "otherqueue", no_ack = False).consumer_tag + body = "test ack" + ch.basic_publish(routing_key = "otherqueue", content = Content(body)) + msg = self.client.queue(ctag).get(timeout = 5) + ch.basic_ack(delivery_tag = msg.delivery_tag) + self.assert_(msg.content.body == body) + + # TODO: Ensure we get a failure if an ack consumer doesn't ack. + + def test_basic_delivery_immediate(self): + """ + Test basic message delivery where consume is issued before publish + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + reply = channel.basic_consume(queue="test-queue", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + + body = "Immediate Delivery" + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content(body), immediate=True) + msg = queue.get(timeout=5) + self.assert_(msg.content.body == body) + + # TODO: Ensure we fail if immediate=True and there's no consumer. + + + def test_basic_delivery_queued(self): + """ + Test basic message delivery where publish is issued before consume + (i.e. requires queueing of the message) + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + body = "Queued Delivery" + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content(body)) + reply = channel.basic_consume(queue="test-queue", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + msg = queue.get(timeout=5) + self.assert_(msg.content.body == body) + diff --git a/qpid/python/tests/example.py b/qpid/python/tests/example.py new file mode 100644 index 0000000000..47d07fe9c5 --- /dev/null +++ b/qpid/python/tests/example.py @@ -0,0 +1,91 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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 qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExampleTest (TestBase): + """ + An example Qpid test, illustrating the unittest frameowkr and the + python Qpid client. The test class must inherit TestCase. The + test code uses the Qpid client to interact with a qpid broker and + verify it behaves as expected. + """ + + def test_example(self): + """ + An example test. Note that test functions must start with 'test_' + to be recognized by the test framework. + """ + + # By inheriting TestBase, self.client is automatically connected + # and self.channel is automatically opened as channel(1) + # Other channel methods mimic the protocol. + channel = self.channel + + # Now we can send regular commands. If you want to see what the method + # arguments mean or what other commands are available, you can use the + # python builtin help() method. For example: + #help(chan) + #help(chan.exchange_declare) + + # If you want browse the available protocol methods without being + # connected to a live server you can use the amqp-doc utility: + # + # Usage amqp-doc [] [ ... ] + # + # Options: + # -e, --regexp use regex instead of glob when matching + + # Now that we know what commands are available we can use them to + # interact with the server. + + # Here we use ordinal arguments. + self.exchange_declare(channel, 0, "test", "direct") + + # Here we use keyword arguments. + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") + + # Call Channel.basic_consume to register as a consumer. + # All the protocol methods return a message object. The message object + # has fields corresponding to the reply method fields, plus a content + # field that is filled if the reply includes content. In this case the + # interesting field is the consumer_tag. + reply = channel.basic_consume(queue="test-queue") + + # We can use the Client.queue(...) method to access the queue + # corresponding to our consumer_tag. + queue = self.client.queue(reply.consumer_tag) + + # Now lets publish a message and see if our consumer gets it. To do + # this we need to import the Content class. + body = "Hello World!" + channel.basic_publish(exchange="test", + routing_key="key", + content=Content(body)) + + # Now we'll wait for the message to arrive. We can use the timeout + # argument in case the server hangs. By default queue.get() will wait + # until a message arrives or the connection to the server dies. + msg = queue.get(timeout=10) + + # And check that we got the right response with assertEqual + self.assertEqual(body, msg.content.body) + + # Now acknowledge the message. + channel.basic_ack(msg.delivery_tag, True) + diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py new file mode 100644 index 0000000000..b9b16bad78 --- /dev/null +++ b/qpid/python/tests/exchange.py @@ -0,0 +1,234 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +""" +Tests for exchange behaviour. + +Test classes ending in 'RuleTests' are derived from rules in amqp.xml. +""" + +import logging, Queue +from qpid.testlib import TestBase +from qpid.content import Content + + +# TODO aconway 2006-09-01: Investigate and add tests as appropriate. +# Observered on C++: +# +# No exception raised for basic_consume on non-existent queue name. +# No exception for basic_publish with bad routing key. +# No exception for binding to non-existent exchange? +# queue_bind hangs with invalid exchange name +# +# Do server exceptions get propagated properly? +# Do Java exceptions propagate with any data (or just Closed()) + +class StandardExchangeVerifier: + """Verifies standard exchange behavior. + + Used as base class for classes that test standard exchanges.""" + + def verifyDirectExchange(self, ex): + """Verify that ex behaves like a direct exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") + self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") + try: + self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") + self.fail("Expected Empty exception") + except Queue.Empty: None # Expected + + def verifyFanOutExchange(self, ex): + """Verify that ex behaves like a fanout exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex) + self.queue_declare(queue="p") + self.channel.queue_bind(queue="p", exchange=ex) + self.assertPublishConsume(exchange=ex, queue=["q","p"]) + + +class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server SHOULD implement these standard exchange types: topic, headers. + + Client attempts to declare an exchange with each of these standard types. + """ + + def testDirect(self): + """Declare and test a direct exchange""" + self.exchange_declare(0, exchange="d", type="direct") + self.verifyDirectExchange("d") + + def testFanout(self): + """Declare and test a fanout exchange""" + self.exchange_declare(0, exchange="f", type="fanout") + self.verifyFanOutExchange("f") + + +class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST, in each virtual host, pre-declare an exchange instance + for each standard exchange type that it implements, where the name of the + exchange instance is amq. followed by the exchange type name. + + Client creates a temporary queue and attempts to bind to each required + exchange instance (amq.fanout, amq.direct, and amq.topic, amq.headers if + those types are defined). + """ + # TODO aconway 2006-09-01: Add tests for 3.1.3.1: + # - Test auto binding by q name + # - Test the nameless "default publish" exchange. + # - Auto created amq.fanout exchange + + def testAmqDirect(self): self.verifyDirectExchange("amq.direct") + + def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") + + def testAmqTopic(self): + self.exchange_declare(0, exchange="amq.topic", passive="true") + # TODO aconway 2006-09-14: verify topic behavior + + def testAmqHeaders(self): + self.exchange_declare(0, exchange="amq.headers", passive="true") + # TODO aconway 2006-09-14: verify headers behavior + +class DefaultExchangeRuleTests(TestBase): + """ + The server MUST predeclare a direct exchange to act as the default exchange + for content Publish methods and for default queue bindings. + + Client checks that the default exchange is active by specifying a queue + binding with no exchange name, and publishing a message with a suitable + routing key but without specifying the exchange name, then ensuring that + the message arrives in the queue correctly. + """ + + +class DefaultAccessRuleTests(TestBase): + """ + The server MUST NOT allow clients to access the default exchange except + by specifying an empty exchange name in the Queue.Bind and content Publish + methods. + """ + + +class ExtensionsRuleTests(TestBase): + """ + The server MAY implement other exchange types as wanted. + """ + + +class DeclareMethodMinimumRuleTests(TestBase): + """ + The server SHOULD support a minimum of 16 exchanges per virtual host and + ideally, impose no limit except as defined by available resources. + + The client creates as many exchanges as it can until the server reports + an error; the number of exchanges successfuly created must be at least + sixteen. + """ + + +class DeclareMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access to + the realm in which the exchange exists or will be created, or "passive" + access if the if-exists flag is set. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeclareMethodExchangeFieldReservedRuleTests(TestBase): + """ + Exchange names starting with "amq." are reserved for predeclared and + standardised exchanges. The client MUST NOT attempt to create an exchange + starting with "amq.". + + + """ + + +class DeclareMethodTypeFieldTypedRuleTests(TestBase): + """ + Exchanges cannot be redeclared with different types. The client MUST not + attempt to redeclare an existing exchange with a different type than used + in the original Exchange.Declare method. + + + """ + + +class DeclareMethodTypeFieldSupportRuleTests(TestBase): + """ + The client MUST NOT attempt to create an exchange with a type that the + server does not support. + + + """ + + +class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): + """ + If set, and the exchange does not already exist, the server MUST raise a + channel exception with reply code 404 (not found). + + + """ + + +class DeclareMethodDurableFieldSupportRuleTests(TestBase): + """ + The server MUST support both durable and transient exchanges. + + + """ + + +class DeclareMethodDurableFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the durable field if the exchange already exists. + + + """ + + +class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the auto-delete field if the exchange already + exists. + + + """ + + +class DeleteMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access + rights to the exchange's access realm. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeleteMethodExchangeFieldExistsRuleTests(TestBase): + """ + The client MUST NOT attempt to delete an exchange that does not exist. + """ + + diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py new file mode 100644 index 0000000000..92260a7d64 --- /dev/null +++ b/qpid/python/tests/queue.py @@ -0,0 +1,254 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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 qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueueTests(TestBase): + """Tests for 'methods' on the amqp queue 'class'""" + + def test_purge(self): + """ + Test that the purge method removes messages from the queue + """ + channel = self.channel + #setup, declare a queue and add some messages to it: + channel.exchange_declare(exchange="test-exchange", type="direct") + channel.queue_declare(queue="test-queue", exclusive=True) + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("one")) + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("two")) + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("three")) + + #check that the queue now reports 3 messages: + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(3, reply.message_count) + + #now do the purge, then test that three messages are purged and the count drops to 0 + reply = channel.queue_purge(queue="test-queue"); + self.assertEqual(3, reply.message_count) + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(0, reply.message_count) + + #send a further message and consume it, ensuring that the other messages are really gone + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("four")) + reply = channel.basic_consume(queue="test-queue", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + msg = queue.get(timeout=1) + self.assertEqual("four", msg.content.body) + + #check error conditions (use new channels): + channel = self.client.channel(2) + channel.channel_open() + try: + #queue specified but doesn't exist: + channel.queue_purge(queue="invalid-queue") + self.fail("Expected failure when purging non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(3) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.queue_purge() + self.fail("Expected failure when purging unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + #cleanup + channel = self.client.channel(4) + channel.channel_open() + channel.exchange_delete(exchange="test-exchange") + + def test_declare_exclusive(self): + """ + Test that the exclusive field is honoured in queue.declare + """ + # TestBase.setUp has already opened channel(1) + c1 = self.channel + # Here we open a second separate connection: + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + + #declare an exclusive queue: + c1.queue_declare(queue="exclusive-queue", exclusive="True") + try: + #other connection should not be allowed to declare this: + c2.queue_declare(queue="exclusive-queue", exclusive="True") + self.fail("Expected second exclusive queue_declare to raise a channel exception") + except Closed, e: + self.assertChannelException(405, e.args[0]) + + + def test_declare_passive(self): + """ + Test that the passive field is honoured in queue.declare + """ + channel = self.channel + #declare an exclusive queue: + channel.queue_declare(queue="passive-queue-1", exclusive="True") + channel.queue_declare(queue="passive-queue-1", passive="True") + try: + #other connection should not be allowed to declare this: + channel.queue_declare(queue="passive-queue-2", passive="True") + self.fail("Expected passive declaration of non-existant queue to raise a channel exception") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_bind(self): + """ + Test various permutations of the queue.bind method + """ + channel = self.channel + channel.queue_declare(queue="queue-1", exclusive="True") + + #straightforward case, both exchange & queue exist so no errors expected: + channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + + #bind the default queue for the channel (i.e. last one declared): + channel.queue_bind(exchange="amq.direct", routing_key="key2") + + #use the queue name where neither routing key nor queue are specified: + channel.queue_bind(exchange="amq.direct") + + #try and bind to non-existant exchange + try: + channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + self.fail("Expected bind to non-existant exchange to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #need to reopen a channel: + channel = self.client.channel(2) + channel.channel_open() + + #try and bind non-existant queue: + try: + channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + self.fail("Expected bind of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_delete_simple(self): + """ + Test basic queue deletion + """ + channel = self.client.channel(1) + channel.channel_open() + + #straight-forward case: + channel.queue_declare(queue="delete-me") + channel.basic_publish(exchange="amq.direct", routing_key="delete-me", content=Content("a")) + channel.basic_publish(exchange="amq.direct", routing_key="delete-me", content=Content("b")) + channel.basic_publish(exchange="amq.direct", routing_key="delete-me", content=Content("c")) + reply = channel.queue_delete(queue="delete-me") + self.assertEqual(3, reply.message_count) + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="delete-me", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #check attempted deletion of non-existant queue is handled correctly: + channel = self.client.channel(2) + channel.channel_open() + try: + channel.queue_delete(queue="i-dont-exist", if_empty="True") + self.fail("Expected delete of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + + def test_delete_ifempty(self): + """ + Test that if_empty field of queue_delete is honoured + """ + channel = self.client.channel(1) + channel.channel_open() + + #create a queue and add a message to it (use default binding): + channel.queue_declare(queue="delete-me-2") + channel.queue_declare(queue="delete-me-2", passive="True") + channel.basic_publish(exchange="amq.direct", routing_key="delete-me-2", content=Content("message")) + + #try to delete, but only if empty: + try: + channel.queue_delete(queue="delete-me-2", if_empty="True") + self.fail("Expected delete if_empty to fail for non-empty queue") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + #need new channel now: + channel = self.client.channel(2) + channel.channel_open() + + #empty queue: + reply = channel.basic_consume(queue="delete-me-2", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + msg = queue.get(timeout=1) + self.assertEqual("message", msg.content.body) + channel.basic_cancel(consumer_tag=reply.consumer_tag) + + #retry deletion on empty queue: + channel.queue_delete(queue="delete-me-2", if_empty="True") + + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-2", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_delete_ifunused(self): + """ + Test that if_unused field of queue_delete is honoured + """ + channel = self.client.channel(1) + channel.channel_open() + + #create a queue and register a consumer: + channel.queue_declare(queue="delete-me-3") + channel.queue_declare(queue="delete-me-3", passive="True") + reply = channel.basic_consume(queue="delete-me-3", no_ack=True) + + #need new channel now: + channel2 = self.client.channel(2) + channel2.channel_open() + #try to delete, but only if empty: + try: + channel2.queue_delete(queue="delete-me-3", if_unused="True") + self.fail("Expected delete if_unused to fail for queue with existing consumer") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + + channel.basic_cancel(consumer_tag=reply.consumer_tag) + channel.queue_delete(queue="delete-me-3", if_unused="True") + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-3", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + -- cgit v1.2.1 From 35244d13859c33821977ae7e407439c7e00add45 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Thu, 21 Sep 2006 18:26:31 +0000 Subject: Implemented topic pattern matching for the TopicExchange. Corrected default bindings to use the exchange named "" rather than "amqp.direct". Added python and unit tests for all of the above. Minor improvements to testlib.py, also some tests for testlib itself. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@448624 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 45 +++++++++++++++++++++++++++++-------------- qpid/python/tests/exchange.py | 44 ++++++++++++++++++++++++++++++------------ qpid/python/tests/queue.py | 8 ++++---- 3 files changed, 67 insertions(+), 30 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index ff9ecbee8a..0bec6a8708 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -14,11 +14,13 @@ # limitations under the License. # +# # Support library for qpid python tests. # import sys, re, unittest, os, random, logging import qpid.client, qpid.spec +import Queue from getopt import getopt, GetoptError @@ -188,26 +190,41 @@ class TestBase(unittest.TestCase): self.exchanges.append((channel,exchange)) return reply - def assertPublishConsume(self, queue="", exchange="", routing_key=""): - """ - Publish a message and consume it, assert it comes back intact. + def uniqueString(self): + """Generate a unique string, unique for this TestBase instance""" + # TODO aconway 2006-09-20: Not thread safe. + if not "uniqueCounter" in dir(self): self.uniqueCounter = 1; + return "Test Message " + str(self.uniqueCounter) + + def consume(self, queueName): + """Consume from named queue returns the Queue object.""" + reply = self.channel.basic_consume(queue=queueName, no_ack=True) + return self.client.queue(reply.consumer_tag) + + def assertEmpty(self, queue): + """Assert that the queue is empty""" + try: + queue.get(timeout=1) + self.fail("Queue is not empty.") + except Queue.Empty: None # Ignore - queue can be a single queue name or a list of queue names. - For a list assert the message appears on all queues. - Crude attempt to make unique messages so we can't consume - a message not really meant for us. + def assertPublishGet(self, queue, exchange="", routing_key=""): """ - body = "TestMessage("+str(random.randint(999999, 1000000))+")" + Publish to exchange and assert queue.get() returns the same message. + """ + body = self.uniqueString() self.channel.basic_publish(exchange=exchange, content=qpid.content.Content(body), routing_key=routing_key) - if not isinstance(queue, list): queue = [queue] - for q in queue: - reply = self.channel.basic_consume(queue=q, no_ack=True) - msg = self.client.queue(reply.consumer_tag).get(timeout=2) - self.assertEqual(body, msg.content.body) - + self.assertEqual(body, queue.get(timeout=2).content.body) + def assertPublishConsume(self, queue="", exchange="", routing_key=""): + """ + Publish a message and consume it, assert it comes back intact. + Return the Queue object used to consume. + """ + self.assertPublishGet(self.consume(queue), exchange, routing_key) + def assertChannelException(self, expectedCode, message): self.assertEqual(message.method.klass.name, "channel") self.assertEqual(message.method.name, "close") diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py index b9b16bad78..4eb64520e6 100644 --- a/qpid/python/tests/exchange.py +++ b/qpid/python/tests/exchange.py @@ -57,9 +57,25 @@ class StandardExchangeVerifier: self.channel.queue_bind(queue="q", exchange=ex) self.queue_declare(queue="p") self.channel.queue_bind(queue="p", exchange=ex) - self.assertPublishConsume(exchange=ex, queue=["q","p"]) + for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) + + def verifyTopicExchange(self, ex): + """Verify that ex behaves like a topic exchange""" + self.queue_declare(queue="a") + self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + q = self.consume("a") + self.assertPublishGet(q, ex, "a.b.x") + self.assertPublishGet(q, ex, "a.x.b.x") + self.assertPublishGet(q, ex, "a.x.x.b.x") + + # Shouldn't match + self.channel.basic_publish(exchange=ex, routing_key="a.b") + self.channel.basic_publish(exchange=ex, routing_key="a.b.x.y") + self.channel.basic_publish(exchange=ex, routing_key="x.a.b.x") + self.channel.basic_publish(exchange=ex, routing_key="a.b") + self.assert_(q.empty()) + - class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): """ The server SHOULD implement these standard exchange types: topic, headers. @@ -76,6 +92,11 @@ class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): """Declare and test a fanout exchange""" self.exchange_declare(0, exchange="f", type="fanout") self.verifyFanOutExchange("f") + + def testTopic(self): + """Declare and test a topic exchange""" + self.exchange_declare(0, exchange="t", type="topic") + self.verifyTopicExchange("t") class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): @@ -88,24 +109,17 @@ class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): exchange instance (amq.fanout, amq.direct, and amq.topic, amq.headers if those types are defined). """ - # TODO aconway 2006-09-01: Add tests for 3.1.3.1: - # - Test auto binding by q name - # - Test the nameless "default publish" exchange. - # - Auto created amq.fanout exchange - def testAmqDirect(self): self.verifyDirectExchange("amq.direct") def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") - def testAmqTopic(self): - self.exchange_declare(0, exchange="amq.topic", passive="true") - # TODO aconway 2006-09-14: verify topic behavior + def testAmqTopic(self): self.verifyTopicExchange("amq.topic") def testAmqHeaders(self): self.exchange_declare(0, exchange="amq.headers", passive="true") # TODO aconway 2006-09-14: verify headers behavior -class DefaultExchangeRuleTests(TestBase): +class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): """ The server MUST predeclare a direct exchange to act as the default exchange for content Publish methods and for default queue bindings. @@ -115,6 +129,12 @@ class DefaultExchangeRuleTests(TestBase): routing key but without specifying the exchange name, then ensuring that the message arrives in the queue correctly. """ + def testDefaultExchange(self): + # Test automatic binding by queue name. + self.queue_declare(queue="d") + self.assertPublishConsume(queue="d", routing_key="d") + # Test explicit bind to default queue + self.verifyDirectExchange("") class DefaultAccessRuleTests(TestBase): @@ -123,7 +143,7 @@ class DefaultAccessRuleTests(TestBase): by specifying an empty exchange name in the Queue.Bind and content Publish methods. """ - + # TODO aconway 2006-09-18: fill this in. class ExtensionsRuleTests(TestBase): """ diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py index 92260a7d64..65baaaa00b 100644 --- a/qpid/python/tests/queue.py +++ b/qpid/python/tests/queue.py @@ -156,9 +156,9 @@ class QueueTests(TestBase): #straight-forward case: channel.queue_declare(queue="delete-me") - channel.basic_publish(exchange="amq.direct", routing_key="delete-me", content=Content("a")) - channel.basic_publish(exchange="amq.direct", routing_key="delete-me", content=Content("b")) - channel.basic_publish(exchange="amq.direct", routing_key="delete-me", content=Content("c")) + channel.basic_publish(routing_key="delete-me", content=Content("a")) + channel.basic_publish(routing_key="delete-me", content=Content("b")) + channel.basic_publish(routing_key="delete-me", content=Content("c")) reply = channel.queue_delete(queue="delete-me") self.assertEqual(3, reply.message_count) #check that it has gone be declaring passively @@ -189,7 +189,7 @@ class QueueTests(TestBase): #create a queue and add a message to it (use default binding): channel.queue_declare(queue="delete-me-2") channel.queue_declare(queue="delete-me-2", passive="True") - channel.basic_publish(exchange="amq.direct", routing_key="delete-me-2", content=Content("message")) + channel.basic_publish(routing_key="delete-me-2", content=Content("message")) #try to delete, but only if empty: try: -- cgit v1.2.1 From 69717c3785632ae8c1f85a5e8ee07def87be2ef6 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 22 Sep 2006 07:43:22 +0000 Subject: Updated test for fix in broker to use un-named exchange as the default (rather than amq.direct) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@448851 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index b912fc40be..c3a2414eac 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -34,8 +34,8 @@ class BasicTests(TestBase): channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) #send a message - channel.basic_publish(exchange="amq.direct", routing_key="test-queue-1a", content=Content("consume_no_local")) - channel.basic_publish(exchange="amq.direct", routing_key="test-queue-1b", content=Content("consume_no_local")) + channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local")) + channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local")) #check the queues of the two consumers excluded = self.client.queue("local_excluded") -- cgit v1.2.1 From 924764a6a8dbcb0a2dd7a432221987302b389af8 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 22 Sep 2006 09:53:47 +0000 Subject: Added tests for basic_cancel and for handling of invalid channel ids. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@448881 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 24 ++++++++++++++++++++++++ qpid/python/tests/broker.py | 18 ++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index c3a2414eac..75d49ec805 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -113,3 +113,27 @@ class BasicTests(TestBase): except Closed, e: self.assertConnectionException(530, e.args[0]) + def test_basic_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") + channel.basic_publish(routing_key="test-queue-4", content=Content("One")) + + #cancel should stop messages being delivered + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.content.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_cancel(consumer_tag="this-never-existed") diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index 1345076604..307d447a6c 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from qpid.client import Closed from qpid.queue import Empty from qpid.content import Content from qpid.testlib import testrunner, TestBase @@ -82,3 +83,20 @@ class BrokerTests(TestBase): msg = queue.get(timeout=5) self.assert_(msg.content.body == body) + def test_invalid_channel(self): + other = self.connect() + channel = other.channel(200) + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for invalid channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) + + channel = self.client.channel(200) + channel.channel_open() + channel.channel_close() + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for closed channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) -- cgit v1.2.1 From 6062c7e903daca2aa0d337d5b2ddbc99e9334a32 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 25 Sep 2006 17:33:26 +0000 Subject: Close channels on fatal errors to prevent hangs, provide useful stack trace. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@449750 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/message.py | 2 +- qpid/python/qpid/peer.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index 08b3e70c0b..ae06c934c1 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -26,7 +26,7 @@ class Message: def __len__(self): l = len(self.fields) - if method.content: + if self.method.content: l += 1 return len(self.fields) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 4179a05568..31d3d24f5f 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -57,13 +57,10 @@ class Peer: thread.start_new_thread(self.reader, ()) thread.start_new_thread(self.worker, ()) - def fatal(message=None): - """Call when an unexpected exception occurs that will kill a thread. - - In this case it's better to crash the process than to continue in - an invalid state with a missing thread.""" + def fatal(self, message=None): + """Call when an unexpected exception occurs that will kill a thread.""" if message: print >> sys.stderr, message - traceback.print_exc() + self.close("Fatal error: %s\n%s" % (message or "", traceback.format_exc())) def reader(self): try: -- cgit v1.2.1 From 0b9afe21f8632ab0621da6f6dfd8563a06e4e203 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 25 Sep 2006 18:01:42 +0000 Subject: Close channels on fatal errors to prevent hangs, provide useful stack trace. Stated test module for the testlib itself. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@449763 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/testlib.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 qpid/python/tests/testlib.py (limited to 'qpid/python') diff --git a/qpid/python/tests/testlib.py b/qpid/python/tests/testlib.py new file mode 100644 index 0000000000..a50f8140b4 --- /dev/null +++ b/qpid/python/tests/testlib.py @@ -0,0 +1,54 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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. +# + +# +# Tests for the testlib itself. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from Queue import Empty + +import sys +from traceback import * + +def mytrace(frame, event, arg): + print_stack(frame); + print "====" + return mytrace + +class TestBaseTest(TestBase): + """Verify TestBase functions work as expected""" + + def testAssertEmptyPass(self): + """Test assert empty works""" + self.queue_declare(queue="empty") + q = self.consume("empty") + self.assertEmpty(q) + try: + q.get(timeout=1) + self.fail("Queue is not empty.") + except Empty: None # Ignore + + def testAssertEmptyFail(self): + self.queue_declare(queue="full") + q = self.consume("full") + self.channel.basic_publish(routing_key="full") + try: + self.assertEmpty(q); + self.fail("assertEmpty did not assert on non-empty queue") + except AssertionError: None # Ignore + -- cgit v1.2.1 From b65e020ec427ab77bcb6d6aac8b56662140abc9f Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 27 Sep 2006 13:21:43 +0000 Subject: Initial implementation of basic_ack & basic_recover git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@450434 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/broker.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index 307d447a6c..1c31767ef8 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -100,3 +100,48 @@ class BrokerTests(TestBase): self.fail("Expected error on queue_declare for closed channel") except Closed, e: self.assertConnectionException(504, e.args[0]) + + + def test_acknowledgement(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue") + + reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) + queue = self.client.queue(reply.consumer_tag) + + channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None -- cgit v1.2.1 From cb3fe168a5c4c0c91b5d32ff28b176d57c8eb870 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 27 Sep 2006 16:44:02 +0000 Subject: Moved ack tests to basic class, added test for requeueing on recovery. Implemented requeuing on recovery. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@450504 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 101 ++++++++++++++++++++++++++++++++++++++++++++ qpid/python/tests/broker.py | 44 ------------------- 2 files changed, 101 insertions(+), 44 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 75d49ec805..9ffcd11b95 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -137,3 +137,104 @@ class BasicTests(TestBase): #cancellation of non-existant consumers should be handled without error channel.basic_cancel(consumer_tag="my-consumer") channel.basic_cancel(consumer_tag="this-never-existed") + + + def test_basic_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue") + + reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) + queue = self.client.queue(reply.consumer_tag) + + channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + def test_basic_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue") + + subscription = channel.basic_consume(queue="test-requeue", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + channel.basic_publish(routing_key="test-requeue", content=Content("One")) + channel.basic_publish(routing_key="test-requeue", content=Content("Two")) + channel.basic_publish(routing_key="test-requeue", content=Content("Three")) + channel.basic_publish(routing_key="test-requeue", content=Content("Four")) + channel.basic_publish(routing_key="test-requeue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_cancel(consumer_tag=subscription.consumer_tag) + subscription2 = channel.basic_consume(queue="test-requeue") + queue2 = self.client.queue(subscription2.consumer_tag) + + channel.basic_recover(requeue=True) + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + self.assertTrue(msg3b.redelivered) + self.assertTrue(msg5b.redelivered) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.content.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.content.body) + except Empty: None + diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index 1c31767ef8..f7cb3bcef3 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -101,47 +101,3 @@ class BrokerTests(TestBase): except Closed, e: self.assertConnectionException(504, e.args[0]) - - def test_acknowledgement(self): - """ - Test basic ack/recover behaviour - """ - channel = self.channel - channel.queue_declare(queue="test-ack-queue") - - reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) - queue = self.client.queue(reply.consumer_tag) - - channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four - - channel.basic_recover(requeue=False) - - msg3b = queue.get(timeout=1) - msg5b = queue.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None -- cgit v1.2.1 From 3d9cd9a1f350c8970c6cd0da20d918b831342636 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 27 Sep 2006 19:50:23 +0000 Subject: git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@450556 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 8 +++-- qpid/python/qpid/peer.py | 3 +- qpid/python/qpid/testlib.py | 20 +++++------ qpid/python/tests/exchange.py | 76 ++++++++++++++++++++++++++++++++---------- qpid/python/tests/testlib.py | 9 +++++ 5 files changed, 81 insertions(+), 35 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index f4d0817e60..fc6c147f2b 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -20,7 +20,7 @@ to read and write Frame objects. This could be used by a client, server, or even a proxy implementation. """ -import socket, codec +import socket, codec,logging from cStringIO import StringIO from spec import load, pythonize from codec import EOF @@ -240,8 +240,10 @@ class Header(Payload): properties = {} for b, f in zip(bits, klass.fields): if b: - properties[f.name] = c.decode(f.type) - + # Note: decode returns a unicode u'' string but only + # plain '' strings can be used as keywords so we need to + # stringify the names. + properties[str(f.name)] = c.decode(f.type) return Header(klass, weight, size, **properties) def __str__(self): diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 31d3d24f5f..3085e24247 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -146,7 +146,6 @@ class Channel: def invoke(self, method, args, content = None): if self.closed: raise Closed(self.reason) - frame = Frame(self.id, Method(method, *args)) self.outgoing.put(frame) @@ -181,7 +180,7 @@ class Channel: def write_content(self, klass, content, queue): size = content.size() - header = Frame(self.id, Header(klass, content.weight(), size)) + header = Frame(self.id, Header(klass, content.weight(), size, **content.properties)) queue.put(header) for child in content.children: self.write_content(klass, child, queue) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 0bec6a8708..92925bea20 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -22,7 +22,7 @@ import sys, re, unittest, os, random, logging import qpid.client, qpid.spec import Queue from getopt import getopt, GetoptError - +from qpid.content import Content def findmodules(root): """Find potential python modules under directory root""" @@ -161,10 +161,6 @@ class TestBase(unittest.TestCase): self.channel.channel_open() def tearDown(self): - # TODO aconway 2006-09-05: Wrong behaviour here, we should - # close all open channels (checking for exceptions on the - # channesl) then open a channel to clean up qs and exs, - # finally close that channel. for ch, q in self.queues: ch.queue_delete(queue=q) for ch, ex in self.exchanges: @@ -186,13 +182,11 @@ class TestBase(unittest.TestCase): arguments={}): channel = channel or self.channel reply = channel.exchange_declare(ticket, exchange, type, passive, durable, auto_delete, internal, nowait, arguments) - # TODO aconway 2006-09-14: Don't add exchange on failure. self.exchanges.append((channel,exchange)) return reply def uniqueString(self): """Generate a unique string, unique for this TestBase instance""" - # TODO aconway 2006-09-20: Not thread safe. if not "uniqueCounter" in dir(self): self.uniqueCounter = 1; return "Test Message " + str(self.uniqueCounter) @@ -208,22 +202,24 @@ class TestBase(unittest.TestCase): self.fail("Queue is not empty.") except Queue.Empty: None # Ignore - def assertPublishGet(self, queue, exchange="", routing_key=""): + def assertPublishGet(self, queue, exchange="", routing_key="", properties=None): """ Publish to exchange and assert queue.get() returns the same message. """ body = self.uniqueString() self.channel.basic_publish(exchange=exchange, - content=qpid.content.Content(body), + content=Content(body, properties=properties), routing_key=routing_key) - self.assertEqual(body, queue.get(timeout=2).content.body) + msg = queue.get(timeout=1) + self.assertEqual(body, msg.content.body) + if (properties): self.assertEqual(properties, msg.content.properties) - def assertPublishConsume(self, queue="", exchange="", routing_key=""): + def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): """ Publish a message and consume it, assert it comes back intact. Return the Queue object used to consume. """ - self.assertPublishGet(self.consume(queue), exchange, routing_key) + self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) def assertChannelException(self, expectedCode, message): self.assertEqual(message.method.klass.name, "channel") diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py index 4eb64520e6..8f3504b15e 100644 --- a/qpid/python/tests/exchange.py +++ b/qpid/python/tests/exchange.py @@ -20,22 +20,11 @@ Tests for exchange behaviour. Test classes ending in 'RuleTests' are derived from rules in amqp.xml. """ -import logging, Queue +import Queue, logging from qpid.testlib import TestBase from qpid.content import Content -# TODO aconway 2006-09-01: Investigate and add tests as appropriate. -# Observered on C++: -# -# No exception raised for basic_consume on non-existent queue name. -# No exception for basic_publish with bad routing key. -# No exception for binding to non-existent exchange? -# queue_bind hangs with invalid exchange name -# -# Do server exceptions get propagated properly? -# Do Java exceptions propagate with any data (or just Closed()) - class StandardExchangeVerifier: """Verifies standard exchange behavior. @@ -67,7 +56,6 @@ class StandardExchangeVerifier: self.assertPublishGet(q, ex, "a.b.x") self.assertPublishGet(q, ex, "a.x.b.x") self.assertPublishGet(q, ex, "a.x.x.b.x") - # Shouldn't match self.channel.basic_publish(exchange=ex, routing_key="a.b") self.channel.basic_publish(exchange=ex, routing_key="a.b.x.y") @@ -75,6 +63,16 @@ class StandardExchangeVerifier: self.channel.basic_publish(exchange=ex, routing_key="a.b") self.assert_(q.empty()) + def verifyHeadersExchange(self, ex): + """Verify that ex is a headers exchange""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) + q = self.consume("q") + headers = {"name":"fred", "age":3} + self.assertPublishGet(q, exchange=ex, properties={'headers':headers}) + self.channel.basic_publish(exchange=ex) # No headers, won't deliver + self.assertEmpty(q); + class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): """ @@ -97,6 +95,11 @@ class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): """Declare and test a topic exchange""" self.exchange_declare(0, exchange="t", type="topic") self.verifyTopicExchange("t") + + def testHeaders(self): + """Declare and test a headers exchange""" + self.exchange_declare(0, exchange="h", type="headers") + self.verifyHeadersExchange("h") class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): @@ -106,7 +109,7 @@ class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): exchange instance is amq. followed by the exchange type name. Client creates a temporary queue and attempts to bind to each required - exchange instance (amq.fanout, amq.direct, and amq.topic, amq.headers if + exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if those types are defined). """ def testAmqDirect(self): self.verifyDirectExchange("amq.direct") @@ -115,9 +118,7 @@ class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): def testAmqTopic(self): self.verifyTopicExchange("amq.topic") - def testAmqHeaders(self): - self.exchange_declare(0, exchange="amq.headers", passive="true") - # TODO aconway 2006-09-14: verify headers behavior + def testAmqMatch(self): self.verifyHeadersExchange("amq.match") class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): """ @@ -137,13 +138,14 @@ class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): self.verifyDirectExchange("") +# TODO aconway 2006-09-27: Fill in empty tests: + class DefaultAccessRuleTests(TestBase): """ The server MUST NOT allow clients to access the default exchange except by specifying an empty exchange name in the Queue.Bind and content Publish methods. """ - # TODO aconway 2006-09-18: fill this in. class ExtensionsRuleTests(TestBase): """ @@ -252,3 +254,41 @@ class DeleteMethodExchangeFieldExistsRuleTests(TestBase): """ +class HeadersExchangeTests(TestBase): + """ + Tests for headers exchange functionality. + """ + def setUp(self): + TestBase.setUp(self) + self.queue_declare(queue="q") + self.q = self.consume("q") + + def myAssertPublishGet(self, headers): + self.assertPublishGet(self.q, exchange="amq.match", properties={'headers':headers}) + + def myBasicPublish(self, headers): + self.channel.basic_publish(exchange="amq.match", content=Content("foobar", properties={'headers':headers})) + + def testMatchAll(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) + + # None of these should match + self.myBasicPublish({}) + self.myBasicPublish({"name":"barney"}) + self.myBasicPublish({"name":10}) + self.myBasicPublish({"name":"fred", "age":2}) + self.assertEmpty(self.q) + + def testMatchAny(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred"}) + self.myAssertPublishGet({"name":"fred", "ignoreme":10}) + self.myAssertPublishGet({"ignoreme":10, "age":3}) + + # Wont match + self.myBasicPublish({}) + self.myBasicPublish({"irrelevant":0}) + self.assertEmpty(self.q) + diff --git a/qpid/python/tests/testlib.py b/qpid/python/tests/testlib.py index a50f8140b4..6a2efb6a11 100644 --- a/qpid/python/tests/testlib.py +++ b/qpid/python/tests/testlib.py @@ -52,3 +52,12 @@ class TestBaseTest(TestBase): self.fail("assertEmpty did not assert on non-empty queue") except AssertionError: None # Ignore + def testMessageProperties(self): + """Verify properties are passed with message""" + props={"headers":{"x":1, "y":2}} + self.queue_declare(queue="q") + q = self.consume("q") + self.assertPublishGet(q, routing_key="q", properties=props) + + + -- cgit v1.2.1 From 152914698c7b75c9bea6a121fab45fde3a582f0a Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 10 Oct 2006 10:06:36 +0000 Subject: Implementation and tests for basic_qos (i.e. prefetching) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@454677 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 97 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 9ffcd11b95..50004614eb 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -113,7 +113,7 @@ class BasicTests(TestBase): except Closed, e: self.assertConnectionException(530, e.args[0]) - def test_basic_cancel(self): + def test_cancel(self): """ Test compliance of the basic.cancel method """ @@ -139,7 +139,7 @@ class BasicTests(TestBase): channel.basic_cancel(consumer_tag="this-never-existed") - def test_basic_ack(self): + def test_ack(self): """ Test basic ack/recover behaviour """ @@ -183,7 +183,7 @@ class BasicTests(TestBase): self.fail("Got unexpected message: " + extra.content.body) except Empty: None - def test_basic_recover_requeue(self): + def test_recover_requeue(self): """ Test requeing on recovery """ @@ -238,3 +238,94 @@ class BasicTests(TestBase): self.fail("Got unexpected message in original queue: " + extra.content.body) except Empty: None + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 5: + channel.basic_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.basic_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.content.body) -- cgit v1.2.1 From deff1e751c9d0c1baec2d0b7a9e5b88d5668a861 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 11 Oct 2006 08:24:42 +0000 Subject: Implementation of basic_get. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@462729 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 61 +++++++++++++++++++++++++++++++++++++++++++++ qpid/python/tests/broker.py | 2 -- 2 files changed, 61 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 50004614eb..afdf1a4003 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -329,3 +329,64 @@ class BasicTests(TestBase): channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) msg = queue.get(timeout=1) self.assertEqual(large, msg.content.body) + + def test_get(self): + """ + Test basic_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + #use basic_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-ok") + self.assertEqual("Message %d" % i, reply.content.body) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + for i in range(11, 21): + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-ok") + self.assertEqual("Message %d" % i, reply.content.body) + if(i == 13): + channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) + if(i in [15, 17, 19]): + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-empty") + + #recover(requeue=True) + channel.basic_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-ok") + self.assertEqual("Message %d" % i, reply.content.body) + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-empty") + + channel.basic_recover(requeue=True) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get-empty") diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index f7cb3bcef3..173391c91b 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -45,8 +45,6 @@ class BrokerTests(TestBase): msg = self.client.queue(ctag).get(timeout = 5) ch.basic_ack(delivery_tag = msg.delivery_tag) self.assert_(msg.content.body == body) - - # TODO: Ensure we get a failure if an ack consumer doesn't ack. def test_basic_delivery_immediate(self): """ -- cgit v1.2.1 From 98d82ee5d6a7f5906716475f0fff41d0fa3a6e2d Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 17 Oct 2006 15:29:28 +0000 Subject: Added test for simple commit and rollback. Updated 'failing' lists Fix for amqp methods with no arguments git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@464943 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing.txt | 2 + qpid/python/java_failing.txt | 16 +++++ qpid/python/qpid/spec.py | 2 + qpid/python/tests/tx.py | 143 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 qpid/python/tests/tx.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing.txt b/qpid/python/cpp_failing.txt index e69de29bb2..167fc9cce5 100644 --- a/qpid/python/cpp_failing.txt +++ b/qpid/python/cpp_failing.txt @@ -0,0 +1,2 @@ +tests.tx.TxTests.test_commit +tests.tx.TxTests.test_rollback diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index 2e61363817..a752bc53c0 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,9 +1,23 @@ +tests.basic.BasicTests.test_cancel tests.basic.BasicTests.test_consume_exclusive tests.basic.BasicTests.test_consume_no_local tests.basic.BasicTests.test_consume_queue_errors tests.basic.BasicTests.test_consume_unique_consumers +tests.basic.BasicTests.test_get +tests.basic.BasicTests.test_qos_prefetch_size +tests.basic.BasicTests.test_recover_requeue +tests.exchange.ExchangeTests +tests.exchange.DefaultExchangeRuleTests.testDefaultExchange +tests.exchange.HeadersExchangeTests.testMatchAll +tests.exchange.HeadersExchangeTests.testMatchAny +tests.exchange.RecommendedTypesRuleTests.testDirect tests.exchange.RecommendedTypesRuleTests.testFanout +tests.exchange.RecommendedTypesRuleTests.testHeaders +tests.exchange.RecommendedTypesRuleTests.testTopic +tests.exchange.RequiredInstancesRuleTests.testAmqDirect tests.exchange.RequiredInstancesRuleTests.testAmqFanOut +tests.exchange.RequiredInstancesRuleTests.testAmqMatch +tests.exchange.RequiredInstancesRuleTests.testAmqTopic tests.queue.QueueTests.test_declare_exclusive tests.queue.QueueTests.test_declare_passive tests.queue.QueueTests.test_delete_ifempty @@ -11,3 +25,5 @@ tests.queue.QueueTests.test_delete_ifunused tests.queue.QueueTests.test_delete_simple tests.queue.QueueTests.test_purge tests.queue.QueueTests.test_bind +tests.testlib.TestBaseTest.testMessageProperties +tests.broker.BrokerTests.test_invalid_channel diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 70e09aa1e9..b270c45e13 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -204,6 +204,8 @@ class Method(Metadata): code += " return self.invoke(%s" % Method.METHOD if argnames: code += ", (%s,)" % argnames + else: + code += ", ()" if self.content: code += ", content" code += ")" diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py new file mode 100644 index 0000000000..b1030bf602 --- /dev/null +++ b/qpid/python/tests/tx.py @@ -0,0 +1,143 @@ +# +# Copyright (c) 2006 The Apache Software Foundation +# +# 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 qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class TxTests(TestBase): + """ + Tests for 'methods' on the amqp tx 'class' + """ + + def test_commit(self): + """ + Test that commited publishes are delivered and commited acks are not re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c") + channel.tx_commit() + + #check results + for i in range(1, 5): + msg = queue_c.get(timeout=1) + self.assertEqual("TxMessage %d" % i, msg.content.body) + + msg = queue_b.get(timeout=1) + self.assertEqual("TxMessage 6", msg.content.body) + + msg = queue_a.get(timeout=1) + self.assertEqual("TxMessage 7", msg.content.body) + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.basic_ack(delivery_tag=0, multiple=True) + channel.tx_commit() + + def test_rollback(self): + """ + Test that rolled back publishes are not delivered and rolled back acks are re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.basic_ack(delivery_tag=0, multiple=True) + channel.tx_commit() + + def perform_txn_work(self, channel, name_a, name_b, name_c): + """ + Utility method that does some setup and some work under a transaction. Used for testing both + commit and rollback + """ + #setup: + channel.queue_declare(queue=name_a, exclusive=True) + channel.queue_declare(queue=name_b, exclusive=True) + channel.queue_declare(queue=name_c, exclusive=True) + + key = "my_key_" + name_b + topic = "my_topic_" + name_c + + channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) + channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) + + for i in range(1, 5): + channel.basic_publish(routing_key=name_a, content=Content("Message %d" % i)) + + channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("Message 6")) + channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("Message 7")) + + + channel.tx_select() + + #consume and ack messages + sub_a = channel.basic_consume(queue=name_a, no_ack=False) + queue_a = self.client.queue(sub_a.consumer_tag) + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + sub_b = channel.basic_consume(queue=name_b, no_ack=False) + queue_b = self.client.queue(sub_b.consumer_tag) + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + channel.basic_ack(delivery_tag=msg.delivery_tag) + + sub_c = channel.basic_consume(queue=name_c, no_ack=False) + queue_c = self.client.queue(sub_c.consumer_tag) + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + channel.basic_ack(delivery_tag=msg.delivery_tag) + + #publish messages + for i in range(1, 5): + channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("TxMessage %d" % i)) + + channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("TxMessage 6")) + channel.basic_publish(routing_key=name_a, content=Content("TxMessage 7")) + + return queue_a, queue_b, queue_c -- cgit v1.2.1 From 280bff8336167763e11c02ce4acc11bf9b90558d Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 30 Oct 2006 19:28:40 +0000 Subject: c++ broker now passes the basic tx tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@469243 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing.txt | 2 -- qpid/python/tests/tx.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing.txt b/qpid/python/cpp_failing.txt index 167fc9cce5..e69de29bb2 100644 --- a/qpid/python/cpp_failing.txt +++ b/qpid/python/cpp_failing.txt @@ -1,2 +0,0 @@ -tests.tx.TxTests.test_commit -tests.tx.TxTests.test_rollback diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py index b1030bf602..c84a228f2a 100644 --- a/qpid/python/tests/tx.py +++ b/qpid/python/tests/tx.py @@ -110,9 +110,8 @@ class TxTests(TestBase): channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("Message 6")) channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("Message 7")) - channel.tx_select() - + #consume and ack messages sub_a = channel.basic_consume(queue=name_a, no_ack=False) queue_a = self.client.queue(sub_a.consumer_tag) @@ -141,3 +140,31 @@ class TxTests(TestBase): channel.basic_publish(routing_key=name_a, content=Content("TxMessage 7")) return queue_a, queue_b, queue_c + + def test_commit_overlapping_acks(self): + """ + Test that logically 'overlapping' acks do not cause errors on commit + """ + channel = self.channel + channel.queue_declare(queue="commit-overlapping", exclusive=True) + for i in range(1, 10): + channel.basic_publish(routing_key="commit-overlapping", content=Content("Message %d" % i)) + + + channel.tx_select() + + sub = channel.basic_consume(queue="commit-overlapping", no_ack=False) + queue = self.client.queue(sub.consumer_tag) + for i in range(1, 10): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + if i in [3, 6, 10]: + channel.basic_ack(delivery_tag=msg.delivery_tag) + + channel.tx_commit() + + #check all have been acked: + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None -- cgit v1.2.1 From bcc0879b6eee7e831e869334a792704e7e22c26f Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 31 Oct 2006 18:33:40 +0000 Subject: Hid locking within exchange registry, switched to shared_ptr for exchanges, added some extra error handling and tests. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@469599 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/exchange.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py index 8f3504b15e..a823718f03 100644 --- a/qpid/python/tests/exchange.py +++ b/qpid/python/tests/exchange.py @@ -23,6 +23,7 @@ Test classes ending in 'RuleTests' are derived from rules in amqp.xml. import Queue, logging from qpid.testlib import TestBase from qpid.content import Content +from qpid.client import Closed class StandardExchangeVerifier: @@ -207,10 +208,14 @@ class DeclareMethodTypeFieldSupportRuleTests(TestBase): class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): """ If set, and the exchange does not already exist, the server MUST raise a - channel exception with reply code 404 (not found). - - + channel exception with reply code 404 (not found). """ + def test(self): + try: + self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) + self.fail("Expected 404 for passive declaration of unknown exchange.") + except Closed, e: + self.assertChannelException(404, e.args[0]) class DeclareMethodDurableFieldSupportRuleTests(TestBase): @@ -292,3 +297,28 @@ class HeadersExchangeTests(TestBase): self.myBasicPublish({"irrelevant":0}) self.assertEmpty(self.q) + +class MiscellaneousErrorsTests(TestBase): + """ + Test some miscellaneous error conditions + """ + def testTypeNotKnown(self): + try: + self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") + self.fail("Expected 503 for declaration of unknown exchange type.") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def testDifferentDeclaredType(self): + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + try: + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + self.fail("Expected 507 for redeclaration of exchange with different type.") + except Closed, e: + self.assertConnectionException(507, e.args[0]) + #cleanup + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + c2.exchange_delete(exchange="test_different_declared_type_exchange") + -- cgit v1.2.1 From f34ace35220726fa64f063a0fccc6eeaaa40af3c Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 8 Nov 2006 17:07:44 +0000 Subject: More reorg to separate APR/posix code, work in progress. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@472545 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 1 - 1 file changed, 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index afdf1a4003..314c20c8a0 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -260,7 +260,6 @@ class BasicTests(TestBase): for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.content.body) - try: extra = queue.get(timeout=1) self.fail("Got unexpected 6th message in original queue: " + extra.content.body) -- cgit v1.2.1 From b25510070965c07d4047655cc5b4aada50df9833 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 8 Nov 2006 19:36:45 +0000 Subject: removed dead code git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@472606 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegate.py | 1 - 1 file changed, 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py index 0467162498..00ee9c98db 100644 --- a/qpid/python/qpid/delegate.py +++ b/qpid/python/qpid/delegate.py @@ -36,7 +36,6 @@ class Delegate: def dispatch(self, channel, message): method = message.method - spec = method.klass.spec try: handler = self.handlers[method] -- cgit v1.2.1 From 22b4cec87dfe57326523337f2233a4033f532edb Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 8 Nov 2006 19:37:01 +0000 Subject: added default for timestamp git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@472608 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index b270c45e13..59af0cda2a 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -185,7 +185,8 @@ class Method(Metadata): "octet": 0, "short": 0, "long": 0, - "longlong": 0} + "longlong": 0, + "timestamp": 0} def define_method(self, name): g = {Method.METHOD: self} -- cgit v1.2.1 From 5404fc332f791e78fc7a957d393d0b517a807714 Mon Sep 17 00:00:00 2001 From: Stephen Vinoski Date: Fri, 10 Nov 2006 23:18:04 +0000 Subject: update Apache licenses to the current version git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@473568 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/amqp-doc | 29 ++++++++++++++++------------- qpid/python/pal2py | 19 +++++++++++++++++++ qpid/python/qpid/__init__.py | 29 ++++++++++++++++------------- qpid/python/qpid/client.py | 29 ++++++++++++++++------------- qpid/python/qpid/codec.py | 29 ++++++++++++++++------------- qpid/python/qpid/connection.py | 29 ++++++++++++++++------------- qpid/python/qpid/content.py | 29 ++++++++++++++++------------- qpid/python/qpid/delegate.py | 29 ++++++++++++++++------------- qpid/python/qpid/message.py | 29 ++++++++++++++++------------- qpid/python/qpid/peer.py | 29 ++++++++++++++++------------- qpid/python/qpid/queue.py | 29 ++++++++++++++++------------- qpid/python/qpid/spec.py | 29 ++++++++++++++++------------- qpid/python/qpid/testlib.py | 29 ++++++++++++++++------------- qpid/python/qpid/xmlutil.py | 29 ++++++++++++++++------------- qpid/python/rule2test | 19 +++++++++++++++++++ qpid/python/run-tests | 29 ++++++++++++++++------------- qpid/python/tests/__init__.py | 19 +++++++++++++++++++ qpid/python/tests/basic.py | 29 ++++++++++++++++------------- qpid/python/tests/broker.py | 29 ++++++++++++++++------------- qpid/python/tests/example.py | 29 ++++++++++++++++------------- qpid/python/tests/exchange.py | 29 ++++++++++++++++------------- qpid/python/tests/queue.py | 29 ++++++++++++++++------------- qpid/python/tests/testlib.py | 29 ++++++++++++++++------------- qpid/python/tests/tx.py | 29 ++++++++++++++++------------- 24 files changed, 393 insertions(+), 273 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/amqp-doc b/qpid/python/amqp-doc index a5b785fd73..60a12c2838 100755 --- a/qpid/python/amqp-doc +++ b/qpid/python/amqp-doc @@ -1,18 +1,21 @@ #!/usr/bin/env python # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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 sys, re from qpid.spec import load, pythonize diff --git a/qpid/python/pal2py b/qpid/python/pal2py index 48fa0111de..544151bf76 100755 --- a/qpid/python/pal2py +++ b/qpid/python/pal2py @@ -1,4 +1,23 @@ #!/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 sys, os, xml from qpid.spec import load, pythonize diff --git a/qpid/python/qpid/__init__.py b/qpid/python/qpid/__init__.py index 3f69e88e24..4363f175fb 100644 --- a/qpid/python/qpid/__init__.py +++ b/qpid/python/qpid/__init__.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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 spec, codec, connection, content, peer, delegate, client diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index cef10622ac..b4a282f251 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index c4bbe91f32..69c7ca8afa 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -1,19 +1,22 @@ #!/usr/bin/env python # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index fc6c147f2b..6f6c26559c 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/content.py b/qpid/python/qpid/content.py index 33c9ec35f4..bcbea1697c 100644 --- a/qpid/python/qpid/content.py +++ b/qpid/python/qpid/content.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py index 00ee9c98db..035bb3c476 100644 --- a/qpid/python/qpid/delegate.py +++ b/qpid/python/qpid/delegate.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index ae06c934c1..914b878147 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # from sets import Set diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 3085e24247..acffeb2537 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index 491cc3947d..5438b328ab 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 59af0cda2a..184ae157d1 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 92925bea20..f7a34294be 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # # diff --git a/qpid/python/qpid/xmlutil.py b/qpid/python/qpid/xmlutil.py index 8f8a7116f5..585516b44f 100644 --- a/qpid/python/qpid/xmlutil.py +++ b/qpid/python/qpid/xmlutil.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/rule2test b/qpid/python/rule2test index b57ea9e24e..10f151366e 100755 --- a/qpid/python/rule2test +++ b/qpid/python/rule2test @@ -1,5 +1,24 @@ #!/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. +# + # # Convert rules to tests # diff --git a/qpid/python/run-tests b/qpid/python/run-tests index c49bd32a96..90c0200d01 100755 --- a/qpid/python/run-tests +++ b/qpid/python/run-tests @@ -1,18 +1,21 @@ #!/usr/bin/env python # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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 sys diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py index d55ff3fd85..9a09d2d04f 100644 --- a/qpid/python/tests/__init__.py +++ b/qpid/python/tests/__init__.py @@ -1 +1,20 @@ # Do not delete - marks this directory as a python package. + +# +# 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. +# diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 314c20c8a0..6cc0d727d5 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # from qpid.client import Client, Closed from qpid.queue import Empty diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index 173391c91b..f3888589a5 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # from qpid.client import Closed from qpid.queue import Empty diff --git a/qpid/python/tests/example.py b/qpid/python/tests/example.py index 47d07fe9c5..bc84f002e0 100644 --- a/qpid/python/tests/example.py +++ b/qpid/python/tests/example.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # from qpid.content import Content diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py index a823718f03..121b47097d 100644 --- a/qpid/python/tests/exchange.py +++ b/qpid/python/tests/exchange.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # """ diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py index 65baaaa00b..d5c13fa87a 100644 --- a/qpid/python/tests/queue.py +++ b/qpid/python/tests/queue.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # from qpid.client import Client, Closed from qpid.queue import Empty diff --git a/qpid/python/tests/testlib.py b/qpid/python/tests/testlib.py index 6a2efb6a11..cab07cc4ac 100644 --- a/qpid/python/tests/testlib.py +++ b/qpid/python/tests/testlib.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # # diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py index c84a228f2a..e8d0e99628 100644 --- a/qpid/python/tests/tx.py +++ b/qpid/python/tests/tx.py @@ -1,17 +1,20 @@ # -# Copyright (c) 2006 The Apache Software Foundation -# -# 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. +# 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. # from qpid.client import Client, Closed from qpid.queue import Empty -- cgit v1.2.1 From 39afa34d1965ee3d97b8cc488ec33e46ea908699 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 17 Nov 2006 11:03:22 +0000 Subject: Some fixes and tests for bugs uncovered during testing of persistence. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@476108 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 12 ++++++------ qpid/python/tests/tx.py | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index f7a34294be..d35c38e4ad 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -225,13 +225,13 @@ class TestBase(unittest.TestCase): self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) def assertChannelException(self, expectedCode, message): - self.assertEqual(message.method.klass.name, "channel") - self.assertEqual(message.method.name, "close") - self.assertEqual(message.reply_code, expectedCode) + self.assertEqual("channel", message.method.klass.name) + self.assertEqual("close", message.method.name) + self.assertEqual(expectedCode, message.reply_code) def assertConnectionException(self, expectedCode, message): - self.assertEqual(message.method.klass.name, "connection") - self.assertEqual(message.method.name, "close") - self.assertEqual(message.reply_code, expectedCode) + self.assertEqual("connection", message.method.klass.name) + self.assertEqual("close", message.method.name) + self.assertEqual(expectedCode, message.reply_code) diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py index e8d0e99628..054fb8d8b7 100644 --- a/qpid/python/tests/tx.py +++ b/qpid/python/tests/tx.py @@ -55,6 +55,42 @@ class TxTests(TestBase): channel.basic_ack(delivery_tag=0, multiple=True) channel.tx_commit() + def test_auto_rollback(self): + """ + Test that a channel closed with an open transaction is effectively rolled back + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.basic_ack(delivery_tag=0, multiple=True) + channel.tx_commit() + def test_rollback(self): """ Test that rolled back publishes are not delivered and rolled back acks are re-delivered @@ -90,7 +126,7 @@ class TxTests(TestBase): #cleanup channel.basic_ack(delivery_tag=0, multiple=True) channel.tx_commit() - + def perform_txn_work(self, channel, name_a, name_b, name_c): """ Utility method that does some setup and some work under a transaction. Used for testing both -- cgit v1.2.1 From 6eb649e888e16fe966b8df48b6ccb2e020d52b62 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 22 Nov 2006 19:05:27 +0000 Subject: Fixes for cleaning up behind tests that fail on re-run if broker is not restarted. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@478287 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 6cc0d727d5..591d2d296f 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -147,7 +147,7 @@ class BasicTests(TestBase): Test basic ack/recover behaviour """ channel = self.channel - channel.queue_declare(queue="test-ack-queue") + channel.queue_declare(queue="test-ack-queue", exclusive=True) reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) queue = self.client.queue(reply.consumer_tag) @@ -191,7 +191,7 @@ class BasicTests(TestBase): Test requeing on recovery """ channel = self.channel - channel.queue_declare(queue="test-requeue") + channel.queue_declare(queue="test-requeue", exclusive=True) subscription = channel.basic_consume(queue="test-requeue", no_ack=False) queue = self.client.queue(subscription.consumer_tag) -- cgit v1.2.1 From 401c800bac2beccc20d6bb9665ab7eca68275d61 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Wed, 29 Nov 2006 19:42:38 +0000 Subject: Adding license files git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@480694 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/LICENSE.txt | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ qpid/python/NOTICE.txt | 20 +++++ 2 files changed, 223 insertions(+) create mode 100755 qpid/python/LICENSE.txt create mode 100644 qpid/python/NOTICE.txt (limited to 'qpid/python') diff --git a/qpid/python/LICENSE.txt b/qpid/python/LICENSE.txt new file mode 100755 index 0000000000..6b0b1270ff --- /dev/null +++ b/qpid/python/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/qpid/python/NOTICE.txt b/qpid/python/NOTICE.txt new file mode 100644 index 0000000000..32ccdb70c4 --- /dev/null +++ b/qpid/python/NOTICE.txt @@ -0,0 +1,20 @@ +========================================================================= +== NOTICE file corresponding to the section 4 d of == +== the Apache License, Version 2.0, == +== in this case for the Apache Qpid distribution. == +========================================================================= + +This product includes software developed by the Apache Software Foundation +(http://www.apache.org/). + +Please read the LICENSE.txt file present in the root directory of this +distribution. + + +Aside from contributions to the Apache Qpid project, this software also +includes (binary only): + + - None at this time + + + -- cgit v1.2.1 From 42017e18b50b8d96d3a4971b86528f89a804363c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 7 Dec 2006 14:48:05 +0000 Subject: Changed assertTrue(x) to assertEquals(True, x) as older version od pyunit seems not to have the former. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@483471 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 591d2d296f..9f26ee3728 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -229,8 +229,8 @@ class BasicTests(TestBase): self.assertEqual("Three", msg3b.content.body) self.assertEqual("Five", msg5b.content.body) - self.assertTrue(msg3b.redelivered) - self.assertTrue(msg5b.redelivered) + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) try: extra = queue2.get(timeout=1) -- cgit v1.2.1 From 9b7a8cc90e159cbcdbececf0f2c4ec81e2976203 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 20 Dec 2006 19:35:28 +0000 Subject: Simple setup.py script git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@489179 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/setup.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 qpid/python/setup.py (limited to 'qpid/python') diff --git a/qpid/python/setup.py b/qpid/python/setup.py new file mode 100644 index 0000000000..b418775c91 --- /dev/null +++ b/qpid/python/setup.py @@ -0,0 +1,7 @@ +#!/usr/bin/python +from distutils.core import setup + +setup(name="qpid", version="0.1", packages=["qpid"], scripts=["amqp-doc"], + url="http://incubator.apache.org/qpid", + license="Apache Software License", + description="Python language client implementation for Apache Qpid") -- cgit v1.2.1 From 3805e425741ae75c62b1dd703257e627f7677614 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 20 Dec 2006 19:37:45 +0000 Subject: Added license. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@489180 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/setup.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/setup.py b/qpid/python/setup.py index b418775c91..a49fa6ca51 100644 --- a/qpid/python/setup.py +++ b/qpid/python/setup.py @@ -1,4 +1,22 @@ -#!/usr/bin/python +#!/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. +# from distutils.core import setup setup(name="qpid", version="0.1", packages=["qpid"], scripts=["amqp-doc"], -- cgit v1.2.1 From 39e4feab60591fc68efaf404cbd3f9070826fb04 Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Fri, 22 Dec 2006 18:26:40 +0000 Subject: File tidy-up for AMQP version code: Removed old XSLT files; moved cluster.asl into spec directory; renamed spec files to better reflect version notation; updated java, c++ and python files to reference new names. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@489715 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/amqp-doc | 2 +- qpid/python/qpid/testlib.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/amqp-doc b/qpid/python/amqp-doc index 60a12c2838..0e7f9e862a 100755 --- a/qpid/python/amqp-doc +++ b/qpid/python/amqp-doc @@ -42,7 +42,7 @@ except GetoptError, e: die(str(e)) regexp = False -spec = "../specs/amqp-8.0.xml" +spec = "../specs/amqp.0-8.xml" for k, v in opts: if k == "-e" or k == "--regexp": regexp = True if k == "-s" or k == "--spec": spec = v diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index d35c38e4ad..39bad75b86 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -80,7 +80,7 @@ Options: def __init__(self): # Defaults self.setBroker("localhost") - self.spec = "../specs/amqp-8.0.xml" + self.spec = "../specs/amqp.0-8.xml" self.verbose = 1 self.ignore = [] -- cgit v1.2.1 From c2ccad027ce7ab1562ad7d8a977d4f8d2da6e35f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 4 Jan 2007 20:31:43 +0000 Subject: add support for 0-9 style usage of the domain attribute, and added content primitive type git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@492724 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 184ae157d1..0e3a477066 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -189,7 +189,8 @@ class Method(Metadata): "short": 0, "long": 0, "longlong": 0, - "timestamp": 0} + "timestamp": 0, + "content": None} def define_method(self, name): g = {Method.METHOD: self} @@ -233,9 +234,11 @@ def get_docs(nd): def load_fields(nd, l, domains): for f_nd in nd["field"]: try: - type = f_nd["@type"] + type = f_nd["@domain"] except KeyError: - type = domains[f_nd["@domain"]] + type = f_nd["@type"] + while domains.has_key(type) and domains[type] != type: + type = domains[type] l.add(Field(f_nd["@name"], f_nd.index(), type, get_docs(f_nd))) def load(specfile): -- cgit v1.2.1 From 786c2fe29eebf63387e62fe83134d5d693626c32 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 4 Jan 2007 20:45:57 +0000 Subject: use pythonized names to reference spec constants git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@492730 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 6f6c26559c..0b788e091b 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -59,7 +59,7 @@ class Connection: self.host = host self.port = port self.spec = spec - self.FRAME_END = self.spec.constants.byname["frame end"].id + self.FRAME_END = self.spec.constants.bypyname["frame_end"].id def connect(self): sock = socket.socket() @@ -78,14 +78,14 @@ class Connection: def write(self, frame): c = self.codec - c.encode_octet(self.spec.constants.byname[frame.payload.type].id) + c.encode_octet(self.spec.constants.bypyname[frame.payload.type].id) c.encode_short(frame.channel) frame.payload.encode(c) c.encode_octet(self.FRAME_END) def read(self): c = self.codec - type = self.spec.constants.byid[c.decode_octet()].name + type = pythonize(self.spec.constants.byid[c.decode_octet()].name) channel = c.decode_short() payload = Frame.DECODERS[type].decode(self.spec, c) end = c.decode_octet() @@ -96,14 +96,14 @@ class Connection: class Frame: - METHOD = "frame method" - HEADER = "frame header" - BODY = "frame body" - OOB_METHOD = "frame oob method" - OOB_HEADER = "frame oob header" - OOB_BODY = "frame oob body" - TRACE = "frame trace" - HEARTBEAT = "frame heartbeat" + METHOD = "frame_method" + HEADER = "frame_header" + BODY = "frame_body" + OOB_METHOD = "frame_oob_method" + OOB_HEADER = "frame_oob_header" + OOB_BODY = "frame_oob_body" + TRACE = "frame_trace" + HEARTBEAT = "frame_heartbeat" DECODERS = {} -- cgit v1.2.1 From d989614d675abf5d970e0aff420fb3db79ace159 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Wed, 10 Jan 2007 08:52:41 +0000 Subject: QPID-275 : (Patch supplied by Rob Godfrey) Fixes to allow broker to pass more of the Python tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@494769 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing.txt | 14 -------------- qpid/python/tests/exchange.py | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index a752bc53c0..a564fe0148 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,29 +1,15 @@ -tests.basic.BasicTests.test_cancel tests.basic.BasicTests.test_consume_exclusive tests.basic.BasicTests.test_consume_no_local -tests.basic.BasicTests.test_consume_queue_errors -tests.basic.BasicTests.test_consume_unique_consumers tests.basic.BasicTests.test_get tests.basic.BasicTests.test_qos_prefetch_size tests.basic.BasicTests.test_recover_requeue -tests.exchange.ExchangeTests tests.exchange.DefaultExchangeRuleTests.testDefaultExchange tests.exchange.HeadersExchangeTests.testMatchAll tests.exchange.HeadersExchangeTests.testMatchAny -tests.exchange.RecommendedTypesRuleTests.testDirect -tests.exchange.RecommendedTypesRuleTests.testFanout -tests.exchange.RecommendedTypesRuleTests.testHeaders tests.exchange.RecommendedTypesRuleTests.testTopic -tests.exchange.RequiredInstancesRuleTests.testAmqDirect -tests.exchange.RequiredInstancesRuleTests.testAmqFanOut tests.exchange.RequiredInstancesRuleTests.testAmqMatch tests.exchange.RequiredInstancesRuleTests.testAmqTopic tests.queue.QueueTests.test_declare_exclusive -tests.queue.QueueTests.test_declare_passive -tests.queue.QueueTests.test_delete_ifempty -tests.queue.QueueTests.test_delete_ifunused -tests.queue.QueueTests.test_delete_simple tests.queue.QueueTests.test_purge -tests.queue.QueueTests.test_bind tests.testlib.TestBaseTest.testMessageProperties tests.broker.BrokerTests.test_invalid_channel diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py index 121b47097d..56d6fa82e4 100644 --- a/qpid/python/tests/exchange.py +++ b/qpid/python/tests/exchange.py @@ -316,9 +316,9 @@ class MiscellaneousErrorsTests(TestBase): self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") try: self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") - self.fail("Expected 507 for redeclaration of exchange with different type.") + self.fail("Expected 530 for redeclaration of exchange with different type.") except Closed, e: - self.assertConnectionException(507, e.args[0]) + self.assertConnectionException(530, e.args[0]) #cleanup other = self.connect() c2 = other.channel(1) -- cgit v1.2.1 From 6fc4eec9fcf8d659b055d994119a1871d09c1d90 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 18 Jan 2007 08:14:36 +0000 Subject: Modified handling of reason for closing in peer to work around race between worker and reader where a connection.close is followed by the socket closing (this is a bit of a hack). Modified test for queue_purge to do cleanup on new connection. Separated testing of scenario where closed channel is used from that where an unopened channel is used. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@497342 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing.txt | 1 + qpid/python/qpid/peer.py | 2 ++ qpid/python/tests/broker.py | 4 ++-- qpid/python/tests/queue.py | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index a564fe0148..b6cfdc600e 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -13,3 +13,4 @@ tests.queue.QueueTests.test_declare_exclusive tests.queue.QueueTests.test_purge tests.testlib.TestBaseTest.testMessageProperties tests.broker.BrokerTests.test_invalid_channel +tests.broker.BrokerTests.test_closed_channel diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index acffeb2537..a265e45f43 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -129,6 +129,8 @@ class Channel: self.reason = None def close(self, reason): + if isinstance(reason, Message): + self.reason = reason if self.closed: return self.closed = True diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index f3888589a5..d9ac69c5e3 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -85,14 +85,14 @@ class BrokerTests(TestBase): self.assert_(msg.content.body == body) def test_invalid_channel(self): - other = self.connect() - channel = other.channel(200) + channel = self.client.channel(200) try: channel.queue_declare(exclusive=True) self.fail("Expected error on queue_declare for invalid channel") except Closed, e: self.assertConnectionException(504, e.args[0]) + def test_closed_channel(self): channel = self.client.channel(200) channel.channel_open() channel.channel_close() diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py index d5c13fa87a..9e32c44fd5 100644 --- a/qpid/python/tests/queue.py +++ b/qpid/python/tests/queue.py @@ -74,7 +74,8 @@ class QueueTests(TestBase): self.assertConnectionException(530, e.args[0]) #cleanup - channel = self.client.channel(4) + other = self.connect() + channel = other.channel(1) channel.channel_open() channel.exchange_delete(exchange="test-exchange") -- cgit v1.2.1 From c6010ef8f904c157a375189155dc0694d7d0be74 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 18 Jan 2007 11:29:31 +0000 Subject: Locked Channel::close() due to race. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@497404 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index a265e45f43..ef913d6196 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -127,16 +127,22 @@ class Channel: self.queue = None self.closed = False self.reason = None + #lock used to synchronise calls to close + self.lock = thread.allocate_lock() def close(self, reason): - if isinstance(reason, Message): + self.lock.acquire() + try: + if isinstance(reason, Message): + self.reason = reason + if self.closed: + return + self.closed = True self.reason = reason - if self.closed: - return - self.closed = True - self.reason = reason - self.incoming.close() - self.responses.close() + self.incoming.close() + self.responses.close() + finally: + self.lock.release() def dispatch(self, frame, work): payload = frame.payload -- cgit v1.2.1 From def168381240de48bf891234f16441a6d4c87851 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Fri, 19 Jan 2007 11:24:08 +0000 Subject: QPID-275 : Patch supplied by Rob Godfrey - Update list of expected failures in Python test git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@497779 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing.txt | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index b6cfdc600e..c7db632fac 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,16 +1,2 @@ -tests.basic.BasicTests.test_consume_exclusive -tests.basic.BasicTests.test_consume_no_local -tests.basic.BasicTests.test_get -tests.basic.BasicTests.test_qos_prefetch_size -tests.basic.BasicTests.test_recover_requeue -tests.exchange.DefaultExchangeRuleTests.testDefaultExchange -tests.exchange.HeadersExchangeTests.testMatchAll -tests.exchange.HeadersExchangeTests.testMatchAny tests.exchange.RecommendedTypesRuleTests.testTopic -tests.exchange.RequiredInstancesRuleTests.testAmqMatch tests.exchange.RequiredInstancesRuleTests.testAmqTopic -tests.queue.QueueTests.test_declare_exclusive -tests.queue.QueueTests.test_purge -tests.testlib.TestBaseTest.testMessageProperties -tests.broker.BrokerTests.test_invalid_channel -tests.broker.BrokerTests.test_closed_channel -- cgit v1.2.1 From 2b71226ef16355b63699be381b52eec3d750acc4 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 23 Jan 2007 10:51:53 +0000 Subject: Proper fix to race condition where connection-close is sent by server. Now handle socket close on worker thread also. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@498983 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index ef913d6196..7c6cf91dea 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -71,7 +71,7 @@ class Peer: try: frame = self.conn.read() except EOF, e: - self.close(e) + self.work.close() break ch = self.channel(frame.channel) ch.dispatch(frame, self.work) @@ -100,6 +100,8 @@ class Peer: try: while True: self.dispatch(self.work.get()) + except QueueClosed, e: + self.close(e) except: self.fatal() @@ -127,22 +129,14 @@ class Channel: self.queue = None self.closed = False self.reason = None - #lock used to synchronise calls to close - self.lock = thread.allocate_lock() def close(self, reason): - self.lock.acquire() - try: - if isinstance(reason, Message): - self.reason = reason - if self.closed: - return - self.closed = True - self.reason = reason - self.incoming.close() - self.responses.close() - finally: - self.lock.release() + if self.closed: + return + self.closed = True + self.reason = reason + self.incoming.close() + self.responses.close() def dispatch(self, frame, work): payload = frame.payload -- cgit v1.2.1 From c3b7f29e97cbcd5b44a0dc5efcfd439e9427eac8 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 21 Feb 2007 19:24:02 +0000 Subject: Fixed cases where open channel was re-opened. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@510160 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/queue.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py index 9e32c44fd5..60ac4c3dfb 100644 --- a/qpid/python/tests/queue.py +++ b/qpid/python/tests/queue.py @@ -155,8 +155,7 @@ class QueueTests(TestBase): """ Test basic queue deletion """ - channel = self.client.channel(1) - channel.channel_open() + channel = self.channel #straight-forward case: channel.queue_declare(queue="delete-me") @@ -187,8 +186,7 @@ class QueueTests(TestBase): """ Test that if_empty field of queue_delete is honoured """ - channel = self.client.channel(1) - channel.channel_open() + channel = self.channel #create a queue and add a message to it (use default binding): channel.queue_declare(queue="delete-me-2") @@ -227,8 +225,7 @@ class QueueTests(TestBase): """ Test that if_unused field of queue_delete is honoured """ - channel = self.client.channel(1) - channel.channel_open() + channel = self.channel #create a queue and register a consumer: channel.queue_declare(queue="delete-me-3") -- cgit v1.2.1 From c5b0c2b0eecd2e7335b44df7284f315cc7aae716 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 16 Mar 2007 20:26:11 +0000 Subject: Merged revisions 496593 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r496593 | rhs | 2007-01-16 00:28:25 -0500 (Tue, 16 Jan 2007) | 1 line 0-9 request/response framing for python ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519129 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/amqp-doc | 2 +- qpid/python/hello-world | 11 +++ qpid/python/qpid/client.py | 21 +++-- qpid/python/qpid/codec.py | 18 ++++ qpid/python/qpid/connection.py | 204 +++++++++++++++++++++++++++-------------- qpid/python/qpid/delegate.py | 20 ++-- qpid/python/qpid/message.py | 63 +++++-------- qpid/python/qpid/peer.py | 192 +++++++++++++++++++++++++++----------- qpid/python/qpid/spec.py | 74 ++++++++++----- qpid/python/server | 30 ++++++ qpid/python/tests/example.py | 4 +- 11 files changed, 424 insertions(+), 215 deletions(-) create mode 100755 qpid/python/hello-world create mode 100755 qpid/python/server (limited to 'qpid/python') diff --git a/qpid/python/amqp-doc b/qpid/python/amqp-doc index 0e7f9e862a..00226d63cb 100755 --- a/qpid/python/amqp-doc +++ b/qpid/python/amqp-doc @@ -42,7 +42,7 @@ except GetoptError, e: die(str(e)) regexp = False -spec = "../specs/amqp.0-8.xml" +spec = "../specs/amqp.0-9.xml" for k, v in opts: if k == "-e" or k == "--regexp": regexp = True if k == "-s" or k == "--spec": spec = v diff --git a/qpid/python/hello-world b/qpid/python/hello-world new file mode 100755 index 0000000000..b05873dff3 --- /dev/null +++ b/qpid/python/hello-world @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import qpid +from qpid.client import Client +from qpid.content import Content + +client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-9.xml")) +client.start({"LOGIN": "guest", "PASSWORD": "guest"}) +ch = client.channel(1) +ch.channel_open() +ch.message_transfer(destination="amq.direct", routing_key="asdf", + body="hello world") diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index b4a282f251..3083cd4933 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -25,7 +25,7 @@ interacting with the server. import threading from peer import Peer, Closed from delegate import Delegate -from connection import Connection, Frame +from connection import Connection, Frame, connect from spec import load from queue import Queue @@ -49,15 +49,13 @@ class Client: self.lock = threading.Lock() self.closed = False + self.reason = None self.started = threading.Event() - self.conn = Connection(self.host, self.port, self.spec) - self.peer = Peer(self.conn, ClientDelegate(self)) - def wait(self): self.started.wait() if self.closed: - raise EOFError() + raise Closed(self.reason) def queue(self, key): self.lock.acquire() @@ -76,7 +74,9 @@ class Client: self.response = response self.locale = locale - self.conn.connect() + self.conn = Connection(connect(self.host, self.port), self.spec) + self.peer = Peer(self.conn, ClientDelegate(self)) + self.conn.init() self.peer.start() self.wait() @@ -92,12 +92,12 @@ class ClientDelegate(Delegate): self.client = client def connection_start(self, ch, msg): - ch.connection_start_ok(mechanism=self.client.mechanism, - response=self.client.response, - locale=self.client.locale) + msg.start_ok(mechanism=self.client.mechanism, + response=self.client.response, + locale=self.client.locale) def connection_tune(self, ch, msg): - ch.connection_tune_ok(*msg.fields) + msg.tune_ok(*msg.frame.args) self.client.started.set() def basic_deliver(self, ch, msg): @@ -111,4 +111,5 @@ class ClientDelegate(Delegate): def close(self, reason): self.client.closed = True + self.client.reason = reason self.client.started.set() diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 69c7ca8afa..d8617c2937 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -185,6 +185,24 @@ class Codec: result[key] = value return result + def encode_timestamp(self, t): + # XXX + self.encode_longlong(t) + + def decode_timestamp(self): + # XXX + return self.decode_longlong() + + def encode_content(self, s): + # XXX + self.encode_octet(0) + self.encode_longstr(s) + + def decode_content(self): + # XXX + self.decode_octet() + return self.decode_longstr() + def test(type, value): if isinstance(value, (list, tuple)): values = value diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 0b788e091b..75fb134760 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -25,7 +25,7 @@ server, or even a proxy implementation. import socket, codec,logging from cStringIO import StringIO -from spec import load, pythonize +from spec import load from codec import EOF class SockIO: @@ -53,19 +53,27 @@ class SockIO: def flush(self): pass +def connect(host, port): + sock = socket.socket() + sock.connect((host, port)) + sock.setblocking(1) + return SockIO(sock) + +def listen(host, port, predicate = lambda: True): + sock = socket.socket() + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((host, port)) + sock.listen(5) + while predicate(): + s, a = sock.accept() + yield SockIO(s) + class Connection: - def __init__(self, host, port, spec): - self.host = host - self.port = port + def __init__(self, io, spec): + self.codec = codec.Codec(io) self.spec = spec - self.FRAME_END = self.spec.constants.bypyname["frame_end"].id - - def connect(self): - sock = socket.socket() - sock.connect((self.host, self.port)) - sock.setblocking(1) - self.codec = codec.Codec(SockIO(sock)) + self.FRAME_END = self.spec.constants.byname["frame_end"].id def flush(self): self.codec.flush() @@ -76,53 +84,55 @@ class Connection: self.codec.pack(Connection.INIT, "AMQP", 1, 1, self.spec.major, self.spec.minor) + def tini(self): + self.codec.unpack(Connection.INIT) + def write(self, frame): c = self.codec - c.encode_octet(self.spec.constants.bypyname[frame.payload.type].id) + c.encode_octet(self.spec.constants.byname[frame.type].id) c.encode_short(frame.channel) - frame.payload.encode(c) + body = StringIO() + enc = codec.Codec(body) + frame.encode(enc) + enc.flush() + c.encode_longstr(body.getvalue()) c.encode_octet(self.FRAME_END) def read(self): c = self.codec - type = pythonize(self.spec.constants.byid[c.decode_octet()].name) + type = self.spec.constants.byid[c.decode_octet()].name channel = c.decode_short() - payload = Frame.DECODERS[type].decode(self.spec, c) + body = c.decode_longstr() + dec = codec.Codec(StringIO(body)) + frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) + frame.channel = channel end = c.decode_octet() if end != self.FRAME_END: - raise "frame error: expected %r, got %r" % (self.FRAME_END, end) - frame = Frame(channel, payload) + garbage = "" + while end != self.FRAME_END: + garbage += chr(end) + end = c.decode_octet() + raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) return frame class Frame: - METHOD = "frame_method" - HEADER = "frame_header" - BODY = "frame_body" - OOB_METHOD = "frame_oob_method" - OOB_HEADER = "frame_oob_header" - OOB_BODY = "frame_oob_body" - TRACE = "frame_trace" - HEARTBEAT = "frame_heartbeat" - DECODERS = {} - def __init__(self, channel, payload): - self.channel = channel - self.payload = payload - - def __str__(self): - return "[%d] %s" % (self.channel, self.payload) - -class Payload: - class __metaclass__(type): def __new__(cls, name, bases, dict): - for req in ("encode", "decode", "type"): - if not dict.has_key(req): - raise TypeError("%s must define %s" % (name, req)) + for attr in ("encode", "decode", "type"): + if not dict.has_key(attr): + raise TypeError("%s must define %s" % (name, attr)) dict["decode"] = staticmethod(dict["decode"]) + if dict.has_key("__init__"): + __init__ = dict["__init__"] + def init(self, *args, **kwargs): + args = list(args) + self.init(args, kwargs) + __init__(self, *args, **kwargs) + dict["__init__"] = init t = type.__new__(cls, name, bases, dict) if t.type != None: Frame.DECODERS[t.type] = t @@ -130,50 +140,100 @@ class Payload: type = None + def init(self, args, kwargs): + self.channel = kwargs.pop("channel", 0) + def encode(self, enc): abstract - def decode(spec, dec): abstract + def decode(spec, dec, size): abstract -class Method(Payload): +class Method(Frame): - type = Frame.METHOD + type = "frame_method" - def __init__(self, method, *args): + def __init__(self, method, args): if len(args) != len(method.fields): - argspec = ["%s: %s" % (pythonize(f.name), f.type) + argspec = ["%s: %s" % (f.name, f.type) for f in method.fields] raise TypeError("%s.%s expecting (%s), got %s" % - (pythonize(method.klass.name), - pythonize(method.name), ", ".join(argspec), args)) + (method.klass.name, method.name, ", ".join(argspec), + args)) self.method = method + self.method_type = method self.args = args - def encode(self, enc): - buf = StringIO() - c = codec.Codec(buf) + def encode(self, c): c.encode_short(self.method.klass.id) c.encode_short(self.method.id) for field, arg in zip(self.method.fields, self.args): c.encode(field.type, arg) - c.flush() - enc.encode_longstr(buf.getvalue()) - def decode(spec, dec): - enc = dec.decode_longstr() - c = codec.Codec(StringIO(enc)) + def decode(spec, c, size): klass = spec.classes.byid[c.decode_short()] meth = klass.methods.byid[c.decode_short()] args = tuple([c.decode(f.type) for f in meth.fields]) - return Method(meth, *args) + return Method(meth, args) def __str__(self): - return "%s %s" % (self.method, ", ".join([str(a) for a in self.args])) + return "[%s] %s %s" % (self.channel, self.method, + ", ".join([str(a) for a in self.args])) + +class Request(Frame): -class Header(Payload): + type = "frame_request" - type = Frame.HEADER + def __init__(self, id, response_mark, method): + self.id = id + self.response_mark = response_mark + self.method = method + self.method_type = method.method_type + self.args = method.args + + def encode(self, enc): + enc.encode_longlong(self.id) + enc.encode_longlong(self.response_mark) + # reserved + enc.encode_long(0) + self.method.encode(enc) + + def decode(spec, dec, size): + id = dec.decode_longlong() + mark = dec.decode_longlong() + # reserved + dec.decode_long() + method = Method.decode(spec, dec, size - 20) + return Request(id, mark, method) + +class Response(Frame): + + type = "frame_response" + + def __init__(self, id, request_id, batch_offset, method): + self.id = id + self.request_id = request_id + self.batch_offset = batch_offset + self.method = method + self.method_type = method.method_type + self.args = method.args + + def encode(self, enc): + enc.encode_longlong(self.id) + enc.encode_longlong(self.request_id) + enc.encode_long(self.batch_offset) + self.method.encode(enc) - def __init__(self, klass, weight, size, **properties): + def decode(spec, dec, size): + id = dec.decode_longlong() + request_id = dec.decode_longlong() + batch_offset = dec.decode_long() + method = Method.decode(spec, dec, size - 20) + return Response(id, request_id, batch_offset, method) + +class Header(Frame): + + type = "frame_header" + + def __init__(self, klass, weight, size, properties): self.klass = klass self.weight = weight self.size = size @@ -188,9 +248,7 @@ class Header(Payload): def __delitem__(self, name): del self.properties[name] - def encode(self, enc): - buf = StringIO() - c = codec.Codec(buf) + def encode(self, c): c.encode_short(self.klass.id) c.encode_short(self.weight) c.encode_longlong(self.size) @@ -218,11 +276,8 @@ class Header(Payload): v = self.properties.get(f.name) if v != None: c.encode(f.type, v) - c.flush() - enc.encode_longstr(buf.getvalue()) - def decode(spec, dec): - c = codec.Codec(StringIO(dec.decode_longstr())) + def decode(spec, c, size): klass = spec.classes.byid[c.decode_short()] weight = c.decode_short() size = c.decode_longlong() @@ -247,24 +302,31 @@ class Header(Payload): # plain '' strings can be used as keywords so we need to # stringify the names. properties[str(f.name)] = c.decode(f.type) - return Header(klass, weight, size, **properties) + return Header(klass, weight, size, properties) def __str__(self): return "%s %s %s %s" % (self.klass, self.weight, self.size, self.properties) -class Body(Payload): +class Body(Frame): - type = Frame.BODY + type = "frame_body" def __init__(self, content): self.content = content def encode(self, enc): - enc.encode_longstr(self.content) + enc.write(self.content) - def decode(spec, dec): - return Body(dec.decode_longstr()) + def decode(spec, dec, size): + return Body(dec.read(size)) def __str__(self): return "Body(%r)" % self.content + +# TODO: +# OOB_METHOD = "frame_oob_method" +# OOB_HEADER = "frame_oob_header" +# OOB_BODY = "frame_oob_body" +# TRACE = "frame_trace" +# HEARTBEAT = "frame_heartbeat" diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py index 035bb3c476..90e5c1edc8 100644 --- a/qpid/python/qpid/delegate.py +++ b/qpid/python/qpid/delegate.py @@ -22,33 +22,25 @@ Delegate implementation intended for use with the peer module. """ import threading, inspect -from spec import pythonize +from connection import Method, Request, Response class Delegate: def __init__(self): self.handlers = {} self.invokers = {} - # initialize all the mixins - self.invoke_all("init") - def invoke_all(self, meth, *args, **kwargs): - for cls in inspect.getmro(self.__class__): - if hasattr(cls, meth): - getattr(cls, meth)(self, *args, **kwargs) - - def dispatch(self, channel, message): - method = message.method + def __call__(self, channel, frame): + method = frame.method try: handler = self.handlers[method] except KeyError: - name = "%s_%s" % (pythonize(method.klass.name), - pythonize(method.name)) + name = "%s_%s" % (method.klass.name, method.name) handler = getattr(self, name) self.handlers[method] = handler - return handler(channel, message) + return handler(channel, frame) def close(self, reason): - self.invoke_all("close", reason) + print "Connection closed: %s" % reason diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index 914b878147..29c8654937 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -16,22 +16,19 @@ # specific language governing permissions and limitations # under the License. # +from connection import Method, Request from sets import Set class Message: - COMMON_FIELDS = Set(("content", "method", "fields")) - - def __init__(self, method, fields, content = None): - self.method = method - self.fields = fields + def __init__(self, channel, frame, content = None): + self.channel = channel + self.frame = frame + self.method = frame.method_type self.content = content def __len__(self): - l = len(self.fields) - if self.method.content: - l += 1 - return len(self.fields) + return len(self.frame.args) def _idx(self, idx): if idx < 0: idx += len(self) @@ -40,45 +37,29 @@ class Message: return idx def __getitem__(self, idx): - idx = self._idx(idx) - if idx == len(self.fields): - return self.content - else: - return self.fields[idx] - - def __setitem__(self, idx, value): - idx = self._idx(idx) - if idx == len(self.fields): - self.content = value - else: - self.fields[idx] = value + return self.frame.args[idx] - def _slot(self, attr): - if attr in Message.COMMON_FIELDS: - env = self.__dict__ - key = attr + def __getattr__(self, attr): + fields = self.method.fields.byname + if fields.has_key(attr): + f = fields[attr] + result = self[self.method.fields.index(f)] else: - env = self.fields - try: - field = self.method.fields.bypyname[attr] - key = self.method.fields.index(field) - except KeyError: + for r in self.method.responses: + if attr == r.name: + result = lambda *args, **kwargs: \ + self.channel.respond(Method(r, r.arguments(*args, **kwargs)), + self.frame) + break + else: raise AttributeError(attr) - return env, key - - def __getattr__(self, attr): - env, key = self._slot(attr) - return env[key] - - def __setattr__(self, attr, value): - env, key = self._slot(attr) - env[attr] = value + return result STR = "%s %s content = %s" REPR = STR.replace("%s", "%r") def __str__(self): - return Message.STR % (self.method, self.fields, self.content) + return Message.STR % (self.method, self.frame.args, self.content) def __repr__(self): - return Message.REPR % (self.method, self.fields, self.content) + return Message.REPR % (self.method, self.frame.args, self.content) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 7c6cf91dea..66d325994b 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -24,13 +24,30 @@ sorts incoming frames to their intended channels, and dispatches incoming method frames to a delegate. """ -import thread, traceback, socket, sys, logging -from connection import Frame, EOF, Method, Header, Body +import thread, threading, traceback, socket, sys, logging +from connection import EOF, Method, Header, Body, Request, Response from message import Message from queue import Queue, Closed as QueueClosed from content import Content from cStringIO import StringIO +class Sequence: + + def __init__(self, start, step = 1): + # we should keep start for wrap around + self._next = start + self.step = step + self.lock = thread.allocate_lock() + + def next(self): + self.lock.acquire() + try: + result = self._next + self._next += self.step + return result + finally: + self.lock.release() + class Peer: def __init__(self, conn, delegate): @@ -39,8 +56,6 @@ class Peer: self.outgoing = Queue(0) self.work = Queue(0) self.channels = {} - self.Channel = type("Channel%s" % conn.spec.klass.__name__, - (Channel, conn.spec.klass), {}) self.lock = thread.allocate_lock() def channel(self, id): @@ -49,7 +64,7 @@ class Peer: try: ch = self.channels[id] except KeyError: - ch = self.Channel(id, self.outgoing) + ch = Channel(id, self.outgoing, self.conn.spec) self.channels[id] = ch finally: self.lock.release() @@ -64,7 +79,7 @@ class Peer: """Call when an unexpected exception occurs that will kill a thread.""" if message: print >> sys.stderr, message self.close("Fatal error: %s\n%s" % (message or "", traceback.format_exc())) - + def reader(self): try: while True: @@ -74,7 +89,7 @@ class Peer: self.work.close() break ch = self.channel(frame.channel) - ch.dispatch(frame, self.work) + ch.receive(frame, self.work) except: self.fatal() @@ -99,37 +114,70 @@ class Peer: def worker(self): try: while True: - self.dispatch(self.work.get()) - except QueueClosed, e: - self.close(e) + queue = self.work.get() + frame = queue.get() + channel = self.channel(frame.channel) + if frame.method_type.content: + content = read_content(queue) + else: + content = None + + self.delegate(channel, Message(channel, frame, content)) except: self.fatal() - def dispatch(self, queue): - frame = queue.get() - channel = self.channel(frame.channel) - payload = frame.payload - if payload.method.content: - content = read_content(queue) +class Requester: + + def __init__(self, writer): + self.write = writer + self.sequence = Sequence(1) + self.mark = 0 + # request_id -> listener + self.outstanding = {} + + def request(self, method, listener, content = None): + frame = Request(self.sequence.next(), self.mark, method) + self.outstanding[frame.id] = listener + self.write(frame, content) + + def receive(self, channel, frame): + listener = self.outstanding.pop(frame.id) + listener(channel, frame) + +class Responder: + + def __init__(self, writer): + self.write = writer + self.sequence = Sequence(1) + + def respond(self, method, request): + if isinstance(request, Method): + self.write(method) else: - content = None - # Let the caller deal with exceptions thrown here. - message = Message(payload.method, payload.args, content) - self.delegate.dispatch(channel, message) + # XXX: batching + frame = Response(self.sequence.next(), request.id, 0, method) + self.write(frame) class Closed(Exception): pass class Channel: - def __init__(self, id, outgoing): + def __init__(self, id, outgoing, spec): self.id = id self.outgoing = outgoing + self.spec = spec self.incoming = Queue(0) self.responses = Queue(0) self.queue = None self.closed = False self.reason = None + self.requester = Requester(self.write) + self.responder = Responder(self.write) + + # XXX: better switch + self.reliable = False + def close(self, reason): if self.closed: return @@ -138,43 +186,87 @@ class Channel: self.incoming.close() self.responses.close() - def dispatch(self, frame, work): - payload = frame.payload - if isinstance(payload, Method): - if payload.method.response: + def write(self, frame, content = None): + if self.closed: + raise Closed(self.reason) + frame.channel = self.id + self.outgoing.put(frame) + if (isinstance(frame, (Method, Request)) + and content == None + and frame.method_type.content): + content = Content() + if content != None: + self.write_content(frame.method_type.klass, content) + + def write_content(self, klass, content): + size = content.size() + header = Header(klass, content.weight(), size, content.properties) + self.write(header) + for child in content.children: + self.write_content(klass, child) + # should split up if content.body exceeds max frame size + if size > 0: + self.write(Body(content.body)) + + def receive(self, frame, work): + if isinstance(frame, Method): + if frame.method.response: self.queue = self.responses else: self.queue = self.incoming work.put(self.incoming) + elif isinstance(frame, Request): + self.queue = self.incoming + work.put(self.incoming) + elif isinstance(frame, Response): + self.requester.receive(self, frame) + return self.queue.put(frame) - def invoke(self, method, args, content = None): - if self.closed: - raise Closed(self.reason) - frame = Frame(self.id, Method(method, *args)) - self.outgoing.put(frame) + def queue_response(self, channel, frame): + channel.responses.put(frame.method) + + def request(self, method, listener, content = None): + self.requester.request(method, listener, content) + + def respond(self, method, request): + self.responder.respond(method, request) - if method.content: - if content == None: - content = Content() - self.write_content(method.klass, content, self.outgoing) + def invoke(self, type, args, kwargs): + content = kwargs.pop("content", None) + frame = Method(type, type.arguments(*args, **kwargs)) + if self.reliable: + self.request(frame, self.queue_response, content) + try: + resp = self.responses.get() + return Message(self, resp) + except QueueClosed, e: + if self.closed: + raise Closed(self.reason) + else: + raise e + else: + return self.invoke_method(frame, content) + + def invoke_method(self, frame, content = None): + self.write(frame, content) try: # here we depend on all nowait fields being named nowait - f = method.fields.byname["nowait"] - nowait = args[method.fields.index(f)] + f = frame.method.fields.byname["nowait"] + nowait = frame.args[frame.method.fields.index(f)] except KeyError: nowait = False try: - if not nowait and method.responses: - resp = self.responses.get().payload + if not nowait and frame.method.responses: + resp = self.responses.get() if resp.method.content: content = read_content(self.responses) else: content = None - if resp.method in method.responses: - return Message(resp.method, resp.args, content) + if resp.method in frame.method.responses: + return Message(self, resp, content) else: raise ValueError(resp) except QueueClosed, e: @@ -183,19 +275,15 @@ class Channel: else: raise e - def write_content(self, klass, content, queue): - size = content.size() - header = Frame(self.id, Header(klass, content.weight(), size, **content.properties)) - queue.put(header) - for child in content.children: - self.write_content(klass, child, queue) - # should split up if content.body exceeds max frame size - if size > 0: - queue.put(Frame(self.id, Body(content.body))) + def __getattr__(self, name): + type = self.spec.method(name) + if type == None: raise AttributeError(name) + method = lambda *args, **kwargs: self.invoke(type, args, kwargs) + self.__dict__[name] = method + return method def read_content(queue): - frame = queue.get() - header = frame.payload + header = queue.get() children = [] for i in range(header.weight): children.append(read_content(queue)) @@ -204,7 +292,7 @@ def read_content(queue): buf = StringIO() while read < size: body = queue.get() - content = body.payload.content + content = body.content buf.write(content) read += len(content) return Content(buf.getvalue(), children, header.properties.copy()) diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 0e3a477066..3e8b9e37bd 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -38,21 +38,16 @@ class SpecContainer: self.byname = {} self.byid = {} self.indexes = {} - self.bypyname = {} def add(self, item): if self.byname.has_key(item.name): raise ValueError("duplicate name: %s" % item) if self.byid.has_key(item.id): raise ValueError("duplicate id: %s" % item) - pyname = pythonize(item.name) - if self.bypyname.has_key(pyname): - raise ValueError("duplicate pyname: %s" % item) self.indexes[item] = len(self.items) self.items.append(item) self.byname[item.name] = item self.byid[item.id] = item - self.bypyname[pyname] = item def index(self, item): try: @@ -91,11 +86,23 @@ class Spec(Metadata): self.file = file self.constants = SpecContainer() self.classes = SpecContainer() + # methods indexed by classname_methname + self.methods = {} def post_load(self): self.module = self.define_module("amqp%s%s" % (self.major, self.minor)) self.klass = self.define_class("Amqp%s%s" % (self.major, self.minor)) + def method(self, name): + if not self.methods.has_key(name): + for cls in self.classes: + clen = len(cls.name) + if name.startswith(cls.name) and name[clen] == "_": + end = name[clen + 1:] + if cls.methods.byname.has_key(end): + self.methods[name] = cls.methods.byname[end] + return self.methods.get(name) + def parse_method(self, name): parts = re.split(r"\s*\.\s*", name) if len(parts) != 2: @@ -107,17 +114,16 @@ class Spec(Metadata): module = new.module(name, doc) module.__file__ = self.file for c in self.classes: - classname = pythonize(c.name) - cls = c.define_class(classname) + cls = c.define_class(c.name) cls.__module__ = module.__name__ - setattr(module, classname, cls) + setattr(module, c.name, cls) return module def define_class(self, name): methods = {} for c in self.classes: for m in c.methods: - meth = pythonize(m.klass.name + "_" + m.name) + meth = m.klass.name + "_" + m.name methods[meth] = m.define_method(meth) return type(name, (), methods) @@ -150,8 +156,7 @@ class Class(Metadata): def define_class(self, name): methods = {} for m in self.methods: - meth = pythonize(m.name) - methods[meth] = m.define_method(meth) + methods[m.name] = m.define_method(m.name) return type(name, (), methods) class Method(Metadata): @@ -172,11 +177,35 @@ class Method(Metadata): self.docs = docs self.response = False + def arguments(self, *args, **kwargs): + nargs = len(args) + len(kwargs) + maxargs = len(self.fields) + if nargs > maxargs: + self._type_error("takes at most %s arguments (%s) given", maxargs, nargs) + result = [] + for f in self.fields: + idx = self.fields.index(f) + if idx < len(args): + result.append(args[idx]) + elif kwargs.has_key(f.name): + result.append(kwargs.pop(f.name)) + else: + result.append(Method.DEFAULTS[f.type]) + for key, value in kwargs.items(): + if self.fields.byname.has_key(key): + self._type_error("got multiple values for keyword argument '%s'", key) + else: + self._type_error("got an unexpected keyword argument '%s'", key) + return tuple(result) + + def _type_error(self, msg, *args): + raise TypeError("%s %s" % (self.name, msg % args)) + def docstring(self): s = "\n\n".join([fill(d, 2) for d in [self.description] + self.docs]) for f in self.fields: if f.docs: - s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, pythonize(f.name))] + + s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, f.name)] + [fill(d, 4) for d in f.docs[1:]]) return s @@ -195,16 +224,13 @@ class Method(Metadata): def define_method(self, name): g = {Method.METHOD: self} l = {} - args = [(pythonize(f.name), Method.DEFAULTS[f.type]) for f in self.fields] + args = [(f.name, Method.DEFAULTS[f.type]) for f in self.fields] + methargs = args[:] if self.content: args += [("content", None)] code = "def %s(self, %s):\n" % \ (name, ", ".join(["%s = %r" % a for a in args])) code += " %r\n" % self.docstring() - if self.content: - methargs = args[:-1] - else: - methargs = args argnames = ", ".join([a[0] for a in methargs]) code += " return self.invoke(%s" % Method.METHOD if argnames: @@ -239,7 +265,7 @@ def load_fields(nd, l, domains): type = f_nd["@type"] while domains.has_key(type) and domains[type] != type: type = domains[type] - l.add(Field(f_nd["@name"], f_nd.index(), type, get_docs(f_nd))) + l.add(Field(pythonize(f_nd["@name"]), f_nd.index(), type, get_docs(f_nd))) def load(specfile): doc = xmlutil.parse(specfile) @@ -248,8 +274,8 @@ def load(specfile): # constants for nd in root["constant"]: - const = Constant(spec, nd["@name"], int(nd["@value"]), nd.get("@class"), - get_docs(nd)) + const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), + nd.get("@class"), get_docs(nd)) spec.constants.add(const) # domains are typedefs @@ -259,14 +285,14 @@ def load(specfile): # classes for c_nd in root["class"]: - klass = Class(spec, c_nd["@name"], int(c_nd["@index"]), c_nd["@handler"], - get_docs(c_nd)) + klass = Class(spec, pythonize(c_nd["@name"]), int(c_nd["@index"]), + c_nd["@handler"], get_docs(c_nd)) load_fields(c_nd, klass.fields, domains) for m_nd in c_nd["method"]: - meth = Method(klass, m_nd["@name"], + meth = Method(klass, pythonize(m_nd["@name"]), int(m_nd["@index"]), m_nd.get_bool("@content", False), - [nd["@name"] for nd in m_nd["response"]], + [pythonize(nd["@name"]) for nd in m_nd["response"]], m_nd.get_bool("@synchronous", False), m_nd.text, get_docs(m_nd)) diff --git a/qpid/python/server b/qpid/python/server new file mode 100755 index 0000000000..4204bc8515 --- /dev/null +++ b/qpid/python/server @@ -0,0 +1,30 @@ +#!/usr/bin/env python +from qpid import spec +from qpid.connection import Connection, listen +from qpid.delegate import Delegate +from qpid.peer import Peer + +class Server(Delegate): + + def connection_open(self, ch, msg): + msg.open_ok() + + def channel_open(self, ch, msg): + print "channel %s open" % ch.id + msg.open_ok() + + def message_transfer(self, ch, msg): + print msg.body + msg.ok() + + +spec = spec.load("../specs/amqp.0-9.xml") + +for io in listen("0.0.0.0", 5672): + c = Connection(io, spec) + p = Peer(c, Server()) + c.tini() + p.start() + ch = p.channel(0) + ch.connection_start() + ch.connection_tune() diff --git a/qpid/python/tests/example.py b/qpid/python/tests/example.py index bc84f002e0..a1949ccb9f 100644 --- a/qpid/python/tests/example.py +++ b/qpid/python/tests/example.py @@ -58,7 +58,7 @@ class ExampleTest (TestBase): # Here we use ordinal arguments. self.exchange_declare(channel, 0, "test", "direct") - + # Here we use keyword arguments. self.queue_declare(channel, queue="test-queue") channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") @@ -85,7 +85,7 @@ class ExampleTest (TestBase): # argument in case the server hangs. By default queue.get() will wait # until a message arrives or the connection to the server dies. msg = queue.get(timeout=10) - + # And check that we got the right response with assertEqual self.assertEqual(body, msg.content.body) -- cgit v1.2.1 From c7b2f8e9fab60f6bb8975d0de0a28c2316a66d6a Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 16 Mar 2007 20:29:10 +0000 Subject: Merged revisions 497277 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r497277 | rhs | 2007-01-17 20:27:33 -0500 (Wed, 17 Jan 2007) | 1 line updated hello-world smoke test ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519132 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index b05873dff3..fd712d324b 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -7,5 +7,8 @@ client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-9.xml")) client.start({"LOGIN": "guest", "PASSWORD": "guest"}) ch = client.channel(1) ch.channel_open() -ch.message_transfer(destination="amq.direct", routing_key="asdf", +ch.queue_declare(queue="test") +ch.queue_bind(exchange="amq.direct", queue="test", routing_key="test") +ch.message_consume(queue="test", destination="test") +ch.message_transfer(destination="amq.direct", routing_key="test", body="hello world") -- cgit v1.2.1 From 06fdb27fc4db7f2115d2fe1ca6290f07c0fb08c7 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 16 Mar 2007 22:31:08 +0000 Subject: * python/tests_0-8/*: copy of python/tests to preserve 0-8 version of tests. python/tests will be merged from the 0-9 branch. * python/qpid/testlib.py: select tests or tests_0-8 based on spec version. * python/tests_0-8/basic.py (BasicTests): replace get-ok with get_ok, Python client now replaces - with _ for all names from the spec. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519166 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 16 +- qpid/python/tests_0-8/__init__.py | 20 ++ qpid/python/tests_0-8/basic.py | 394 ++++++++++++++++++++++++++++++++++++++ qpid/python/tests_0-8/broker.py | 104 ++++++++++ qpid/python/tests_0-8/example.py | 94 +++++++++ qpid/python/tests_0-8/exchange.py | 327 +++++++++++++++++++++++++++++++ qpid/python/tests_0-8/queue.py | 255 ++++++++++++++++++++++++ qpid/python/tests_0-8/testlib.py | 66 +++++++ qpid/python/tests_0-8/tx.py | 209 ++++++++++++++++++++ 9 files changed, 1481 insertions(+), 4 deletions(-) create mode 100644 qpid/python/tests_0-8/__init__.py create mode 100644 qpid/python/tests_0-8/basic.py create mode 100644 qpid/python/tests_0-8/broker.py create mode 100644 qpid/python/tests_0-8/example.py create mode 100644 qpid/python/tests_0-8/exchange.py create mode 100644 qpid/python/tests_0-8/queue.py create mode 100644 qpid/python/tests_0-8/testlib.py create mode 100644 qpid/python/tests_0-8/tx.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 39bad75b86..b4534ab362 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -80,7 +80,7 @@ Options: def __init__(self): # Defaults self.setBroker("localhost") - self.spec = "../specs/amqp.0-8.xml" + self.specfile = "../specs/amqp.0-8.xml" self.verbose = 1 self.ignore = [] @@ -96,14 +96,22 @@ Options: self._die(str(e)) for opt, value in opts: if opt in ("-?", "-h", "--help"): self._die() - if opt in ("-s", "--spec"): self.spec = value + if opt in ("-s", "--spec"): self.specfile = value if opt in ("-b", "--broker"): self.setBroker(value) if opt in ("-v", "--verbose"): self.verbose = 2 if opt in ("-d", "--debug"): logging.basicConfig(level=logging.DEBUG) if opt in ("-i", "--ignore"): self.ignore.append(value) if opt in ("-I", "--ignore-file"): self.ignoreFile(value) - if len(self.tests) == 0: self.tests=findmodules("tests") + self.spec = qpid.spec.load(self.specfile) + if len(self.tests) == 0: + # NB: not a typo but a quirk of AMQP history. + # AMQP 0-8 identifies itself as 8-0. + if self.spec.major==8 and self.spec.minor==0: + testdir="tests_0-8" + else: + testdir="tests" + self.tests=findmodules(testdir) def testSuite(self): class IgnoringTestSuite(unittest.TestSuite): @@ -135,7 +143,7 @@ Options: spec = spec or self.spec user = user or self.user password = password or self.password - client = qpid.client.Client(host, port, qpid.spec.load(spec)) + client = qpid.client.Client(host, port, spec) client.start({"LOGIN": user, "PASSWORD": password}) return client diff --git a/qpid/python/tests_0-8/__init__.py b/qpid/python/tests_0-8/__init__.py new file mode 100644 index 0000000000..9a09d2d04f --- /dev/null +++ b/qpid/python/tests_0-8/__init__.py @@ -0,0 +1,20 @@ +# Do not delete - marks this directory as a python package. + +# +# 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. +# diff --git a/qpid/python/tests_0-8/basic.py b/qpid/python/tests_0-8/basic.py new file mode 100644 index 0000000000..140576540a --- /dev/null +++ b/qpid/python/tests_0-8/basic.py @@ -0,0 +1,394 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BasicTests(TestBase): + """Tests for 'methods' on the amqp basic 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a") + channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local")) + channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local")) + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True) + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.basic_consume(consumer_tag="first", queue="test-queue-2") + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.basic_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.basic_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + try: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") + channel.basic_publish(routing_key="test-queue-4", content=Content("One")) + + #cancel should stop messages being delivered + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.content.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_cancel(consumer_tag="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True) + + reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) + queue = self.client.queue(reply.consumer_tag) + + channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True) + + subscription = channel.basic_consume(queue="test-requeue", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + channel.basic_publish(routing_key="test-requeue", content=Content("One")) + channel.basic_publish(routing_key="test-requeue", content=Content("Two")) + channel.basic_publish(routing_key="test-requeue", content=Content("Three")) + channel.basic_publish(routing_key="test-requeue", content=Content("Four")) + channel.basic_publish(routing_key="test-requeue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_cancel(consumer_tag=subscription.consumer_tag) + subscription2 = channel.basic_consume(queue="test-requeue") + queue2 = self.client.queue(subscription2.consumer_tag) + + channel.basic_recover(requeue=True) + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.content.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.content.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 5: + channel.basic_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.basic_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.content.body) + + def test_get(self): + """ + Test basic_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + #use basic_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + for i in range(11, 21): + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + if(i == 13): + channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) + if(i in [15, 17, 19]): + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + #recover(requeue=True) + channel.basic_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + channel.basic_recover(requeue=True) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") diff --git a/qpid/python/tests_0-8/broker.py b/qpid/python/tests_0-8/broker.py new file mode 100644 index 0000000000..d9ac69c5e3 --- /dev/null +++ b/qpid/python/tests_0-8/broker.py @@ -0,0 +1,104 @@ +# +# 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. +# +from qpid.client import Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BrokerTests(TestBase): + """Tests for basic Broker functionality""" + + def test_amqp_basic_13(self): + """ + First, this test tries to receive a message with a no-ack + consumer. Second, this test tries to explicitely receive and + acknowledge a message with an acknowledging consumer. + """ + ch = self.channel + self.queue_declare(ch, queue = "myqueue") + + # No ack consumer + ctag = ch.basic_consume(queue = "myqueue", no_ack = True).consumer_tag + body = "test no-ack" + ch.basic_publish(routing_key = "myqueue", content = Content(body)) + msg = self.client.queue(ctag).get(timeout = 5) + self.assert_(msg.content.body == body) + + # Acknowleding consumer + self.queue_declare(ch, queue = "otherqueue") + ctag = ch.basic_consume(queue = "otherqueue", no_ack = False).consumer_tag + body = "test ack" + ch.basic_publish(routing_key = "otherqueue", content = Content(body)) + msg = self.client.queue(ctag).get(timeout = 5) + ch.basic_ack(delivery_tag = msg.delivery_tag) + self.assert_(msg.content.body == body) + + def test_basic_delivery_immediate(self): + """ + Test basic message delivery where consume is issued before publish + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + reply = channel.basic_consume(queue="test-queue", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + + body = "Immediate Delivery" + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content(body), immediate=True) + msg = queue.get(timeout=5) + self.assert_(msg.content.body == body) + + # TODO: Ensure we fail if immediate=True and there's no consumer. + + + def test_basic_delivery_queued(self): + """ + Test basic message delivery where publish is issued before consume + (i.e. requires queueing of the message) + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + body = "Queued Delivery" + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content(body)) + reply = channel.basic_consume(queue="test-queue", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + msg = queue.get(timeout=5) + self.assert_(msg.content.body == body) + + def test_invalid_channel(self): + channel = self.client.channel(200) + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for invalid channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) + + def test_closed_channel(self): + channel = self.client.channel(200) + channel.channel_open() + channel.channel_close() + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for closed channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) + diff --git a/qpid/python/tests_0-8/example.py b/qpid/python/tests_0-8/example.py new file mode 100644 index 0000000000..a1949ccb9f --- /dev/null +++ b/qpid/python/tests_0-8/example.py @@ -0,0 +1,94 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExampleTest (TestBase): + """ + An example Qpid test, illustrating the unittest frameowkr and the + python Qpid client. The test class must inherit TestCase. The + test code uses the Qpid client to interact with a qpid broker and + verify it behaves as expected. + """ + + def test_example(self): + """ + An example test. Note that test functions must start with 'test_' + to be recognized by the test framework. + """ + + # By inheriting TestBase, self.client is automatically connected + # and self.channel is automatically opened as channel(1) + # Other channel methods mimic the protocol. + channel = self.channel + + # Now we can send regular commands. If you want to see what the method + # arguments mean or what other commands are available, you can use the + # python builtin help() method. For example: + #help(chan) + #help(chan.exchange_declare) + + # If you want browse the available protocol methods without being + # connected to a live server you can use the amqp-doc utility: + # + # Usage amqp-doc [] [ ... ] + # + # Options: + # -e, --regexp use regex instead of glob when matching + + # Now that we know what commands are available we can use them to + # interact with the server. + + # Here we use ordinal arguments. + self.exchange_declare(channel, 0, "test", "direct") + + # Here we use keyword arguments. + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") + + # Call Channel.basic_consume to register as a consumer. + # All the protocol methods return a message object. The message object + # has fields corresponding to the reply method fields, plus a content + # field that is filled if the reply includes content. In this case the + # interesting field is the consumer_tag. + reply = channel.basic_consume(queue="test-queue") + + # We can use the Client.queue(...) method to access the queue + # corresponding to our consumer_tag. + queue = self.client.queue(reply.consumer_tag) + + # Now lets publish a message and see if our consumer gets it. To do + # this we need to import the Content class. + body = "Hello World!" + channel.basic_publish(exchange="test", + routing_key="key", + content=Content(body)) + + # Now we'll wait for the message to arrive. We can use the timeout + # argument in case the server hangs. By default queue.get() will wait + # until a message arrives or the connection to the server dies. + msg = queue.get(timeout=10) + + # And check that we got the right response with assertEqual + self.assertEqual(body, msg.content.body) + + # Now acknowledge the message. + channel.basic_ack(msg.delivery_tag, True) + diff --git a/qpid/python/tests_0-8/exchange.py b/qpid/python/tests_0-8/exchange.py new file mode 100644 index 0000000000..56d6fa82e4 --- /dev/null +++ b/qpid/python/tests_0-8/exchange.py @@ -0,0 +1,327 @@ +# +# 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. +# + +""" +Tests for exchange behaviour. + +Test classes ending in 'RuleTests' are derived from rules in amqp.xml. +""" + +import Queue, logging +from qpid.testlib import TestBase +from qpid.content import Content +from qpid.client import Closed + + +class StandardExchangeVerifier: + """Verifies standard exchange behavior. + + Used as base class for classes that test standard exchanges.""" + + def verifyDirectExchange(self, ex): + """Verify that ex behaves like a direct exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") + self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") + try: + self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") + self.fail("Expected Empty exception") + except Queue.Empty: None # Expected + + def verifyFanOutExchange(self, ex): + """Verify that ex behaves like a fanout exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex) + self.queue_declare(queue="p") + self.channel.queue_bind(queue="p", exchange=ex) + for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) + + def verifyTopicExchange(self, ex): + """Verify that ex behaves like a topic exchange""" + self.queue_declare(queue="a") + self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + q = self.consume("a") + self.assertPublishGet(q, ex, "a.b.x") + self.assertPublishGet(q, ex, "a.x.b.x") + self.assertPublishGet(q, ex, "a.x.x.b.x") + # Shouldn't match + self.channel.basic_publish(exchange=ex, routing_key="a.b") + self.channel.basic_publish(exchange=ex, routing_key="a.b.x.y") + self.channel.basic_publish(exchange=ex, routing_key="x.a.b.x") + self.channel.basic_publish(exchange=ex, routing_key="a.b") + self.assert_(q.empty()) + + def verifyHeadersExchange(self, ex): + """Verify that ex is a headers exchange""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) + q = self.consume("q") + headers = {"name":"fred", "age":3} + self.assertPublishGet(q, exchange=ex, properties={'headers':headers}) + self.channel.basic_publish(exchange=ex) # No headers, won't deliver + self.assertEmpty(q); + + +class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server SHOULD implement these standard exchange types: topic, headers. + + Client attempts to declare an exchange with each of these standard types. + """ + + def testDirect(self): + """Declare and test a direct exchange""" + self.exchange_declare(0, exchange="d", type="direct") + self.verifyDirectExchange("d") + + def testFanout(self): + """Declare and test a fanout exchange""" + self.exchange_declare(0, exchange="f", type="fanout") + self.verifyFanOutExchange("f") + + def testTopic(self): + """Declare and test a topic exchange""" + self.exchange_declare(0, exchange="t", type="topic") + self.verifyTopicExchange("t") + + def testHeaders(self): + """Declare and test a headers exchange""" + self.exchange_declare(0, exchange="h", type="headers") + self.verifyHeadersExchange("h") + + +class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST, in each virtual host, pre-declare an exchange instance + for each standard exchange type that it implements, where the name of the + exchange instance is amq. followed by the exchange type name. + + Client creates a temporary queue and attempts to bind to each required + exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if + those types are defined). + """ + def testAmqDirect(self): self.verifyDirectExchange("amq.direct") + + def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") + + def testAmqTopic(self): self.verifyTopicExchange("amq.topic") + + def testAmqMatch(self): self.verifyHeadersExchange("amq.match") + +class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST predeclare a direct exchange to act as the default exchange + for content Publish methods and for default queue bindings. + + Client checks that the default exchange is active by specifying a queue + binding with no exchange name, and publishing a message with a suitable + routing key but without specifying the exchange name, then ensuring that + the message arrives in the queue correctly. + """ + def testDefaultExchange(self): + # Test automatic binding by queue name. + self.queue_declare(queue="d") + self.assertPublishConsume(queue="d", routing_key="d") + # Test explicit bind to default queue + self.verifyDirectExchange("") + + +# TODO aconway 2006-09-27: Fill in empty tests: + +class DefaultAccessRuleTests(TestBase): + """ + The server MUST NOT allow clients to access the default exchange except + by specifying an empty exchange name in the Queue.Bind and content Publish + methods. + """ + +class ExtensionsRuleTests(TestBase): + """ + The server MAY implement other exchange types as wanted. + """ + + +class DeclareMethodMinimumRuleTests(TestBase): + """ + The server SHOULD support a minimum of 16 exchanges per virtual host and + ideally, impose no limit except as defined by available resources. + + The client creates as many exchanges as it can until the server reports + an error; the number of exchanges successfuly created must be at least + sixteen. + """ + + +class DeclareMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access to + the realm in which the exchange exists or will be created, or "passive" + access if the if-exists flag is set. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeclareMethodExchangeFieldReservedRuleTests(TestBase): + """ + Exchange names starting with "amq." are reserved for predeclared and + standardised exchanges. The client MUST NOT attempt to create an exchange + starting with "amq.". + + + """ + + +class DeclareMethodTypeFieldTypedRuleTests(TestBase): + """ + Exchanges cannot be redeclared with different types. The client MUST not + attempt to redeclare an existing exchange with a different type than used + in the original Exchange.Declare method. + + + """ + + +class DeclareMethodTypeFieldSupportRuleTests(TestBase): + """ + The client MUST NOT attempt to create an exchange with a type that the + server does not support. + + + """ + + +class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): + """ + If set, and the exchange does not already exist, the server MUST raise a + channel exception with reply code 404 (not found). + """ + def test(self): + try: + self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) + self.fail("Expected 404 for passive declaration of unknown exchange.") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + +class DeclareMethodDurableFieldSupportRuleTests(TestBase): + """ + The server MUST support both durable and transient exchanges. + + + """ + + +class DeclareMethodDurableFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the durable field if the exchange already exists. + + + """ + + +class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the auto-delete field if the exchange already + exists. + + + """ + + +class DeleteMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access + rights to the exchange's access realm. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeleteMethodExchangeFieldExistsRuleTests(TestBase): + """ + The client MUST NOT attempt to delete an exchange that does not exist. + """ + + +class HeadersExchangeTests(TestBase): + """ + Tests for headers exchange functionality. + """ + def setUp(self): + TestBase.setUp(self) + self.queue_declare(queue="q") + self.q = self.consume("q") + + def myAssertPublishGet(self, headers): + self.assertPublishGet(self.q, exchange="amq.match", properties={'headers':headers}) + + def myBasicPublish(self, headers): + self.channel.basic_publish(exchange="amq.match", content=Content("foobar", properties={'headers':headers})) + + def testMatchAll(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) + + # None of these should match + self.myBasicPublish({}) + self.myBasicPublish({"name":"barney"}) + self.myBasicPublish({"name":10}) + self.myBasicPublish({"name":"fred", "age":2}) + self.assertEmpty(self.q) + + def testMatchAny(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred"}) + self.myAssertPublishGet({"name":"fred", "ignoreme":10}) + self.myAssertPublishGet({"ignoreme":10, "age":3}) + + # Wont match + self.myBasicPublish({}) + self.myBasicPublish({"irrelevant":0}) + self.assertEmpty(self.q) + + +class MiscellaneousErrorsTests(TestBase): + """ + Test some miscellaneous error conditions + """ + def testTypeNotKnown(self): + try: + self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") + self.fail("Expected 503 for declaration of unknown exchange type.") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def testDifferentDeclaredType(self): + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + try: + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + self.fail("Expected 530 for redeclaration of exchange with different type.") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + #cleanup + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + c2.exchange_delete(exchange="test_different_declared_type_exchange") + diff --git a/qpid/python/tests_0-8/queue.py b/qpid/python/tests_0-8/queue.py new file mode 100644 index 0000000000..60ac4c3dfb --- /dev/null +++ b/qpid/python/tests_0-8/queue.py @@ -0,0 +1,255 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueueTests(TestBase): + """Tests for 'methods' on the amqp queue 'class'""" + + def test_purge(self): + """ + Test that the purge method removes messages from the queue + """ + channel = self.channel + #setup, declare a queue and add some messages to it: + channel.exchange_declare(exchange="test-exchange", type="direct") + channel.queue_declare(queue="test-queue", exclusive=True) + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("one")) + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("two")) + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("three")) + + #check that the queue now reports 3 messages: + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(3, reply.message_count) + + #now do the purge, then test that three messages are purged and the count drops to 0 + reply = channel.queue_purge(queue="test-queue"); + self.assertEqual(3, reply.message_count) + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(0, reply.message_count) + + #send a further message and consume it, ensuring that the other messages are really gone + channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("four")) + reply = channel.basic_consume(queue="test-queue", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + msg = queue.get(timeout=1) + self.assertEqual("four", msg.content.body) + + #check error conditions (use new channels): + channel = self.client.channel(2) + channel.channel_open() + try: + #queue specified but doesn't exist: + channel.queue_purge(queue="invalid-queue") + self.fail("Expected failure when purging non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(3) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.queue_purge() + self.fail("Expected failure when purging unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.exchange_delete(exchange="test-exchange") + + def test_declare_exclusive(self): + """ + Test that the exclusive field is honoured in queue.declare + """ + # TestBase.setUp has already opened channel(1) + c1 = self.channel + # Here we open a second separate connection: + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + + #declare an exclusive queue: + c1.queue_declare(queue="exclusive-queue", exclusive="True") + try: + #other connection should not be allowed to declare this: + c2.queue_declare(queue="exclusive-queue", exclusive="True") + self.fail("Expected second exclusive queue_declare to raise a channel exception") + except Closed, e: + self.assertChannelException(405, e.args[0]) + + + def test_declare_passive(self): + """ + Test that the passive field is honoured in queue.declare + """ + channel = self.channel + #declare an exclusive queue: + channel.queue_declare(queue="passive-queue-1", exclusive="True") + channel.queue_declare(queue="passive-queue-1", passive="True") + try: + #other connection should not be allowed to declare this: + channel.queue_declare(queue="passive-queue-2", passive="True") + self.fail("Expected passive declaration of non-existant queue to raise a channel exception") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_bind(self): + """ + Test various permutations of the queue.bind method + """ + channel = self.channel + channel.queue_declare(queue="queue-1", exclusive="True") + + #straightforward case, both exchange & queue exist so no errors expected: + channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + + #bind the default queue for the channel (i.e. last one declared): + channel.queue_bind(exchange="amq.direct", routing_key="key2") + + #use the queue name where neither routing key nor queue are specified: + channel.queue_bind(exchange="amq.direct") + + #try and bind to non-existant exchange + try: + channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + self.fail("Expected bind to non-existant exchange to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #need to reopen a channel: + channel = self.client.channel(2) + channel.channel_open() + + #try and bind non-existant queue: + try: + channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + self.fail("Expected bind of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_delete_simple(self): + """ + Test basic queue deletion + """ + channel = self.channel + + #straight-forward case: + channel.queue_declare(queue="delete-me") + channel.basic_publish(routing_key="delete-me", content=Content("a")) + channel.basic_publish(routing_key="delete-me", content=Content("b")) + channel.basic_publish(routing_key="delete-me", content=Content("c")) + reply = channel.queue_delete(queue="delete-me") + self.assertEqual(3, reply.message_count) + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="delete-me", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #check attempted deletion of non-existant queue is handled correctly: + channel = self.client.channel(2) + channel.channel_open() + try: + channel.queue_delete(queue="i-dont-exist", if_empty="True") + self.fail("Expected delete of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + + def test_delete_ifempty(self): + """ + Test that if_empty field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and add a message to it (use default binding): + channel.queue_declare(queue="delete-me-2") + channel.queue_declare(queue="delete-me-2", passive="True") + channel.basic_publish(routing_key="delete-me-2", content=Content("message")) + + #try to delete, but only if empty: + try: + channel.queue_delete(queue="delete-me-2", if_empty="True") + self.fail("Expected delete if_empty to fail for non-empty queue") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + #need new channel now: + channel = self.client.channel(2) + channel.channel_open() + + #empty queue: + reply = channel.basic_consume(queue="delete-me-2", no_ack=True) + queue = self.client.queue(reply.consumer_tag) + msg = queue.get(timeout=1) + self.assertEqual("message", msg.content.body) + channel.basic_cancel(consumer_tag=reply.consumer_tag) + + #retry deletion on empty queue: + channel.queue_delete(queue="delete-me-2", if_empty="True") + + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-2", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_delete_ifunused(self): + """ + Test that if_unused field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and register a consumer: + channel.queue_declare(queue="delete-me-3") + channel.queue_declare(queue="delete-me-3", passive="True") + reply = channel.basic_consume(queue="delete-me-3", no_ack=True) + + #need new channel now: + channel2 = self.client.channel(2) + channel2.channel_open() + #try to delete, but only if empty: + try: + channel2.queue_delete(queue="delete-me-3", if_unused="True") + self.fail("Expected delete if_unused to fail for queue with existing consumer") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + + channel.basic_cancel(consumer_tag=reply.consumer_tag) + channel.queue_delete(queue="delete-me-3", if_unused="True") + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-3", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + diff --git a/qpid/python/tests_0-8/testlib.py b/qpid/python/tests_0-8/testlib.py new file mode 100644 index 0000000000..cab07cc4ac --- /dev/null +++ b/qpid/python/tests_0-8/testlib.py @@ -0,0 +1,66 @@ +# +# 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. +# + +# +# Tests for the testlib itself. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from Queue import Empty + +import sys +from traceback import * + +def mytrace(frame, event, arg): + print_stack(frame); + print "====" + return mytrace + +class TestBaseTest(TestBase): + """Verify TestBase functions work as expected""" + + def testAssertEmptyPass(self): + """Test assert empty works""" + self.queue_declare(queue="empty") + q = self.consume("empty") + self.assertEmpty(q) + try: + q.get(timeout=1) + self.fail("Queue is not empty.") + except Empty: None # Ignore + + def testAssertEmptyFail(self): + self.queue_declare(queue="full") + q = self.consume("full") + self.channel.basic_publish(routing_key="full") + try: + self.assertEmpty(q); + self.fail("assertEmpty did not assert on non-empty queue") + except AssertionError: None # Ignore + + def testMessageProperties(self): + """Verify properties are passed with message""" + props={"headers":{"x":1, "y":2}} + self.queue_declare(queue="q") + q = self.consume("q") + self.assertPublishGet(q, routing_key="q", properties=props) + + + diff --git a/qpid/python/tests_0-8/tx.py b/qpid/python/tests_0-8/tx.py new file mode 100644 index 0000000000..054fb8d8b7 --- /dev/null +++ b/qpid/python/tests_0-8/tx.py @@ -0,0 +1,209 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class TxTests(TestBase): + """ + Tests for 'methods' on the amqp tx 'class' + """ + + def test_commit(self): + """ + Test that commited publishes are delivered and commited acks are not re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c") + channel.tx_commit() + + #check results + for i in range(1, 5): + msg = queue_c.get(timeout=1) + self.assertEqual("TxMessage %d" % i, msg.content.body) + + msg = queue_b.get(timeout=1) + self.assertEqual("TxMessage 6", msg.content.body) + + msg = queue_a.get(timeout=1) + self.assertEqual("TxMessage 7", msg.content.body) + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.basic_ack(delivery_tag=0, multiple=True) + channel.tx_commit() + + def test_auto_rollback(self): + """ + Test that a channel closed with an open transaction is effectively rolled back + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.basic_ack(delivery_tag=0, multiple=True) + channel.tx_commit() + + def test_rollback(self): + """ + Test that rolled back publishes are not delivered and rolled back acks are re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.basic_ack(delivery_tag=0, multiple=True) + channel.tx_commit() + + def perform_txn_work(self, channel, name_a, name_b, name_c): + """ + Utility method that does some setup and some work under a transaction. Used for testing both + commit and rollback + """ + #setup: + channel.queue_declare(queue=name_a, exclusive=True) + channel.queue_declare(queue=name_b, exclusive=True) + channel.queue_declare(queue=name_c, exclusive=True) + + key = "my_key_" + name_b + topic = "my_topic_" + name_c + + channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) + channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) + + for i in range(1, 5): + channel.basic_publish(routing_key=name_a, content=Content("Message %d" % i)) + + channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("Message 6")) + channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("Message 7")) + + channel.tx_select() + + #consume and ack messages + sub_a = channel.basic_consume(queue=name_a, no_ack=False) + queue_a = self.client.queue(sub_a.consumer_tag) + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + sub_b = channel.basic_consume(queue=name_b, no_ack=False) + queue_b = self.client.queue(sub_b.consumer_tag) + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + channel.basic_ack(delivery_tag=msg.delivery_tag) + + sub_c = channel.basic_consume(queue=name_c, no_ack=False) + queue_c = self.client.queue(sub_c.consumer_tag) + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + channel.basic_ack(delivery_tag=msg.delivery_tag) + + #publish messages + for i in range(1, 5): + channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("TxMessage %d" % i)) + + channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("TxMessage 6")) + channel.basic_publish(routing_key=name_a, content=Content("TxMessage 7")) + + return queue_a, queue_b, queue_c + + def test_commit_overlapping_acks(self): + """ + Test that logically 'overlapping' acks do not cause errors on commit + """ + channel = self.channel + channel.queue_declare(queue="commit-overlapping", exclusive=True) + for i in range(1, 10): + channel.basic_publish(routing_key="commit-overlapping", content=Content("Message %d" % i)) + + + channel.tx_select() + + sub = channel.basic_consume(queue="commit-overlapping", no_ack=False) + queue = self.client.queue(sub.consumer_tag) + for i in range(1, 10): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + if i in [3, 6, 10]: + channel.basic_ack(delivery_tag=msg.delivery_tag) + + channel.tx_commit() + + #check all have been acked: + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None -- cgit v1.2.1 From 14f2a8d6398140ae72af9429c75179dde539c972 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 16 Mar 2007 22:54:11 +0000 Subject: Merged revisions 500305 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r500305 | gsim | 2007-01-26 13:51:21 -0500 (Fri, 26 Jan 2007) | 3 lines Updates to use message class in place of basic. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519171 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing.txt | 17 +++++++ qpid/python/qpid/codec.py | 17 +++---- qpid/python/qpid/connection.py | 3 ++ qpid/python/qpid/peer.py | 2 + qpid/python/qpid/testlib.py | 19 ++++---- qpid/python/tests/broker.py | 45 ++++++++++-------- qpid/python/tests/example.py | 14 +++--- qpid/python/tests/exchange.py | 16 +++---- qpid/python/tests/queue.py | 32 ++++++------- qpid/python/tests/testlib.py | 2 +- qpid/python/tests/tx.py | 104 ++++++++++++++++------------------------- 11 files changed, 140 insertions(+), 131 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index c7db632fac..f85e46beca 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,2 +1,19 @@ +tests.basic.BasicTests.test_ack +tests.basic.BasicTests.test_cancel +tests.basic.BasicTests.test_consume_exclusive +tests.basic.BasicTests.test_consume_no_local +tests.basic.BasicTests.test_consume_queue_errors +tests.basic.BasicTests.test_consume_unique_consumers +tests.basic.BasicTests.test_get +tests.basic.BasicTests.test_qos_prefetch_count +tests.basic.BasicTests.test_qos_prefetch_size +tests.basic.BasicTests.test_recover_requeue tests.exchange.RecommendedTypesRuleTests.testTopic tests.exchange.RequiredInstancesRuleTests.testAmqTopic +tests.queue.QueueTests.test_declare_exclusive +tests.queue.QueueTests.test_declare_passive +tests.queue.QueueTests.test_delete_ifempty +tests.queue.QueueTests.test_delete_ifunused +tests.queue.QueueTests.test_delete_simple +tests.queue.QueueTests.test_purge +tests.testlib.TestBaseTest.testMessageProperties diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index d8617c2937..205405894a 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -157,14 +157,15 @@ class Codec: def encode_table(self, tbl): enc = StringIO() codec = Codec(enc) - for key, value in tbl.items(): - codec.encode_shortstr(key) - if isinstance(value, basestring): - codec.write("S") - codec.encode_longstr(value) - else: - codec.write("I") - codec.encode_long(value) + if tbl: + for key, value in tbl.items(): + codec.encode_shortstr(key) + if isinstance(value, basestring): + codec.write("S") + codec.encode_longstr(value) + else: + codec.write("I") + codec.encode_long(value) s = enc.getvalue() self.encode_long(len(s)) self.write(s) diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 75fb134760..fb1e0927f0 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -204,6 +204,9 @@ class Request(Frame): method = Method.decode(spec, dec, size - 20) return Request(id, mark, method) + def __str__(self): + return "[%s] Request(%s) %s" % (self.channel, self.id, self.method) + class Response(Frame): type = "frame_response" diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 66d325994b..8d5029004e 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -237,6 +237,8 @@ class Channel: frame = Method(type, type.arguments(*args, **kwargs)) if self.reliable: self.request(frame, self.queue_response, content) + if not frame.method.responses: + return None try: resp = self.responses.get() return Message(self, resp) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index b4534ab362..ecae499451 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -80,7 +80,7 @@ Options: def __init__(self): # Defaults self.setBroker("localhost") - self.specfile = "../specs/amqp.0-8.xml" + self.spec = "../specs/amqp.0-8.xml" self.verbose = 1 self.ignore = [] @@ -203,8 +203,11 @@ class TestBase(unittest.TestCase): def consume(self, queueName): """Consume from named queue returns the Queue object.""" - reply = self.channel.basic_consume(queue=queueName, no_ack=True) - return self.client.queue(reply.consumer_tag) + if not "uniqueTag" in dir(self): self.uniqueTag = 1 + else: self.uniqueTag += 1 + consumer_tag = "tag" + str(self.uniqueTag) + self.channel.message_consume(queue=queueName, destination=consumer_tag, no_ack=True) + return self.client.queue(consumer_tag) def assertEmpty(self, queue): """Assert that the queue is empty""" @@ -218,12 +221,12 @@ class TestBase(unittest.TestCase): Publish to exchange and assert queue.get() returns the same message. """ body = self.uniqueString() - self.channel.basic_publish(exchange=exchange, - content=Content(body, properties=properties), - routing_key=routing_key) + self.channel.message_transfer(destination=exchange, + body=body, application_headers=properties, + routing_key=routing_key) msg = queue.get(timeout=1) - self.assertEqual(body, msg.content.body) - if (properties): self.assertEqual(properties, msg.content.properties) + self.assertEqual(body, msg.body) + if (properties): self.assertEqual(properties, msg.application_headers) def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): """ diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py index d9ac69c5e3..ebb3a525f5 100644 --- a/qpid/python/tests/broker.py +++ b/qpid/python/tests/broker.py @@ -24,7 +24,7 @@ from qpid.testlib import testrunner, TestBase class BrokerTests(TestBase): """Tests for basic Broker functionality""" - def test_amqp_basic_13(self): + def test_ack_and_no_ack(self): """ First, this test tries to receive a message with a no-ack consumer. Second, this test tries to explicitely receive and @@ -34,41 +34,44 @@ class BrokerTests(TestBase): self.queue_declare(ch, queue = "myqueue") # No ack consumer - ctag = ch.basic_consume(queue = "myqueue", no_ack = True).consumer_tag + ctag = "tag1" + ch.message_consume(queue = "myqueue", destination = ctag, no_ack = True) body = "test no-ack" - ch.basic_publish(routing_key = "myqueue", content = Content(body)) + ch.message_transfer(routing_key = "myqueue", body = body) msg = self.client.queue(ctag).get(timeout = 5) - self.assert_(msg.content.body == body) + self.assert_(msg.body == body) - # Acknowleding consumer + # Acknowledging consumer self.queue_declare(ch, queue = "otherqueue") - ctag = ch.basic_consume(queue = "otherqueue", no_ack = False).consumer_tag + ctag = "tag2" + ch.message_consume(queue = "otherqueue", destination = ctag, no_ack = False) body = "test ack" - ch.basic_publish(routing_key = "otherqueue", content = Content(body)) + ch.message_transfer(routing_key = "otherqueue", body = body) msg = self.client.queue(ctag).get(timeout = 5) - ch.basic_ack(delivery_tag = msg.delivery_tag) - self.assert_(msg.content.body == body) + msg.ok() + self.assert_(msg.body == body) - def test_basic_delivery_immediate(self): + def test_simple_delivery_immediate(self): """ - Test basic message delivery where consume is issued before publish + Test simple message delivery where consume is issued before publish """ channel = self.channel self.exchange_declare(channel, exchange="test-exchange", type="direct") self.queue_declare(channel, queue="test-queue") channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - reply = channel.basic_consume(queue="test-queue", no_ack=True) - queue = self.client.queue(reply.consumer_tag) + consumer_tag = "tag1" + channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + queue = self.client.queue(consumer_tag) body = "Immediate Delivery" - channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content(body), immediate=True) + channel.message_transfer(destination="test-exchange", routing_key="key", body=body, immediate=True) msg = queue.get(timeout=5) - self.assert_(msg.content.body == body) + self.assert_(msg.body == body) # TODO: Ensure we fail if immediate=True and there's no consumer. - def test_basic_delivery_queued(self): + def test_simple_delivery_queued(self): """ Test basic message delivery where publish is issued before consume (i.e. requires queueing of the message) @@ -78,11 +81,13 @@ class BrokerTests(TestBase): self.queue_declare(channel, queue="test-queue") channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") body = "Queued Delivery" - channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content(body)) - reply = channel.basic_consume(queue="test-queue", no_ack=True) - queue = self.client.queue(reply.consumer_tag) + channel.message_transfer(destination="test-exchange", routing_key="key", body=body) + + consumer_tag = "tag1" + channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + queue = self.client.queue(consumer_tag) msg = queue.get(timeout=5) - self.assert_(msg.content.body == body) + self.assert_(msg.body == body) def test_invalid_channel(self): channel = self.client.channel(200) diff --git a/qpid/python/tests/example.py b/qpid/python/tests/example.py index a1949ccb9f..7ab4cc7d0a 100644 --- a/qpid/python/tests/example.py +++ b/qpid/python/tests/example.py @@ -68,18 +68,18 @@ class ExampleTest (TestBase): # has fields corresponding to the reply method fields, plus a content # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. - reply = channel.basic_consume(queue="test-queue") + channel.message_consume(queue="test-queue", destination="consumer_tag") # We can use the Client.queue(...) method to access the queue # corresponding to our consumer_tag. - queue = self.client.queue(reply.consumer_tag) + queue = self.client.queue("consumer_tag") # Now lets publish a message and see if our consumer gets it. To do # this we need to import the Content class. body = "Hello World!" - channel.basic_publish(exchange="test", - routing_key="key", - content=Content(body)) + channel.message_transfer(destination="test", + routing_key="key", + body = body) # Now we'll wait for the message to arrive. We can use the timeout # argument in case the server hangs. By default queue.get() will wait @@ -87,8 +87,8 @@ class ExampleTest (TestBase): msg = queue.get(timeout=10) # And check that we got the right response with assertEqual - self.assertEqual(body, msg.content.body) + self.assertEqual(body, msg.body) # Now acknowledge the message. - channel.basic_ack(msg.delivery_tag, True) + msg.ok() diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py index 56d6fa82e4..54c462de24 100644 --- a/qpid/python/tests/exchange.py +++ b/qpid/python/tests/exchange.py @@ -61,10 +61,10 @@ class StandardExchangeVerifier: self.assertPublishGet(q, ex, "a.x.b.x") self.assertPublishGet(q, ex, "a.x.x.b.x") # Shouldn't match - self.channel.basic_publish(exchange=ex, routing_key="a.b") - self.channel.basic_publish(exchange=ex, routing_key="a.b.x.y") - self.channel.basic_publish(exchange=ex, routing_key="x.a.b.x") - self.channel.basic_publish(exchange=ex, routing_key="a.b") + self.channel.message_transfer(destination=ex, routing_key="a.b") + self.channel.message_transfer(destination=ex, routing_key="a.b.x.y") + self.channel.message_transfer(destination=ex, routing_key="x.a.b.x") + self.channel.message_transfer(destination=ex, routing_key="a.b") self.assert_(q.empty()) def verifyHeadersExchange(self, ex): @@ -73,8 +73,8 @@ class StandardExchangeVerifier: self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) q = self.consume("q") headers = {"name":"fred", "age":3} - self.assertPublishGet(q, exchange=ex, properties={'headers':headers}) - self.channel.basic_publish(exchange=ex) # No headers, won't deliver + self.assertPublishGet(q, exchange=ex, properties=headers) + self.channel.message_transfer(destination=ex, body="") # No headers, won't deliver self.assertEmpty(q); @@ -272,10 +272,10 @@ class HeadersExchangeTests(TestBase): self.q = self.consume("q") def myAssertPublishGet(self, headers): - self.assertPublishGet(self.q, exchange="amq.match", properties={'headers':headers}) + self.assertPublishGet(self.q, exchange="amq.match", properties=headers) def myBasicPublish(self, headers): - self.channel.basic_publish(exchange="amq.match", content=Content("foobar", properties={'headers':headers})) + self.channel.message_transfer(destination="amq.match", body="foobar", application_headers=headers) def testMatchAll(self): self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py index 60ac4c3dfb..d85f04c4c2 100644 --- a/qpid/python/tests/queue.py +++ b/qpid/python/tests/queue.py @@ -33,9 +33,9 @@ class QueueTests(TestBase): channel.exchange_declare(exchange="test-exchange", type="direct") channel.queue_declare(queue="test-queue", exclusive=True) channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("one")) - channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("two")) - channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("three")) + channel.message_transfer(destination="test-exchange", routing_key="key", body="one") + channel.message_transfer(destination="test-exchange", routing_key="key", body="two") + channel.message_transfer(destination="test-exchange", routing_key="key", body="three") #check that the queue now reports 3 messages: reply = channel.queue_declare(queue="test-queue") @@ -48,9 +48,9 @@ class QueueTests(TestBase): self.assertEqual(0, reply.message_count) #send a further message and consume it, ensuring that the other messages are really gone - channel.basic_publish(exchange="test-exchange", routing_key="key", content=Content("four")) - reply = channel.basic_consume(queue="test-queue", no_ack=True) - queue = self.client.queue(reply.consumer_tag) + channel.message_transfer(destination="test-exchange", routing_key="key", content=Content("four")) + channel.message_consume(queue="test-queue", destination="tag", no_ack=True) + queue = self.client.queue("tag") msg = queue.get(timeout=1) self.assertEqual("four", msg.content.body) @@ -153,15 +153,15 @@ class QueueTests(TestBase): def test_delete_simple(self): """ - Test basic queue deletion + Test core queue deletion behaviour """ channel = self.channel #straight-forward case: channel.queue_declare(queue="delete-me") - channel.basic_publish(routing_key="delete-me", content=Content("a")) - channel.basic_publish(routing_key="delete-me", content=Content("b")) - channel.basic_publish(routing_key="delete-me", content=Content("c")) + channel.message_transfer(routing_key="delete-me", body="a") + channel.message_transfer(routing_key="delete-me", body="b") + channel.message_transfer(routing_key="delete-me", body="c") reply = channel.queue_delete(queue="delete-me") self.assertEqual(3, reply.message_count) #check that it has gone be declaring passively @@ -191,7 +191,7 @@ class QueueTests(TestBase): #create a queue and add a message to it (use default binding): channel.queue_declare(queue="delete-me-2") channel.queue_declare(queue="delete-me-2", passive="True") - channel.basic_publish(routing_key="delete-me-2", content=Content("message")) + channel.message_transfer(routing_key="delete-me-2", body="message") #try to delete, but only if empty: try: @@ -205,11 +205,11 @@ class QueueTests(TestBase): channel.channel_open() #empty queue: - reply = channel.basic_consume(queue="delete-me-2", no_ack=True) - queue = self.client.queue(reply.consumer_tag) + channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True) + queue = self.client.queue("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.content.body) - channel.basic_cancel(consumer_tag=reply.consumer_tag) + channel.message_cancel(destination="consumer_tag") #retry deletion on empty queue: channel.queue_delete(queue="delete-me-2", if_empty="True") @@ -230,7 +230,7 @@ class QueueTests(TestBase): #create a queue and register a consumer: channel.queue_declare(queue="delete-me-3") channel.queue_declare(queue="delete-me-3", passive="True") - reply = channel.basic_consume(queue="delete-me-3", no_ack=True) + channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True) #need new channel now: channel2 = self.client.channel(2) @@ -243,7 +243,7 @@ class QueueTests(TestBase): self.assertChannelException(406, e.args[0]) - channel.basic_cancel(consumer_tag=reply.consumer_tag) + channel.message_cancel(destination="consumer_tag") channel.queue_delete(queue="delete-me-3", if_unused="True") #check that it has gone by declaring passively: try: diff --git a/qpid/python/tests/testlib.py b/qpid/python/tests/testlib.py index cab07cc4ac..d9391fc529 100644 --- a/qpid/python/tests/testlib.py +++ b/qpid/python/tests/testlib.py @@ -49,7 +49,7 @@ class TestBaseTest(TestBase): def testAssertEmptyFail(self): self.queue_declare(queue="full") q = self.consume("full") - self.channel.basic_publish(routing_key="full") + self.channel.message_transfer(routing_key="full", body="") try: self.assertEmpty(q); self.fail("assertEmpty did not assert on non-empty queue") diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py index 054fb8d8b7..55a5eaeade 100644 --- a/qpid/python/tests/tx.py +++ b/qpid/python/tests/tx.py @@ -37,22 +37,24 @@ class TxTests(TestBase): #check results for i in range(1, 5): msg = queue_c.get(timeout=1) - self.assertEqual("TxMessage %d" % i, msg.content.body) + self.assertEqual("TxMessage %d" % i, msg.body) + msg.ok() msg = queue_b.get(timeout=1) - self.assertEqual("TxMessage 6", msg.content.body) + self.assertEqual("TxMessage 6", msg.body) + msg.ok() msg = queue_a.get(timeout=1) - self.assertEqual("TxMessage 7", msg.content.body) + self.assertEqual("TxMessage 7", msg.body) + msg.ok() for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup - channel.basic_ack(delivery_tag=0, multiple=True) channel.tx_commit() def test_auto_rollback(self): @@ -65,7 +67,7 @@ class TxTests(TestBase): for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None channel.tx_rollback() @@ -73,22 +75,24 @@ class TxTests(TestBase): #check results for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) + self.assertEqual("Message 6", msg.body) + msg.ok() msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) + self.assertEqual("Message 7", msg.body) + msg.ok() for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup - channel.basic_ack(delivery_tag=0, multiple=True) channel.tx_commit() def test_rollback(self): @@ -101,7 +105,7 @@ class TxTests(TestBase): for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None channel.tx_rollback() @@ -109,22 +113,24 @@ class TxTests(TestBase): #check results for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) + self.assertEqual("Message 6", msg.body) + msg.ok() msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) + self.assertEqual("Message 7", msg.body) + msg.ok() for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup - channel.basic_ack(delivery_tag=0, multiple=True) channel.tx_commit() def perform_txn_work(self, channel, name_a, name_b, name_c): @@ -144,66 +150,38 @@ class TxTests(TestBase): channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) for i in range(1, 5): - channel.basic_publish(routing_key=name_a, content=Content("Message %d" % i)) + channel.message_transfer(routing_key=name_a, body="Message %d" % i) - channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("Message 6")) - channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("Message 7")) + channel.message_transfer(routing_key=key, destination="amq.direct", body="Message 6") + channel.message_transfer(routing_key=topic, destination="amq.topic", body="Message 7") channel.tx_select() #consume and ack messages - sub_a = channel.basic_consume(queue=name_a, no_ack=False) - queue_a = self.client.queue(sub_a.consumer_tag) + channel.message_consume(queue=name_a, destination="sub_a", no_ack=False) + queue_a = self.client.queue("sub_a") for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() - sub_b = channel.basic_consume(queue=name_b, no_ack=False) - queue_b = self.client.queue(sub_b.consumer_tag) + channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) + queue_b = self.client.queue("sub_b") msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - channel.basic_ack(delivery_tag=msg.delivery_tag) + self.assertEqual("Message 6", msg.body) + msg.ok() - sub_c = channel.basic_consume(queue=name_c, no_ack=False) - queue_c = self.client.queue(sub_c.consumer_tag) + sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False) + queue_c = self.client.queue("sub_c") msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - channel.basic_ack(delivery_tag=msg.delivery_tag) + self.assertEqual("Message 7", msg.body) + msg.ok() #publish messages for i in range(1, 5): - channel.basic_publish(routing_key=topic, exchange="amq.topic", content=Content("TxMessage %d" % i)) + channel.message_transfer(routing_key=topic, destination="amq.topic", body="TxMessage %d" % i) - channel.basic_publish(routing_key=key, exchange="amq.direct", content=Content("TxMessage 6")) - channel.basic_publish(routing_key=name_a, content=Content("TxMessage 7")) + channel.message_transfer(routing_key=key, destination="amq.direct", body="TxMessage 6") + channel.message_transfer(routing_key=name_a, body="TxMessage 7") return queue_a, queue_b, queue_c - - def test_commit_overlapping_acks(self): - """ - Test that logically 'overlapping' acks do not cause errors on commit - """ - channel = self.channel - channel.queue_declare(queue="commit-overlapping", exclusive=True) - for i in range(1, 10): - channel.basic_publish(routing_key="commit-overlapping", content=Content("Message %d" % i)) - - - channel.tx_select() - - sub = channel.basic_consume(queue="commit-overlapping", no_ack=False) - queue = self.client.queue(sub.consumer_tag) - for i in range(1, 10): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - if i in [3, 6, 10]: - channel.basic_ack(delivery_tag=msg.delivery_tag) - - channel.tx_commit() - - #check all have been acked: - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None -- cgit v1.2.1 From 7ab2263f546329a2c620cfe151f27677ad06fd9d Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Sat, 17 Mar 2007 00:18:26 +0000 Subject: * python/qpid/testlib.py: - fix testlib ops to use 0-8 or 0-9 classes as appropriate. - fix bug in arg parsing of specfile. * cpp/tests/run-python-tests: Fix scripting bug that gave a OK build result even if there were python failures. * cpp/gen: svn:ignore all generated source and Makefile. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519194 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 50 +++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index ecae499451..eebb860104 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -80,7 +80,7 @@ Options: def __init__(self): # Defaults self.setBroker("localhost") - self.spec = "../specs/amqp.0-8.xml" + self.specfile = "../specs/amqp.0-8.xml" self.verbose = 1 self.ignore = [] @@ -89,6 +89,11 @@ Options: for line in f.readlines(): self.ignore.append(line.strip()) f.close() + def use08spec(self): + "True if we are running with the old 0-8 spec." + # NB: AMQP 0-8 identifies itself as 8-0 for historical reasons. + return self.spec.major==8 and self.spec.minor==0 + def _parseargs(self, args): try: opts, self.tests = getopt(args, "s:b:h?dvi:I:", ["help", "spec", "server", "verbose", "ignore", "ignore-file"]) @@ -105,9 +110,7 @@ Options: self.spec = qpid.spec.load(self.specfile) if len(self.tests) == 0: - # NB: not a typo but a quirk of AMQP history. - # AMQP 0-8 identifies itself as 8-0. - if self.spec.major==8 and self.spec.minor==0: + if self.use08spec(): testdir="tests_0-8" else: testdir="tests" @@ -203,11 +206,15 @@ class TestBase(unittest.TestCase): def consume(self, queueName): """Consume from named queue returns the Queue object.""" - if not "uniqueTag" in dir(self): self.uniqueTag = 1 - else: self.uniqueTag += 1 - consumer_tag = "tag" + str(self.uniqueTag) - self.channel.message_consume(queue=queueName, destination=consumer_tag, no_ack=True) - return self.client.queue(consumer_tag) + if testrunner.use08spec(): + reply = self.channel.basic_consume(queue=queueName, no_ack=True) + return self.client.queue(reply.consumer_tag) + else: + if not "uniqueTag" in dir(self): self.uniqueTag = 1 + else: self.uniqueTag += 1 + consumer_tag = "tag" + str(self.uniqueTag) + self.channel.message_consume(queue=queueName, destination=consumer_tag, no_ack=True) + return self.client.queue(consumer_tag) def assertEmpty(self, queue): """Assert that the queue is empty""" @@ -221,12 +228,25 @@ class TestBase(unittest.TestCase): Publish to exchange and assert queue.get() returns the same message. """ body = self.uniqueString() - self.channel.message_transfer(destination=exchange, - body=body, application_headers=properties, - routing_key=routing_key) - msg = queue.get(timeout=1) - self.assertEqual(body, msg.body) - if (properties): self.assertEqual(properties, msg.application_headers) + if testrunner.use08spec(): + self.channel.basic_publish( + exchange=exchange, + content=Content(body, properties=properties), + routing_key=routing_key) + else: + self.channel.message_transfer( + destination=exchange, body=body, + application_headers=properties, + routing_key=routing_key) + msg = queue.get(timeout=1) + if testrunner.use08spec(): + self.assertEqual(body, msg.content.body) + if (properties): + self.assertEqual(properties, msg.content.properties) + else: + self.assertEqual(body, msg.body) + if (properties): + self.assertEqual(properties, msg.application_headers) def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): """ -- cgit v1.2.1 From 5e2835f140a383743259e34aa575bcc0b64c8271 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 15:37:49 +0000 Subject: Merged revisions 501025 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r501025 | gsim | 2007-01-29 07:26:19 -0500 (Mon, 29 Jan 2007) | 3 lines Pass in application headers directly ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519974 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/testlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/testlib.py b/qpid/python/tests/testlib.py index d9391fc529..f345fbbd80 100644 --- a/qpid/python/tests/testlib.py +++ b/qpid/python/tests/testlib.py @@ -57,7 +57,7 @@ class TestBaseTest(TestBase): def testMessageProperties(self): """Verify properties are passed with message""" - props={"headers":{"x":1, "y":2}} + props={"x":1, "y":2} self.queue_declare(queue="q") q = self.consume("q") self.assertPublishGet(q, routing_key="q", properties=props) -- cgit v1.2.1 From 33dc32c3fadfdcbf21d21a39375a7442bcc015d3 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 16:12:51 +0000 Subject: * python/qpid/client.py (Client.__init__): Use "/" as default vhost. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519988 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing.txt | 21 ++------------------- qpid/python/qpid/client.py | 2 +- 2 files changed, 3 insertions(+), 20 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index f85e46beca..c13b40a42c 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,19 +1,2 @@ -tests.basic.BasicTests.test_ack -tests.basic.BasicTests.test_cancel -tests.basic.BasicTests.test_consume_exclusive -tests.basic.BasicTests.test_consume_no_local -tests.basic.BasicTests.test_consume_queue_errors -tests.basic.BasicTests.test_consume_unique_consumers -tests.basic.BasicTests.test_get -tests.basic.BasicTests.test_qos_prefetch_count -tests.basic.BasicTests.test_qos_prefetch_size -tests.basic.BasicTests.test_recover_requeue -tests.exchange.RecommendedTypesRuleTests.testTopic -tests.exchange.RequiredInstancesRuleTests.testAmqTopic -tests.queue.QueueTests.test_declare_exclusive -tests.queue.QueueTests.test_declare_passive -tests.queue.QueueTests.test_delete_ifempty -tests.queue.QueueTests.test_delete_ifunused -tests.queue.QueueTests.test_delete_simple -tests.queue.QueueTests.test_purge -tests.testlib.TestBaseTest.testMessageProperties +tests_0-8.exchange.RecommendedTypesRuleTests.testTopic +tests_0-8.exchange.RequiredInstancesRuleTests.testAmqTopic diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 3083cd4933..20d1093878 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -43,7 +43,7 @@ class Client: self.vhost = vhost if self.vhost == None: - self.vhost = self.host + self.vhost = "/" self.queues = {} self.lock = threading.Lock() -- cgit v1.2.1 From 5f6ffffd0b19da29b45c75f3e74e1f7c8961122b Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 16:32:29 +0000 Subject: Merged revisions 501143 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r501143 | gsim | 2007-01-29 13:51:18 -0500 (Mon, 29 Jan 2007) | 4 lines Added test suite for the message class. Updated list of failing tests for java broker on this branch. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@519999 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing.txt | 20 ++- qpid/python/tests/message.py | 397 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 qpid/python/tests/message.py (limited to 'qpid/python') diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index c13b40a42c..b671f86064 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,2 +1,18 @@ -tests_0-8.exchange.RecommendedTypesRuleTests.testTopic -tests_0-8.exchange.RequiredInstancesRuleTests.testAmqTopic +tests.basic.BasicTests.test_qos_prefetch_count +tests.basic.BasicTests.test_ack +tests.basic.BasicTests.test_cancel +tests.basic.BasicTests.test_consume_exclusive +tests.basic.BasicTests.test_consume_no_local +tests.basic.BasicTests.test_consume_queue_errors +tests.basic.BasicTests.test_consume_unique_consumers +tests.basic.BasicTests.test_get +tests.basic.BasicTests.test_qos_prefetch_size +tests.basic.BasicTests.test_recover_requeue +tests.exchange.RecommendedTypesRuleTests.testTopic +tests.exchange.RequiredInstancesRuleTests.testAmqTopic + +tests.message.MessageTests.test_qos_prefetch_count +tests.message.MessageTests.test_ack +tests.message.MessageTests.test_get +tests.message.MessageTests.test_qos_prefetch_size +tests.message.MessageTests.test_recover_requeue diff --git a/qpid/python/tests/message.py b/qpid/python/tests/message.py new file mode 100644 index 0000000000..0c9581f1c4 --- /dev/null +++ b/qpid/python/tests/message.py @@ -0,0 +1,397 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class MessageTests(TestBase): + """Tests for 'methods' on the amqp message 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.message_consume(destination="local_included", queue="test-queue-1a") + channel.message_consume(destination="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local") + channel.message_transfer(routing_key="test-queue-1b", body="consume_no_local") + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.message_consume(destination="first", queue="test-queue-2", exclusive=True) + try: + channel.message_consume(destination="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.message_consume(destination="first", queue="test-queue-2") + try: + channel.message_consume(destination="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.message_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.message_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.message_consume(destination="first", queue="test-queue-3") + try: + channel.message_consume(destination="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.message_consume(destination="my-consumer", queue="test-queue-4") + channel.message_transfer(routing_key="test-queue-4", body="One") + + #cancel should stop messages being delivered + channel.message_cancel(destination="my-consumer") + channel.message_transfer(routing_key="test-queue-4", body="Two") + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.message_cancel(destination="my-consumer") + channel.message_cancel(destination="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True) + + reply = channel.message_consume(queue="test-ack-queue", no_ack=False) + queue = self.client.queue(reply.consumer_tag) + + channel.message_transfer(routing_key="test-ack-queue", body="One") + channel.message_transfer(routing_key="test-ack-queue", body="Two") + channel.message_transfer(routing_key="test-ack-queue", body="Three") + channel.message_transfer(routing_key="test-ack-queue", body="Four") + channel.message_transfer(routing_key="test-ack-queue", body="Five") + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + channel.message_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.message_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.message_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True) + + channel.message_consume(queue="test-requeue", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(routing_key="test-requeue", body="One") + channel.message_transfer(routing_key="test-requeue", body="Two") + channel.message_transfer(routing_key="test-requeue", body="Three") + channel.message_transfer(routing_key="test-requeue", body="Four") + channel.message_transfer(routing_key="test-requeue", body="Five") + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + #channel.message_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + #channel.message_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + msg1.ok() #One + msg2.ok() #Two + msg4.ok() #Two + + channel.message_cancel(destination="consumer_tag") + channel.message_consume(queue="test-requeue", destination="consumer_tag") + queue2 = self.client.queue("consumer_tag") + + channel.message_recover(requeue=True) + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) + + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.message_consume(queue="test-prefetch-count", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 5: + channel.message_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.message_consume(queue="test-prefetch-size", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.message_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.message_transfer(routing_key="test-prefetch-size", body=large) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.body) + + def test_get(self): + """ + Test message_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.message_transfer(routing_key="test-get", body="Message %d" % i) + + #use message_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + reply = channel.message_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-ok") + self.assertEqual("Message %d" % i, reply.body) + + reply = channel.message_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.message_transfer(routing_key="test-get", body="Message %d" % i) + + for i in range(11, 21): + reply = channel.message_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-ok") + self.assertEqual("Message %d" % i, reply.body) + if(i == 13): + channel.message_ack(delivery_tag=reply.delivery_tag, multiple=True) + if(i in [15, 17, 19]): + channel.message_ack(delivery_tag=reply.delivery_tag) + + reply = channel.message_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-empty") + + #recover(requeue=True) + channel.message_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + reply = channel.message_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-ok") + self.assertEqual("Message %d" % i, reply.body) + channel.message_ack(delivery_tag=reply.delivery_tag) + + reply = channel.message_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-empty") + + channel.message_recover(requeue=True) + + reply = channel.message_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "get-empty") -- cgit v1.2.1 From cd8e6cabd2fd4f37b4ca7fe4b673629062aa194d Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 16:34:53 +0000 Subject: Add java_failing_0-1.txt for current Java failures against python/tests_0-8. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520007 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing_0-8.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 qpid/python/java_failing_0-8.txt (limited to 'qpid/python') diff --git a/qpid/python/java_failing_0-8.txt b/qpid/python/java_failing_0-8.txt new file mode 100644 index 0000000000..c7db632fac --- /dev/null +++ b/qpid/python/java_failing_0-8.txt @@ -0,0 +1,2 @@ +tests.exchange.RecommendedTypesRuleTests.testTopic +tests.exchange.RequiredInstancesRuleTests.testAmqTopic -- cgit v1.2.1 From 8467f918a5504b6198450c4900e425263318958a Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 19:15:11 +0000 Subject: * python/testlib.py: -s (spec) option now also takes abbreviations "0-8" and "0-9" to load default 0-8 or 0-9 XML respectively. Default is still 0-8. Merged revisions 501586 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r501586 | rhs | 2007-01-30 16:44:41 -0500 (Tue, 30 Jan 2007) | 1 line updated python spec parse to load from multiple files, changed default specs to include errata ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520050 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 3 +- qpid/python/qpid/spec.py | 88 ++++++++++++++++++++++++++------------------- qpid/python/qpid/testlib.py | 26 ++++++++------ 3 files changed, 69 insertions(+), 48 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index fd712d324b..8b7a2752c5 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -3,7 +3,8 @@ import qpid from qpid.client import Client from qpid.content import Content -client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-9.xml")) +client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-9.xml", + "../specs/amqp-errata.0-9.xml")) client.start({"LOGIN": "guest", "PASSWORD": "guest"}) ch = client.channel(1) ch.channel_open() diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 3e8b9e37bd..ffd7b5a454 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -79,11 +79,12 @@ class Spec(Metadata): PRINT=["major", "minor", "file"] - def __init__(self, major, minor, file): + def __init__(self, major, minor, file, errata): Metadata.__init__(self) self.major = major self.minor = minor self.file = file + self.errata = errata self.constants = SpecContainer() self.classes = SpecContainer() # methods indexed by classname_methname @@ -267,43 +268,56 @@ def load_fields(nd, l, domains): type = domains[type] l.add(Field(pythonize(f_nd["@name"]), f_nd.index(), type, get_docs(f_nd))) -def load(specfile): +def load(specfile, *errata): doc = xmlutil.parse(specfile) - root = doc["amqp"][0] - spec = Spec(int(root["@major"]), int(root["@minor"]), specfile) - - # constants - for nd in root["constant"]: - const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), - nd.get("@class"), get_docs(nd)) - spec.constants.add(const) - - # domains are typedefs - domains = {} - for nd in root["domain"]: - domains[nd["@name"]] = nd["@type"] - - # classes - for c_nd in root["class"]: - klass = Class(spec, pythonize(c_nd["@name"]), int(c_nd["@index"]), - c_nd["@handler"], get_docs(c_nd)) - load_fields(c_nd, klass.fields, domains) - for m_nd in c_nd["method"]: - meth = Method(klass, pythonize(m_nd["@name"]), - int(m_nd["@index"]), - m_nd.get_bool("@content", False), - [pythonize(nd["@name"]) for nd in m_nd["response"]], - m_nd.get_bool("@synchronous", False), - m_nd.text, - get_docs(m_nd)) - load_fields(m_nd, meth.fields, domains) - klass.methods.add(meth) - # resolve the responses - for m in klass.methods: - m.responses = [klass.methods.byname[r] for r in m.responses] - for resp in m.responses: - resp.response = True - spec.classes.add(klass) + spec_root = doc["amqp"][0] + spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile, errata) + + for root in [spec_root] + map(lambda x: xmlutil.parse(x)["amqp"][0], errata): + # constants + for nd in root["constant"]: + const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), + nd.get("@class"), get_docs(nd)) + spec.constants.add(const) + + # domains are typedefs + domains = {} + for nd in root["domain"]: + domains[nd["@name"]] = nd["@type"] + + # classes + for c_nd in root["class"]: + cname = pythonize(c_nd["@name"]) + if root == spec_root: + klass = Class(spec, cname, int(c_nd["@index"]), c_nd["@handler"], + get_docs(c_nd)) + spec.classes.add(klass) + else: + klass = spec.classes.byname[cname] + + added_methods = [] + load_fields(c_nd, klass.fields, domains) + for m_nd in c_nd["method"]: + mname = pythonize(m_nd["@name"]) + if root == spec_root: + meth = Method(klass, mname, + int(m_nd["@index"]), + m_nd.get_bool("@content", False), + [pythonize(nd["@name"]) for nd in m_nd["response"]], + m_nd.get_bool("@synchronous", False), + m_nd.text, + get_docs(m_nd)) + klass.methods.add(meth) + added_methods.append(meth) + else: + meth = klass.methods.byname[mname] + load_fields(m_nd, meth.fields, domains) + # resolve the responses + for m in added_methods: + m.responses = [klass.methods.byname[r] for r in m.responses] + for resp in m.responses: + resp.response = True + spec.post_load() return spec diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index eebb860104..708ebcdcb9 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -57,7 +57,9 @@ run-tests [options] [test*] The name of a test is package.module.ClassName.testMethod Options: -?/-h/--help : this message - -s/--spec : file containing amqp XML spec + -s/--spec : URL of AMQP XML specification or one of these abbreviations: + 0-8 - use the default 0-8 specification. + 0-9 - use the default 0-9 specification. -b/--broker [[/]@][:] : broker to connect to -v/--verbose : verbose - lists tests as they are run. -d/--debug : enable debug logging. @@ -77,13 +79,6 @@ Options: self.user = default(self.user, "guest") self.password = default(self.password, "guest") - def __init__(self): - # Defaults - self.setBroker("localhost") - self.specfile = "../specs/amqp.0-8.xml" - self.verbose = 1 - self.ignore = [] - def ignoreFile(self, filename): f = file(filename) for line in f.readlines(): self.ignore.append(line.strip()) @@ -95,6 +90,12 @@ Options: return self.spec.major==8 and self.spec.minor==0 def _parseargs(self, args): + # Defaults + self.setBroker("localhost") + self.verbose = 1 + self.ignore = [] + self.specfile = "0-8" + self.errata = [] try: opts, self.tests = getopt(args, "s:b:h?dvi:I:", ["help", "spec", "server", "verbose", "ignore", "ignore-file"]) except GetoptError, e: @@ -107,8 +108,13 @@ Options: if opt in ("-d", "--debug"): logging.basicConfig(level=logging.DEBUG) if opt in ("-i", "--ignore"): self.ignore.append(value) if opt in ("-I", "--ignore-file"): self.ignoreFile(value) - - self.spec = qpid.spec.load(self.specfile) + if (self.specfile == "0-8"): + self.specfile = "../specs/amqp.0-8.xml" + if (self.specfile == "0-9"): + self.specfile = "../specs/amqp.0-9.xml" + self.errata = ["../specs/amqp-errata.0-9.xml"] + print "Using specification from:", self.specfile + self.spec = qpid.spec.load(self.specfile, *self.errata) if len(self.tests) == 0: if self.use08spec(): testdir="tests_0-8" -- cgit v1.2.1 From 219cf653fabddd97c0063b6159aeab2282333d69 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 19:19:53 +0000 Subject: * python/testlib.py: -s (spec) option now also takes abbreviations "0-8" and "0-9" to load default 0-8 or 0-9 XML respectively. Default is still 0-8. Merged revisions 501586 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r501586 | rhs | 2007-01-30 16:44:41 -0500 (Tue, 30 Jan 2007) | 1 line updated python spec parse to load from multiple files, changed default specs to include errata ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520051 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 3 +++ qpid/python/qpid/peer.py | 8 +++++- qpid/python/tests/basic.py | 14 +++++------ qpid/python/tests/message.py | 56 ++++++++++++++++++++++++++++-------------- 4 files changed, 54 insertions(+), 27 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index fb1e0927f0..0785fe8774 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -232,6 +232,9 @@ class Response(Frame): method = Method.decode(spec, dec, size - 20) return Response(id, request_id, batch_offset, method) + def __str__(self): + return "[%s] Response(%s,%s,%s) %s" % (self.channel, self.id, self.request_id, self.batch_offset, self.method) + class Header(Frame): type = "frame_header" diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 8d5029004e..b5c655dc2a 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -220,6 +220,8 @@ class Channel: work.put(self.incoming) elif isinstance(frame, Response): self.requester.receive(self, frame) + if frame.method_type.content: + self.queue = self.responses return self.queue.put(frame) @@ -241,7 +243,11 @@ class Channel: return None try: resp = self.responses.get() - return Message(self, resp) + if resp.method_type.content: + return Message(self, resp, read_content(self.responses)) + else: + return Message(self, resp) + except QueueClosed, e: if self.closed: raise Closed(self.reason) diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 9f26ee3728..140576540a 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -347,12 +347,12 @@ class BasicTests(TestBase): for i in range(1, 11): reply = channel.basic_get(no_ack=True) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-ok") + self.assertEqual(reply.method.name, "get_ok") self.assertEqual("Message %d" % i, reply.content.body) reply = channel.basic_get(no_ack=True) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-empty") + self.assertEqual(reply.method.name, "get_empty") #repeat for no_ack=False for i in range(11, 21): @@ -361,7 +361,7 @@ class BasicTests(TestBase): for i in range(11, 21): reply = channel.basic_get(no_ack=False) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-ok") + self.assertEqual(reply.method.name, "get_ok") self.assertEqual("Message %d" % i, reply.content.body) if(i == 13): channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) @@ -370,7 +370,7 @@ class BasicTests(TestBase): reply = channel.basic_get(no_ack=True) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-empty") + self.assertEqual(reply.method.name, "get_empty") #recover(requeue=True) channel.basic_recover(requeue=True) @@ -379,16 +379,16 @@ class BasicTests(TestBase): for i in [14, 16, 18, 20]: reply = channel.basic_get(no_ack=False) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-ok") + self.assertEqual(reply.method.name, "get_ok") self.assertEqual("Message %d" % i, reply.content.body) channel.basic_ack(delivery_tag=reply.delivery_tag) reply = channel.basic_get(no_ack=True) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-empty") + self.assertEqual(reply.method.name, "get_empty") channel.basic_recover(requeue=True) reply = channel.basic_get(no_ack=True) self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get-empty") + self.assertEqual(reply.method.name, "get_empty") diff --git a/qpid/python/tests/message.py b/qpid/python/tests/message.py index 0c9581f1c4..a7c8f875eb 100644 --- a/qpid/python/tests/message.py +++ b/qpid/python/tests/message.py @@ -149,8 +149,8 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue="test-ack-queue", exclusive=True) - reply = channel.message_consume(queue="test-ack-queue", no_ack=False) - queue = self.client.queue(reply.consumer_tag) + channel.message_consume(queue="test-ack-queue", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") channel.message_transfer(routing_key="test-ack-queue", body="One") channel.message_transfer(routing_key="test-ack-queue", body="Two") @@ -170,8 +170,9 @@ class MessageTests(TestBase): self.assertEqual("Four", msg4.body) self.assertEqual("Five", msg5.body) - channel.message_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - channel.message_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + msg1.ok() + msg2.ok() + msg4.ok() channel.message_recover(requeue=False) @@ -214,8 +215,6 @@ class MessageTests(TestBase): self.assertEqual("Four", msg4.body) self.assertEqual("Five", msg5.body) - #channel.message_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - #channel.message_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four msg1.ok() #One msg2.ok() #Two msg4.ok() #Two @@ -252,8 +251,8 @@ class MessageTests(TestBase): #setup: declare queue and subscribe channel = self.channel channel.queue_declare(queue="test-prefetch-count", exclusive=True) - subscription = channel.message_consume(queue="test-prefetch-count", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) + subscription = channel.message_consume(queue="test-prefetch-count", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") #set prefetch to 5: channel.message_qos(prefetch_count=5) @@ -263,22 +262,30 @@ class MessageTests(TestBase): channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) #only 5 messages should have been delivered: + msgs = [] for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) + msgs.add(msg) try: extra = queue.get(timeout=1) self.fail("Got unexpected 6th message in original queue: " + extra.body) except Empty: None #ack messages and check that the next set arrive ok: - channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + #todo: once batching is implmented, send a single response for all messages + for msg in msgs: + msg.ok() + msgs.clear() for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) + msgs.add(msg) - channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + for msg in msgs: + msg.ok() + msgs.clear() try: extra = queue.get(timeout=1) @@ -294,8 +301,8 @@ class MessageTests(TestBase): #setup: declare queue and subscribe channel = self.channel channel.queue_declare(queue="test-prefetch-size", exclusive=True) - subscription = channel.message_consume(queue="test-prefetch-size", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) + subscription = channel.message_consume(queue="test-prefetch-size", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") #set prefetch to 50 bytes (each message is 9 or 10 bytes): channel.message_qos(prefetch_size=50) @@ -305,9 +312,11 @@ class MessageTests(TestBase): channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) #only 5 messages should have been delivered (i.e. 45 bytes worth): + msgs = [] for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) + msgs.add(msg) try: extra = queue.get(timeout=1) @@ -315,13 +324,18 @@ class MessageTests(TestBase): except Empty: None #ack messages and check that the next set arrive ok: - channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + for msg in msgs: + msg.ok() + msgs.clear() for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) + msgs.add(msg) - channel.message_ack(delivery_tag=msg.delivery_tag, multiple=True) + for msg in msgs: + msg.ok() + msgs.clear() try: extra = queue.get(timeout=1) @@ -366,10 +380,13 @@ class MessageTests(TestBase): self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-ok") self.assertEqual("Message %d" % i, reply.body) - if(i == 13): - channel.message_ack(delivery_tag=reply.delivery_tag, multiple=True) - if(i in [15, 17, 19]): - channel.message_ack(delivery_tag=reply.delivery_tag) + reply.ok() + + #todo: when batching is available, test ack multiple + #if(i == 13): + # channel.message_ack(delivery_tag=reply.delivery_tag, multiple=True) + #if(i in [15, 17, 19]): + # channel.message_ack(delivery_tag=reply.delivery_tag) reply = channel.message_get(no_ack=True) self.assertEqual(reply.method.klass.name, "message") @@ -384,7 +401,8 @@ class MessageTests(TestBase): self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-ok") self.assertEqual("Message %d" % i, reply.body) - channel.message_ack(delivery_tag=reply.delivery_tag) + reply.ok() + #channel.message_ack(delivery_tag=reply.delivery_tag) reply = channel.message_get(no_ack=True) self.assertEqual(reply.method.klass.name, "message") -- cgit v1.2.1 From 495f9288300c8e9e69a865e1ed75f73eb311df29 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 19:39:55 +0000 Subject: Merged revisions 504590 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r504590 | gsim | 2007-02-07 10:36:01 -0500 (Wed, 07 Feb 2007) | 6 lines Added support for receiving and sending of references Added asynchronous mode to channels (responses can be tracked via a future, rather than blocking on each request) Added ability to override server suggested connection tune params Added two tests for reference functionality (more to follow) ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520061 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/client.py | 31 +++++++++-- qpid/python/qpid/codec.py | 27 +++++++--- qpid/python/qpid/peer.py | 27 +++++++++- qpid/python/qpid/reference.py | 117 ++++++++++++++++++++++++++++++++++++++++++ qpid/python/qpid/testlib.py | 4 +- qpid/python/tests/message.py | 64 +++++++++++++++++++++++ 6 files changed, 256 insertions(+), 14 deletions(-) create mode 100644 qpid/python/qpid/reference.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 20d1093878..ea6aa7901a 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -28,6 +28,7 @@ from delegate import Delegate from connection import Connection, Frame, connect from spec import load from queue import Queue +from reference import ReferenceId, References class Client: @@ -69,13 +70,14 @@ class Client: self.lock.release() return q - def start(self, response, mechanism="AMQPLAIN", locale="en_US"): + def start(self, response, mechanism="AMQPLAIN", locale="en_US", tune_params=None): self.mechanism = mechanism self.response = response self.locale = locale + self.tune_params = tune_params self.conn = Connection(connect(self.host, self.port), self.spec) - self.peer = Peer(self.conn, ClientDelegate(self)) + self.peer = Peer(self.conn, ClientDelegate(self), self.opened) self.conn.init() self.peer.start() @@ -85,6 +87,9 @@ class Client: def channel(self, id): return self.peer.channel(id) + def opened(self, ch): + ch.references = References() + class ClientDelegate(Delegate): def __init__(self, client): @@ -97,9 +102,29 @@ class ClientDelegate(Delegate): locale=self.client.locale) def connection_tune(self, ch, msg): - msg.tune_ok(*msg.frame.args) + if self.client.tune_params: + #todo: just override the params, i.e. don't require them + # all to be included in tune_params + msg.tune_ok(**self.client.tune_params) + else: + msg.tune_ok(*msg.frame.args) self.client.started.set() + def message_transfer(self, ch, msg): + if isinstance(msg.body, ReferenceId): + self.client.queue(msg.destination).put(ch.references.get(msg.body.id)) + else: + self.client.queue(msg.destination).put(msg) + + def message_open(self, ch, msg): + ch.references.open(msg.reference) + + def message_close(self, ch, msg): + ch.references.close(msg.reference) + + def message_append(self, ch, msg): + ch.references.get(msg.reference).append(msg.bytes) + def basic_deliver(self, ch, msg): self.client.queue(msg.consumer_tag).put(msg) diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 205405894a..3c1e73c2e6 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -26,6 +26,7 @@ fields. from cStringIO import StringIO from struct import * +from reference import ReferenceId class EOF(Exception): pass @@ -195,14 +196,24 @@ class Codec: return self.decode_longlong() def encode_content(self, s): - # XXX - self.encode_octet(0) - self.encode_longstr(s) - - def decode_content(self): - # XXX - self.decode_octet() - return self.decode_longstr() + # content can be passed as a string in which case it is assumed to + # be inline data, or as an instance of ReferenceId indicating it is + # a reference id + if isinstance(s, ReferenceId): + self.encode_octet(1) + self.encode_longstr(s.id) + else: + self.encode_octet(0) + self.encode_longstr(s) + + def decode_content(self): + # return a string for inline data and a ReferenceId instance for + # references + type = self.decode_octet() + if type == 0: + return self.decode_longstr() + else: + return ReferenceId(self.decode_longstr()) def test(type, value): if isinstance(value, (list, tuple)): diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index b5c655dc2a..6c8c6647c9 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -50,13 +50,14 @@ class Sequence: class Peer: - def __init__(self, conn, delegate): + def __init__(self, conn, delegate, channel_callback=None): self.conn = conn self.delegate = delegate self.outgoing = Queue(0) self.work = Queue(0) self.channels = {} self.lock = thread.allocate_lock() + self.channel_callback = channel_callback #notified when channels are created def channel(self, id): self.lock.acquire() @@ -66,6 +67,8 @@ class Peer: except KeyError: ch = Channel(id, self.outgoing, self.conn.spec) self.channels[id] = ch + if self.channel_callback: + self.channel_callback(ch) finally: self.lock.release() return ch @@ -177,6 +180,7 @@ class Channel: # XXX: better switch self.reliable = False + self.synchronous = True def close(self, reason): if self.closed: @@ -238,6 +242,12 @@ class Channel: content = kwargs.pop("content", None) frame = Method(type, type.arguments(*args, **kwargs)) if self.reliable: + if not self.synchronous: + future = Future() + self.request(frame, future.put_response, content) + if not frame.method.responses: return None + else: return future + self.request(frame, self.queue_response, content) if not frame.method.responses: return None @@ -304,3 +314,18 @@ def read_content(queue): buf.write(content) read += len(content) return Content(buf.getvalue(), children, header.properties.copy()) + +class Future: + def __init__(self): + self.completed = threading.Event() + + def put_response(self, channel, response): + self.response = response + self.completed.set() + + def get_response(self, timeout=None): + self.completed.wait(timeout) + return self.response + + def is_complete(self): + return self.completed.isSet() diff --git a/qpid/python/qpid/reference.py b/qpid/python/qpid/reference.py new file mode 100644 index 0000000000..d357560390 --- /dev/null +++ b/qpid/python/qpid/reference.py @@ -0,0 +1,117 @@ +#!/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. +# + +""" +Support for amqp 'reference' content (as opposed to inline content) +""" + +import threading +from queue import Queue, Closed + +class NotOpened(Exception): pass + +class AlreadyOpened(Exception): pass + +""" +A representation of a reference id; can be passed wherever amqp +content is required in place of inline data +""" +class ReferenceId: + + def __init__(self, id): + self.id = id + +""" +Holds content received through 'reference api'. Instances of this +class will be placed in the consumers queue on receiving a transfer +(assuming the reference has been opened). Data can be retrieved in +chunks (as append calls are received) or in full (after reference has +been closed signalling data s complete). +""" + +class Reference: + + def __init__(self, id): + self.id = id + self.chunks = Queue(0) + + def close(self): + self.chunks.close() + + def append(self, bytes): + self.chunks.put(bytes) + + def get_chunk(self): + return self.chunks.get() + + def get_complete(self): + data = "" + for chunk in self: + data += chunk + return data + + def next(self): + try: + return self.get_chunk() + except Closed, e: + raise StopIteration + + def __iter__(self): + return self + +""" +Manages a set of opened references. New references can be opened and +existing references can be retrieved or closed. +""" +class References: + + def __init__(self): + self.map = {} + self.lock = threading.Lock() + + def get(self, id): + self.lock.acquire() + try: + try: + ref = self.map[id] + except KeyError: + raise NotOpened() + finally: + self.lock.release() + return ref + + def open(self, id): + self.lock.acquire() + try: + if id in self.map: raise AlreadyOpened() + self.map[id] = Reference(id) + finally: + self.lock.release() + + + def close(self, id): + self.get(id).close() + self.lock.acquire() + try: + del map[id] + finally: + self.lock.release() + diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 708ebcdcb9..dcbf0ed91c 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -145,7 +145,7 @@ Options: print "=======================================" return result.wasSuccessful() - def connect(self, host=None, port=None, spec=None, user=None, password=None): + def connect(self, host=None, port=None, spec=None, user=None, password=None, tune_params=None): """Connect to the broker, returns a qpid.client.Client""" host = host or self.host port = port or self.port @@ -153,7 +153,7 @@ Options: user = user or self.user password = password or self.password client = qpid.client.Client(host, port, spec) - client.start({"LOGIN": user, "PASSWORD": password}) + client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params) return client diff --git a/qpid/python/tests/message.py b/qpid/python/tests/message.py index a7c8f875eb..10d0f51448 100644 --- a/qpid/python/tests/message.py +++ b/qpid/python/tests/message.py @@ -20,6 +20,7 @@ from qpid.client import Client, Closed from qpid.queue import Empty from qpid.content import Content from qpid.testlib import testrunner, TestBase +from qpid.reference import Reference, ReferenceId class MessageTests(TestBase): """Tests for 'methods' on the amqp message 'class'""" @@ -413,3 +414,66 @@ class MessageTests(TestBase): reply = channel.message_get(no_ack=True) self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-empty") + + def test_reference_simple(self): + """ + Test basic ability to handle references + """ + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.message_append(reference=refId, bytes="abcd") + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) + channel.synchronous = True + + channel.message_append(reference=refId, bytes="efgh") + channel.message_append(reference=refId, bytes="ijkl") + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + msg = queue.get(timeout=1) + if isinstance(msg, Reference): + #should we force broker to deliver as reference by frame + #size limit? or test that separately? for compliance, + #allowing either seems best for now... + data = msg.get_complete() + else: + data = msg.body + self.assertEquals("abcdefghijkl", data) + + + def test_reference_large(self): + """ + Test basic ability to handle references whose content exceeds max frame size + """ + channel = self.channel + self.queue_declare(queue="ref_queue") + + #generate a big data string (> max frame size of consumer): + data = "0123456789" + for i in range(0, 10): + data += data + #send it inline + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=data) + channel.synchronous = True + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + #create a new connection for consumer, with specific max frame size (< data) + other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0}) + ch2 = other.channel(1) + ch2.channel_open() + ch2.message_consume(queue="ref_queue", destination="c1") + queue = other.queue("c1") + + msg = queue.get(timeout=1) + self.assertTrue(isinstance(msg, Reference)) + self.assertEquals(data, msg.get_complete()) -- cgit v1.2.1 From 12f96a3a954c2827a9fd80466d01b91942669ea3 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 19:45:42 +0000 Subject: Changed tests.* to tests_0-8.*. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520067 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/java_failing_0-8.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/java_failing_0-8.txt b/qpid/python/java_failing_0-8.txt index c7db632fac..c13b40a42c 100644 --- a/qpid/python/java_failing_0-8.txt +++ b/qpid/python/java_failing_0-8.txt @@ -1,2 +1,2 @@ -tests.exchange.RecommendedTypesRuleTests.testTopic -tests.exchange.RequiredInstancesRuleTests.testAmqTopic +tests_0-8.exchange.RecommendedTypesRuleTests.testTopic +tests_0-8.exchange.RequiredInstancesRuleTests.testAmqTopic -- cgit v1.2.1 From e99f2ccaab3d3f2ec4733b57c51bce52df8fc790 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 20:02:29 +0000 Subject: Merged revisions 504601-504602,504604-504609,504611-504702,504704-504707,504709-504849 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r504601 | gsim | 2007-02-07 11:09:16 -0500 (Wed, 07 Feb 2007) | 3 lines Added list of valid responses to method descriptions where appropriate. ........ r504700 | aconway | 2007-02-07 16:30:32 -0500 (Wed, 07 Feb 2007) | 2 lines Use self.queue_open to ensure deletion of queue - was clashing with message.py tests. ........ r504849 | gsim | 2007-02-08 05:14:50 -0500 (Thu, 08 Feb 2007) | 3 lines Fixes to qos and get tests. Added test for correct completion of references. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520073 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 4 +++ qpid/python/tests/basic.py | 2 +- qpid/python/tests/message.py | 86 +++++++++++++++++++++++++++++++++----------- 3 files changed, 70 insertions(+), 22 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index ffd7b5a454..e430c45b96 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -208,6 +208,10 @@ class Method(Metadata): if f.docs: s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, f.name)] + [fill(d, 4) for d in f.docs[1:]]) + if self.responses: + s += "\n\nValid responses: " + for r in self.responses: + s += r.name + " " return s METHOD = "__method__" diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py index 140576540a..0100b8ee3e 100644 --- a/qpid/python/tests/basic.py +++ b/qpid/python/tests/basic.py @@ -147,7 +147,7 @@ class BasicTests(TestBase): Test basic ack/recover behaviour """ channel = self.channel - channel.queue_declare(queue="test-ack-queue", exclusive=True) + self.queue_declare(queue="test-ack-queue", exclusive=True) reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) queue = self.client.queue(reply.consumer_tag) diff --git a/qpid/python/tests/message.py b/qpid/python/tests/message.py index 10d0f51448..916a9825bd 100644 --- a/qpid/python/tests/message.py +++ b/qpid/python/tests/message.py @@ -267,7 +267,7 @@ class MessageTests(TestBase): for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.add(msg) + msgs.append(msg) try: extra = queue.get(timeout=1) self.fail("Got unexpected 6th message in original queue: " + extra.body) @@ -277,16 +277,16 @@ class MessageTests(TestBase): #todo: once batching is implmented, send a single response for all messages for msg in msgs: msg.ok() - msgs.clear() + del msgs for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.add(msg) + msgs.append(msg) for msg in msgs: msg.ok() - msgs.clear() + del msgs try: extra = queue.get(timeout=1) @@ -317,7 +317,8 @@ class MessageTests(TestBase): for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.add(msg) + print "Got Message %d" % i + msgs.append(msg) try: extra = queue.get(timeout=1) @@ -327,16 +328,16 @@ class MessageTests(TestBase): #ack messages and check that the next set arrive ok: for msg in msgs: msg.ok() - msgs.clear() + del msgs for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.add(msg) + msgs.append(msg) for msg in msgs: msg.ok() - msgs.clear() + del msgs try: extra = queue.get(timeout=1) @@ -363,12 +364,13 @@ class MessageTests(TestBase): #use message_get to read back the messages, and check that we get an empty at the end for i in range(1, 11): - reply = channel.message_get(no_ack=True) + tag = "queue %d" % i + reply = channel.message_get(no_ack=True, queue="test-get", destination=tag) self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-ok") - self.assertEqual("Message %d" % i, reply.body) + self.assertEqual(reply.method.name, "ok") + self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) - reply = channel.message_get(no_ack=True) + reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-empty") @@ -377,10 +379,11 @@ class MessageTests(TestBase): channel.message_transfer(routing_key="test-get", body="Message %d" % i) for i in range(11, 21): - reply = channel.message_get(no_ack=False) + tag = "queue %d" % i + reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-ok") - self.assertEqual("Message %d" % i, reply.body) + self.assertEqual(reply.method.name, "ok") + self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) reply.ok() #todo: when batching is available, test ack multiple @@ -389,7 +392,7 @@ class MessageTests(TestBase): #if(i in [15, 17, 19]): # channel.message_ack(delivery_tag=reply.delivery_tag) - reply = channel.message_get(no_ack=True) + reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-empty") @@ -398,20 +401,21 @@ class MessageTests(TestBase): #get the unacked messages again (14, 16, 18, 20) for i in [14, 16, 18, 20]: - reply = channel.message_get(no_ack=False) + tag = "queue %d" % i + reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-ok") - self.assertEqual("Message %d" % i, reply.body) + self.assertEqual(reply.method.name, "ok") + self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) reply.ok() #channel.message_ack(delivery_tag=reply.delivery_tag) - reply = channel.message_get(no_ack=True) + reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-empty") channel.message_recover(requeue=True) - reply = channel.message_get(no_ack=True) + reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "get-empty") @@ -477,3 +481,43 @@ class MessageTests(TestBase): msg = queue.get(timeout=1) self.assertTrue(isinstance(msg, Reference)) self.assertEquals(data, msg.get_complete()) + + def test_reference_completion(self): + """ + Test that reference transfer are not deemed complete until + closed (therefore are not acked or routed until that point) + """ + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.message_append(reference=refId, bytes="abcd") + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) + channel.synchronous = True + + try: + msg = queue.get(timeout=1) + self.fail("Got unexpected message on queue: " + msg) + except Empty: None + + self.assertTrue(not ack.is_complete()) + + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + msg = queue.get(timeout=1) + if isinstance(msg, Reference): + #should we force broker to deliver as reference by frame + #size limit? or test that separately? for compliance, + #allowing either seems best for now... + data = msg.get_complete() + else: + data = msg.body + self.assertEquals("abcdefghijkl", data) + -- cgit v1.2.1 From 9975a3e9875d6be093a010071e9e7f3bbd9045f4 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 20:34:32 +0000 Subject: Merged revisions 507491-507559,507561-507601,507603-507621,507623-507671,507673-507959,507961-507992,507994-508097,508099-508149,508151-508155,508157-508232,508234-508378,508380-508390,508392-508459,508461-508704,508707-509615,509617-509737,509739-509753,509756-509833,509835-510106,510108-510160,510162-510179,510181-510552,510554-510704,510706-510911,510913-510985,510987-511003,511005-514750,514752-515720,515722-516156,516158-516458,516461-516484,516486-516488,516490-517823,517825,517827,517829,517831-517832,517834-517848,517850,517852-517854,517856-517858,517860-517877,517879-517886,517888-517891,517893-517903,517905,517907-517928,517930,517932-518197,518201-518206,518208-518230,518232,518235,518237,518239-518240,518243-518245,518247-518255,518257,518259-518260,518262,518264,518266-518292,518294-518707 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/qpid.0-9 ........ r507491 | gsim | 2007-02-14 06:39:26 -0500 (Wed, 14 Feb 2007) | 3 lines Expanded the use of batched acks to a few other places in tests. ........ r508377 | gsim | 2007-02-16 07:03:37 -0500 (Fri, 16 Feb 2007) | 4 lines Updated failing list for java (some of these result in test suite blocking) Added better error handling when connection closes without close method ........ r508396 | gsim | 2007-02-16 08:54:54 -0500 (Fri, 16 Feb 2007) | 3 lines Fix: use message_resume not channel_resume ........ r509611 | gsim | 2007-02-20 10:39:14 -0500 (Tue, 20 Feb 2007) | 3 lines Fixed bug where response id rather than request id was being used to get listener for response. ........ r509617 | gsim | 2007-02-20 10:52:31 -0500 (Tue, 20 Feb 2007) | 3 lines Updated list of failing tests for java broker on this branch. ........ r510096 | gsim | 2007-02-21 11:49:27 -0500 (Wed, 21 Feb 2007) | 5 lines Fixed bug in references where map wasn't qualified in close Attach reference to transfer, as it will be deleted on close Altered tests to get reference from the message on the queue rather than looking them up from channel as they are already gone there ........ r510114 | astitcher | 2007-02-21 12:37:36 -0500 (Wed, 21 Feb 2007) | 3 lines r1224@fuschia: andrew | 2007-02-21 17:20:59 +0000 Updated expected cpp broker test failures ........ r510128 | gsim | 2007-02-21 13:06:02 -0500 (Wed, 21 Feb 2007) | 3 lines Ensure socket is closed in tearDown ........ r510913 | gsim | 2007-02-23 06:37:08 -0500 (Fri, 23 Feb 2007) | 3 lines Revised list of failing tests for java broker on this branch ........ r515363 | aconway | 2007-03-06 18:35:08 -0500 (Tue, 06 Mar 2007) | 6 lines * python/qpid/peer.py (Channel.__init__): use reliable framing if version >= 0-8. * python/qpid/spec.py (Spec.__init__): Remove unused parameter. * python/qpid/testlib.py (TestRunner._parseargs): Add --errata option, set default errata only if --spec is not present. ........ r518707 | aconway | 2007-03-15 13:49:44 -0400 (Thu, 15 Mar 2007) | 6 lines * python/qpid/peer.py (Peer.close): Close delegate *before* channels. Otherwise we get a race: closing a channel can wake a client thread, which may see client.closed as still false. Was causing bogus exceptions in some tests. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520094 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing.txt | 4 + qpid/python/java_failing.txt | 12 +-- qpid/python/qpid/client.py | 11 ++- qpid/python/qpid/connection.py | 3 + qpid/python/qpid/peer.py | 11 ++- qpid/python/qpid/reference.py | 2 +- qpid/python/qpid/spec.py | 5 +- qpid/python/qpid/testlib.py | 53 ++++++---- qpid/python/tests/message.py | 214 +++++++++++++++++++++++++++++++++-------- qpid/python/tests/tx.py | 3 +- 10 files changed, 243 insertions(+), 75 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing.txt b/qpid/python/cpp_failing.txt index e69de29bb2..69c7cf3f5a 100644 --- a/qpid/python/cpp_failing.txt +++ b/qpid/python/cpp_failing.txt @@ -0,0 +1,4 @@ +tests.message.MessageTests.test_checkpoint +tests.message.MessageTests.test_reference_large +tests.message.MessageTests.test_reject +tests.basic.BasicTests.test_get diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt index b671f86064..7252d0f496 100644 --- a/qpid/python/java_failing.txt +++ b/qpid/python/java_failing.txt @@ -1,4 +1,4 @@ -tests.basic.BasicTests.test_qos_prefetch_count +ntests.basic.BasicTests.test_qos_prefetch_count tests.basic.BasicTests.test_ack tests.basic.BasicTests.test_cancel tests.basic.BasicTests.test_consume_exclusive @@ -8,11 +8,11 @@ tests.basic.BasicTests.test_consume_unique_consumers tests.basic.BasicTests.test_get tests.basic.BasicTests.test_qos_prefetch_size tests.basic.BasicTests.test_recover_requeue + tests.exchange.RecommendedTypesRuleTests.testTopic tests.exchange.RequiredInstancesRuleTests.testAmqTopic -tests.message.MessageTests.test_qos_prefetch_count -tests.message.MessageTests.test_ack -tests.message.MessageTests.test_get -tests.message.MessageTests.test_qos_prefetch_size -tests.message.MessageTests.test_recover_requeue +tests.message.MessageTests.test_checkpoint +tests.message.MessageTests.test_reject + +tests.broker.BrokerTests.test_ping_pong diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index ea6aa7901a..e548ef0e99 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -76,7 +76,8 @@ class Client: self.locale = locale self.tune_params = tune_params - self.conn = Connection(connect(self.host, self.port), self.spec) + self.socket = connect(self.host, self.port) + self.conn = Connection(self.socket, self.spec) self.peer = Peer(self.conn, ClientDelegate(self), self.opened) self.conn.init() @@ -90,6 +91,9 @@ class Client: def opened(self, ch): ch.references = References() + def close(self): + self.socket.close() + class ClientDelegate(Delegate): def __init__(self, client): @@ -112,9 +116,8 @@ class ClientDelegate(Delegate): def message_transfer(self, ch, msg): if isinstance(msg.body, ReferenceId): - self.client.queue(msg.destination).put(ch.references.get(msg.body.id)) - else: - self.client.queue(msg.destination).put(msg) + msg.reference = ch.references.get(msg.body.id) + self.client.queue(msg.destination).put(msg) def message_open(self, ch, msg): ch.references.open(msg.reference) diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 0785fe8774..cdfa2c2dc0 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -53,6 +53,9 @@ class SockIO: def flush(self): pass + def close(self): + self.sock.shutdown(socket.SHUT_RDWR) + def connect(host, port): sock = socket.socket() sock.connect((host, port)) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 6c8c6647c9..28db20f187 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -97,9 +97,12 @@ class Peer: self.fatal() def close(self, reason): + # We must close the delegate first because closing channels + # may wake up waiting threads and we don't want them to see + # the delegate as open. + self.delegate.close(reason) for ch in self.channels.values(): ch.close(reason) - self.delegate.close(reason) def writer(self): try: @@ -144,7 +147,7 @@ class Requester: self.write(frame, content) def receive(self, channel, frame): - listener = self.outstanding.pop(frame.id) + listener = self.outstanding.pop(frame.request_id) listener(channel, frame) class Responder: @@ -178,8 +181,8 @@ class Channel: self.requester = Requester(self.write) self.responder = Responder(self.write) - # XXX: better switch - self.reliable = False + # Use reliable framing if version == 0-9. + self.reliable = (spec.major == 0 and spec.minor == 9) self.synchronous = True def close(self, reason): diff --git a/qpid/python/qpid/reference.py b/qpid/python/qpid/reference.py index d357560390..48ecb67656 100644 --- a/qpid/python/qpid/reference.py +++ b/qpid/python/qpid/reference.py @@ -111,7 +111,7 @@ class References: self.get(id).close() self.lock.acquire() try: - del map[id] + self.map.pop(id) finally: self.lock.release() diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index e430c45b96..4f0661bcbc 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -79,12 +79,11 @@ class Spec(Metadata): PRINT=["major", "minor", "file"] - def __init__(self, major, minor, file, errata): + def __init__(self, major, minor, file): Metadata.__init__(self) self.major = major self.minor = minor self.file = file - self.errata = errata self.constants = SpecContainer() self.classes = SpecContainer() # methods indexed by classname_methname @@ -275,7 +274,7 @@ def load_fields(nd, l, domains): def load(specfile, *errata): doc = xmlutil.parse(specfile) spec_root = doc["amqp"][0] - spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile, errata) + spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile) for root in [spec_root] + map(lambda x: xmlutil.parse(x)["amqp"][0], errata): # constants diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index dcbf0ed91c..05641bce7e 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -26,6 +26,7 @@ import qpid.client, qpid.spec import Queue from getopt import getopt, GetoptError from qpid.content import Content +from qpid.message import Message def findmodules(root): """Find potential python modules under directory root""" @@ -56,15 +57,16 @@ class TestRunner: run-tests [options] [test*] The name of a test is package.module.ClassName.testMethod Options: - -?/-h/--help : this message + -?/-h/--help : this message -s/--spec : URL of AMQP XML specification or one of these abbreviations: 0-8 - use the default 0-8 specification. 0-9 - use the default 0-9 specification. + -e/--errata : file containing amqp XML errata -b/--broker [[/]@][:] : broker to connect to - -v/--verbose : verbose - lists tests as they are run. - -d/--debug : enable debug logging. - -i/--ignore : ignore the named test. - -I/--ignore-file : file containing patterns to ignore. + -v/--verbose : verbose - lists tests as they are run. + -d/--debug : enable debug logging. + -i/--ignore : ignore the named test. + -I/--ignore-file : file containing patterns to ignore. """ sys.exit(1) @@ -103,24 +105,27 @@ Options: for opt, value in opts: if opt in ("-?", "-h", "--help"): self._die() if opt in ("-s", "--spec"): self.specfile = value + if opt in ("-e", "--errata"): self.errata.append(value) if opt in ("-b", "--broker"): self.setBroker(value) if opt in ("-v", "--verbose"): self.verbose = 2 if opt in ("-d", "--debug"): logging.basicConfig(level=logging.DEBUG) if opt in ("-i", "--ignore"): self.ignore.append(value) if opt in ("-I", "--ignore-file"): self.ignoreFile(value) + # Abbreviations for default settings. if (self.specfile == "0-8"): - self.specfile = "../specs/amqp.0-8.xml" + self.specfile = "../specs/amqp.0-8.xml" if (self.specfile == "0-9"): - self.specfile = "../specs/amqp.0-9.xml" - self.errata = ["../specs/amqp-errata.0-9.xml"] + self.specfile = "../specs/amqp.0-9.xml" + self.errata.append("../specs/amqp-errata.0-9.xml") + if (self.specfile == None): + self._die("No XML specification provided") print "Using specification from:", self.specfile self.spec = qpid.spec.load(self.specfile, *self.errata) if len(self.tests) == 0: if self.use08spec(): - testdir="tests_0-8" + self.tests=findmodules("tests_0-8") else: - testdir="tests" - self.tests=findmodules(testdir) + self.tests=findmodules("tests") def testSuite(self): class IgnoringTestSuite(unittest.TestSuite): @@ -137,7 +142,11 @@ Options: self._parseargs(args) runner = unittest.TextTestRunner(descriptions=False, verbosity=self.verbose) - result = runner.run(self.testSuite()) + try: + result = runner.run(self.testSuite()) + except: + print "Unhandled error in test:", sys.exc_info() + if (self.ignore): print "=======================================" print "NOTE: the following tests were ignored:" @@ -181,10 +190,18 @@ class TestBase(unittest.TestCase): self.channel.channel_open() def tearDown(self): - for ch, q in self.queues: - ch.queue_delete(queue=q) - for ch, ex in self.exchanges: - ch.exchange_delete(exchange=ex) + try: + for ch, q in self.queues: + ch.queue_delete(queue=q) + for ch, ex in self.exchanges: + ch.exchange_delete(exchange=ex) + except: + print "Error on tearDown:", sys.exc_info() + + if not self.client.closed: + self.client.channel(0).connection_close(reply_code=200) + else: + self.client.close() def connect(self, *args, **keys): """Create a new connction, return the Client object""" @@ -261,13 +278,15 @@ class TestBase(unittest.TestCase): """ self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) - def assertChannelException(self, expectedCode, message): + def assertChannelException(self, expectedCode, message): + if not isinstance(message, Message): self.fail("expected channel_close method") self.assertEqual("channel", message.method.klass.name) self.assertEqual("close", message.method.name) self.assertEqual(expectedCode, message.reply_code) def assertConnectionException(self, expectedCode, message): + if not isinstance(message, Message): self.fail("expected connection_close method") self.assertEqual("connection", message.method.klass.name) self.assertEqual("close", message.method.name) self.assertEqual(expectedCode, message.reply_code) diff --git a/qpid/python/tests/message.py b/qpid/python/tests/message.py index 916a9825bd..d5f5d4dbc2 100644 --- a/qpid/python/tests/message.py +++ b/qpid/python/tests/message.py @@ -171,8 +171,7 @@ class MessageTests(TestBase): self.assertEqual("Four", msg4.body) self.assertEqual("Five", msg5.body) - msg1.ok() - msg2.ok() + msg1.ok(batchoffset=1)#One and Two msg4.ok() channel.message_recover(requeue=False) @@ -216,9 +215,8 @@ class MessageTests(TestBase): self.assertEqual("Four", msg4.body) self.assertEqual("Five", msg5.body) - msg1.ok() #One - msg2.ok() #Two - msg4.ok() #Two + msg1.ok(batchoffset=1) #One and Two + msg4.ok() #Four channel.message_cancel(destination="consumer_tag") channel.message_consume(queue="test-requeue", destination="consumer_tag") @@ -263,11 +261,9 @@ class MessageTests(TestBase): channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) #only 5 messages should have been delivered: - msgs = [] for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.append(msg) try: extra = queue.get(timeout=1) self.fail("Got unexpected 6th message in original queue: " + extra.body) @@ -275,18 +271,13 @@ class MessageTests(TestBase): #ack messages and check that the next set arrive ok: #todo: once batching is implmented, send a single response for all messages - for msg in msgs: - msg.ok() - del msgs + msg.ok(batchoffset=-4)#1-5 for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.append(msg) - for msg in msgs: - msg.ok() - del msgs + msg.ok(batchoffset=-4)#6-10 try: extra = queue.get(timeout=1) @@ -313,12 +304,9 @@ class MessageTests(TestBase): channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) #only 5 messages should have been delivered (i.e. 45 bytes worth): - msgs = [] for i in range(1, 6): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - print "Got Message %d" % i - msgs.append(msg) try: extra = queue.get(timeout=1) @@ -326,18 +314,13 @@ class MessageTests(TestBase): except Empty: None #ack messages and check that the next set arrive ok: - for msg in msgs: - msg.ok() - del msgs + msg.ok(batchoffset=-4)#1-5 for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msgs.append(msg) - for msg in msgs: - msg.ok() - del msgs + msg.ok(batchoffset=-4)#6-10 try: extra = queue.get(timeout=1) @@ -383,14 +366,13 @@ class MessageTests(TestBase): reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "ok") - self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) - reply.ok() - - #todo: when batching is available, test ack multiple - #if(i == 13): - # channel.message_ack(delivery_tag=reply.delivery_tag, multiple=True) - #if(i in [15, 17, 19]): - # channel.message_ack(delivery_tag=reply.delivery_tag) + msg = self.client.queue(tag).get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + if (i==13): + msg.ok(batchoffset=-2)#11, 12 & 13 + if(i in [15, 17, 19]): + msg.ok() reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") @@ -479,8 +461,9 @@ class MessageTests(TestBase): queue = other.queue("c1") msg = queue.get(timeout=1) - self.assertTrue(isinstance(msg, Reference)) - self.assertEquals(data, msg.get_complete()) + self.assertTrue(isinstance(msg.body, ReferenceId)) + self.assertTrue(msg.reference) + self.assertEquals(data, msg.reference.get_complete()) def test_reference_completion(self): """ @@ -511,12 +494,165 @@ class MessageTests(TestBase): #first, wait for the ok for the transfer ack.get_response(timeout=1) + self.assertDataEquals(channel, queue.get(timeout=1), "abcd") + + def test_reference_multi_transfer(self): + """ + Test that multiple transfer requests for the same reference are + correctly handled. + """ + channel = self.channel + #declare and consume from two queues + channel.queue_declare(queue="q-one", exclusive=True) + channel.queue_declare(queue="q-two", exclusive=True) + channel.message_consume(queue="q-one", destination="q-one") + channel.message_consume(queue="q-two", destination="q-two") + queue1 = self.client.queue("q-one") + queue2 = self.client.queue("q-two") + + #transfer a single ref to both queues (in separate commands) + channel.message_open(reference="my-ref") + channel.synchronous = False + ack1 = channel.message_transfer(routing_key="q-one", body=ReferenceId("my-ref")) + channel.message_append(reference="my-ref", bytes="my data") + ack2 = channel.message_transfer(routing_key="q-two", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + #check that both queues have the message + self.assertDataEquals(channel, queue1.get(timeout=1), "my data") + self.assertDataEquals(channel, queue2.get(timeout=1), "my data") + self.assertEmpty(queue1) + self.assertEmpty(queue2) + + #transfer a single ref to the same queue twice (in separate commands) + channel.message_open(reference="my-ref") + channel.synchronous = False + ack1 = channel.message_transfer(routing_key="q-one", message_id="abc", body=ReferenceId("my-ref")) + channel.message_append(reference="my-ref", bytes="second message") + ack2 = channel.message_transfer(routing_key="q-one", message_id="xyz", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + msg1 = queue1.get(timeout=1) + msg2 = queue1.get(timeout=1) + #order is undefined + if msg1.message_id == "abc": + self.assertEquals(msg2.message_id, "xyz") + else: + self.assertEquals(msg1.message_id, "xyz") + self.assertEquals(msg2.message_id, "abc") + + #would be legal for the incoming messages to be transfered + #inline or by reference in any combination + + if isinstance(msg1.body, ReferenceId): + self.assertEquals("second message", msg1.reference.get_complete()) + if isinstance(msg2.body, ReferenceId): + if msg1.body != msg2.body: + self.assertEquals("second message", msg2.reference.get_complete()) + #else ok, as same ref as msg1 + else: + self.assertEquals("second message", msg1.body) + if isinstance(msg2.body, ReferenceId): + self.assertEquals("second message", msg2.reference.get_complete()) + else: + self.assertEquals("second message", msg2.body) + + self.assertEmpty(queue1) + + def test_reference_unopened_on_append_error(self): + channel = self.channel + try: + channel.message_append(reference="unopened") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_unopened_on_close_error(self): + channel = self.channel + try: + channel.message_close(reference="unopened") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_unopened_on_transfer_error(self): + channel = self.channel + try: + channel.message_transfer(body=ReferenceId("unopened")) + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_already_opened_error(self): + channel = self.channel + channel.message_open(reference="a") + try: + channel.message_open(reference="a") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_empty_reference(self): + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", message_id="empty-msg", body=ReferenceId(refId)) + channel.synchronous = True + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + msg = queue.get(timeout=1) - if isinstance(msg, Reference): - #should we force broker to deliver as reference by frame - #size limit? or test that separately? for compliance, - #allowing either seems best for now... - data = msg.get_complete() + self.assertEquals(msg.message_id, "empty-msg") + self.assertDataEquals(channel, msg, "") + + def test_reject(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + + channel.message_consume(queue = "q", destination = "consumer") + channel.message_transfer(routing_key = "q", body="blah, blah") + msg = self.client.queue("consumer").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + channel.message_cancel(destination = "consumer") + msg.reject() + + channel.message_consume(queue = "q", destination = "checker") + msg = self.client.queue("checker").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + + def test_checkpoint(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + + channel.message_open(reference="my-ref") + channel.message_append(reference="my-ref", bytes="abcdefgh") + channel.message_append(reference="my-ref", bytes="ijklmnop") + channel.message_checkpoint(reference="my-ref", identifier="my-checkpoint") + channel.channel_close() + + channel = self.client.channel(2) + channel.channel_open() + channel.message_consume(queue = "q", destination = "consumer") + offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value + self.assertEquals(offset, 16) + channel.message_append(reference="my-ref", bytes="qrstuvwxyz") + channel.synchronous = False + channel.message_transfer(routing_key="q-one", message_id="abcd", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz") + self.assertEmpty(self.client.queue("consumer")) + + + def assertDataEquals(self, channel, msg, expected): + if isinstance(msg.body, ReferenceId): + data = msg.reference.get_complete() else: data = msg.body self.assertEquals("abcdefghijkl", data) diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py index 55a5eaeade..0f6b4f5cd1 100644 --- a/qpid/python/tests/tx.py +++ b/qpid/python/tests/tx.py @@ -163,7 +163,8 @@ class TxTests(TestBase): for i in range(1, 5): msg = queue_a.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok() + + msg.ok(batchoffset=-3) channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) queue_b = self.client.queue("sub_b") -- cgit v1.2.1 From 6529db15d4265531005989ae4930c9ef415e3bdd Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 19 Mar 2007 20:44:38 +0000 Subject: * python/cpp_failing_0-8.txt,java_failing_0-8.txt: updated to current failures * java_failing.txt,cpp_failing.txt: renamed to *_failing_0-9.txt for clarity. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520098 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing.txt | 4 ---- qpid/python/cpp_failing_0-8.txt | 0 qpid/python/cpp_failing_0-9.txt | 4 ++++ qpid/python/java_failing.txt | 18 ------------------ qpid/python/java_failing_0-9.txt | 18 ++++++++++++++++++ 5 files changed, 22 insertions(+), 22 deletions(-) delete mode 100644 qpid/python/cpp_failing.txt create mode 100644 qpid/python/cpp_failing_0-8.txt create mode 100644 qpid/python/cpp_failing_0-9.txt delete mode 100644 qpid/python/java_failing.txt create mode 100644 qpid/python/java_failing_0-9.txt (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing.txt b/qpid/python/cpp_failing.txt deleted file mode 100644 index 69c7cf3f5a..0000000000 --- a/qpid/python/cpp_failing.txt +++ /dev/null @@ -1,4 +0,0 @@ -tests.message.MessageTests.test_checkpoint -tests.message.MessageTests.test_reference_large -tests.message.MessageTests.test_reject -tests.basic.BasicTests.test_get diff --git a/qpid/python/cpp_failing_0-8.txt b/qpid/python/cpp_failing_0-8.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qpid/python/cpp_failing_0-9.txt b/qpid/python/cpp_failing_0-9.txt new file mode 100644 index 0000000000..69c7cf3f5a --- /dev/null +++ b/qpid/python/cpp_failing_0-9.txt @@ -0,0 +1,4 @@ +tests.message.MessageTests.test_checkpoint +tests.message.MessageTests.test_reference_large +tests.message.MessageTests.test_reject +tests.basic.BasicTests.test_get diff --git a/qpid/python/java_failing.txt b/qpid/python/java_failing.txt deleted file mode 100644 index 7252d0f496..0000000000 --- a/qpid/python/java_failing.txt +++ /dev/null @@ -1,18 +0,0 @@ -ntests.basic.BasicTests.test_qos_prefetch_count -tests.basic.BasicTests.test_ack -tests.basic.BasicTests.test_cancel -tests.basic.BasicTests.test_consume_exclusive -tests.basic.BasicTests.test_consume_no_local -tests.basic.BasicTests.test_consume_queue_errors -tests.basic.BasicTests.test_consume_unique_consumers -tests.basic.BasicTests.test_get -tests.basic.BasicTests.test_qos_prefetch_size -tests.basic.BasicTests.test_recover_requeue - -tests.exchange.RecommendedTypesRuleTests.testTopic -tests.exchange.RequiredInstancesRuleTests.testAmqTopic - -tests.message.MessageTests.test_checkpoint -tests.message.MessageTests.test_reject - -tests.broker.BrokerTests.test_ping_pong diff --git a/qpid/python/java_failing_0-9.txt b/qpid/python/java_failing_0-9.txt new file mode 100644 index 0000000000..7252d0f496 --- /dev/null +++ b/qpid/python/java_failing_0-9.txt @@ -0,0 +1,18 @@ +ntests.basic.BasicTests.test_qos_prefetch_count +tests.basic.BasicTests.test_ack +tests.basic.BasicTests.test_cancel +tests.basic.BasicTests.test_consume_exclusive +tests.basic.BasicTests.test_consume_no_local +tests.basic.BasicTests.test_consume_queue_errors +tests.basic.BasicTests.test_consume_unique_consumers +tests.basic.BasicTests.test_get +tests.basic.BasicTests.test_qos_prefetch_size +tests.basic.BasicTests.test_recover_requeue + +tests.exchange.RecommendedTypesRuleTests.testTopic +tests.exchange.RequiredInstancesRuleTests.testAmqTopic + +tests.message.MessageTests.test_checkpoint +tests.message.MessageTests.test_reject + +tests.broker.BrokerTests.test_ping_pong -- cgit v1.2.1 From 1fb29f138da337b2b318355477b271dcf2fa13ca Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 21 Mar 2007 01:26:37 +0000 Subject: * cpp-0-9: svn copy of 0-9 branch cpp, will rename to cpp on next update. * cpp-0-9/gentools: independent copy of gentools for cpp. Temporary measure till /java and /gentools are at 0-9 * python/tests_0-9: tests from 0-9 branch. * python/qpid/testlib.py: Use tests_0-9 with 0-9 spec (default is still 0-8) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520690 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-9.txt | 2 +- qpid/python/qpid/testlib.py | 8 +- qpid/python/tests_0-9/__init__.py | 20 ++ qpid/python/tests_0-9/basic.py | 394 +++++++++++++++++++++++ qpid/python/tests_0-9/broker.py | 116 +++++++ qpid/python/tests_0-9/example.py | 94 ++++++ qpid/python/tests_0-9/exchange.py | 327 +++++++++++++++++++ qpid/python/tests_0-9/message.py | 651 ++++++++++++++++++++++++++++++++++++++ qpid/python/tests_0-9/queue.py | 306 ++++++++++++++++++ qpid/python/tests_0-9/testlib.py | 66 ++++ qpid/python/tests_0-9/tx.py | 188 +++++++++++ 11 files changed, 2166 insertions(+), 6 deletions(-) create mode 100644 qpid/python/tests_0-9/__init__.py create mode 100644 qpid/python/tests_0-9/basic.py create mode 100644 qpid/python/tests_0-9/broker.py create mode 100644 qpid/python/tests_0-9/example.py create mode 100644 qpid/python/tests_0-9/exchange.py create mode 100644 qpid/python/tests_0-9/message.py create mode 100644 qpid/python/tests_0-9/queue.py create mode 100644 qpid/python/tests_0-9/testlib.py create mode 100644 qpid/python/tests_0-9/tx.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-9.txt b/qpid/python/cpp_failing_0-9.txt index 69c7cf3f5a..5d74874d52 100644 --- a/qpid/python/cpp_failing_0-9.txt +++ b/qpid/python/cpp_failing_0-9.txt @@ -1,4 +1,4 @@ tests.message.MessageTests.test_checkpoint -tests.message.MessageTests.test_reference_large tests.message.MessageTests.test_reject tests.basic.BasicTests.test_get + diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 05641bce7e..ba2dbe9fc3 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -125,7 +125,7 @@ Options: if self.use08spec(): self.tests=findmodules("tests_0-8") else: - self.tests=findmodules("tests") + self.tests=findmodules("tests_0-9") def testSuite(self): class IgnoringTestSuite(unittest.TestSuite): @@ -142,16 +142,14 @@ Options: self._parseargs(args) runner = unittest.TextTestRunner(descriptions=False, verbosity=self.verbose) - try: - result = runner.run(self.testSuite()) - except: - print "Unhandled error in test:", sys.exc_info() + result = runner.run(self.testSuite()) if (self.ignore): print "=======================================" print "NOTE: the following tests were ignored:" for t in self.ignore: print t print "=======================================" + return result.wasSuccessful() def connect(self, host=None, port=None, spec=None, user=None, password=None, tune_params=None): diff --git a/qpid/python/tests_0-9/__init__.py b/qpid/python/tests_0-9/__init__.py new file mode 100644 index 0000000000..9a09d2d04f --- /dev/null +++ b/qpid/python/tests_0-9/__init__.py @@ -0,0 +1,20 @@ +# Do not delete - marks this directory as a python package. + +# +# 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. +# diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py new file mode 100644 index 0000000000..140576540a --- /dev/null +++ b/qpid/python/tests_0-9/basic.py @@ -0,0 +1,394 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BasicTests(TestBase): + """Tests for 'methods' on the amqp basic 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a") + channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local")) + channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local")) + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True) + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.basic_consume(consumer_tag="first", queue="test-queue-2") + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.basic_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.basic_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + try: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") + channel.basic_publish(routing_key="test-queue-4", content=Content("One")) + + #cancel should stop messages being delivered + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.content.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_cancel(consumer_tag="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True) + + reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) + queue = self.client.queue(reply.consumer_tag) + + channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True) + + subscription = channel.basic_consume(queue="test-requeue", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + channel.basic_publish(routing_key="test-requeue", content=Content("One")) + channel.basic_publish(routing_key="test-requeue", content=Content("Two")) + channel.basic_publish(routing_key="test-requeue", content=Content("Three")) + channel.basic_publish(routing_key="test-requeue", content=Content("Four")) + channel.basic_publish(routing_key="test-requeue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_cancel(consumer_tag=subscription.consumer_tag) + subscription2 = channel.basic_consume(queue="test-requeue") + queue2 = self.client.queue(subscription2.consumer_tag) + + channel.basic_recover(requeue=True) + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.content.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.content.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 5: + channel.basic_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.basic_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.content.body) + + def test_get(self): + """ + Test basic_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + #use basic_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + for i in range(11, 21): + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + if(i == 13): + channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) + if(i in [15, 17, 19]): + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + #recover(requeue=True) + channel.basic_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + channel.basic_recover(requeue=True) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") diff --git a/qpid/python/tests_0-9/broker.py b/qpid/python/tests_0-9/broker.py new file mode 100644 index 0000000000..a978993891 --- /dev/null +++ b/qpid/python/tests_0-9/broker.py @@ -0,0 +1,116 @@ +# +# 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. +# +from qpid.client import Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BrokerTests(TestBase): + """Tests for basic Broker functionality""" + + def test_ack_and_no_ack(self): + """ + First, this test tries to receive a message with a no-ack + consumer. Second, this test tries to explicitely receive and + acknowledge a message with an acknowledging consumer. + """ + ch = self.channel + self.queue_declare(ch, queue = "myqueue") + + # No ack consumer + ctag = "tag1" + ch.message_consume(queue = "myqueue", destination = ctag, no_ack = True) + body = "test no-ack" + ch.message_transfer(routing_key = "myqueue", body = body) + msg = self.client.queue(ctag).get(timeout = 5) + self.assert_(msg.body == body) + + # Acknowledging consumer + self.queue_declare(ch, queue = "otherqueue") + ctag = "tag2" + ch.message_consume(queue = "otherqueue", destination = ctag, no_ack = False) + body = "test ack" + ch.message_transfer(routing_key = "otherqueue", body = body) + msg = self.client.queue(ctag).get(timeout = 5) + msg.ok() + self.assert_(msg.body == body) + + def test_simple_delivery_immediate(self): + """ + Test simple message delivery where consume is issued before publish + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + consumer_tag = "tag1" + channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + queue = self.client.queue(consumer_tag) + + body = "Immediate Delivery" + channel.message_transfer(destination="test-exchange", routing_key="key", body=body, immediate=True) + msg = queue.get(timeout=5) + self.assert_(msg.body == body) + + # TODO: Ensure we fail if immediate=True and there's no consumer. + + + def test_simple_delivery_queued(self): + """ + Test basic message delivery where publish is issued before consume + (i.e. requires queueing of the message) + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + body = "Queued Delivery" + channel.message_transfer(destination="test-exchange", routing_key="key", body=body) + + consumer_tag = "tag1" + channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + queue = self.client.queue(consumer_tag) + msg = queue.get(timeout=5) + self.assert_(msg.body == body) + + def test_invalid_channel(self): + channel = self.client.channel(200) + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for invalid channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) + + def test_closed_channel(self): + channel = self.client.channel(200) + channel.channel_open() + channel.channel_close() + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for closed channel") + except Closed, e: + if isinstance(e.args[0], str): self.fail(e) + self.assertConnectionException(504, e.args[0]) + + def test_ping_pong(self): + channel = self.channel + reply = channel.channel_ping() + self.assertEqual(reply.method.klass.name, "channel") + self.assertEqual(reply.method.name, "ok") + #todo: provide a way to get notified of incoming pongs... diff --git a/qpid/python/tests_0-9/example.py b/qpid/python/tests_0-9/example.py new file mode 100644 index 0000000000..7ab4cc7d0a --- /dev/null +++ b/qpid/python/tests_0-9/example.py @@ -0,0 +1,94 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExampleTest (TestBase): + """ + An example Qpid test, illustrating the unittest frameowkr and the + python Qpid client. The test class must inherit TestCase. The + test code uses the Qpid client to interact with a qpid broker and + verify it behaves as expected. + """ + + def test_example(self): + """ + An example test. Note that test functions must start with 'test_' + to be recognized by the test framework. + """ + + # By inheriting TestBase, self.client is automatically connected + # and self.channel is automatically opened as channel(1) + # Other channel methods mimic the protocol. + channel = self.channel + + # Now we can send regular commands. If you want to see what the method + # arguments mean or what other commands are available, you can use the + # python builtin help() method. For example: + #help(chan) + #help(chan.exchange_declare) + + # If you want browse the available protocol methods without being + # connected to a live server you can use the amqp-doc utility: + # + # Usage amqp-doc [] [ ... ] + # + # Options: + # -e, --regexp use regex instead of glob when matching + + # Now that we know what commands are available we can use them to + # interact with the server. + + # Here we use ordinal arguments. + self.exchange_declare(channel, 0, "test", "direct") + + # Here we use keyword arguments. + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") + + # Call Channel.basic_consume to register as a consumer. + # All the protocol methods return a message object. The message object + # has fields corresponding to the reply method fields, plus a content + # field that is filled if the reply includes content. In this case the + # interesting field is the consumer_tag. + channel.message_consume(queue="test-queue", destination="consumer_tag") + + # We can use the Client.queue(...) method to access the queue + # corresponding to our consumer_tag. + queue = self.client.queue("consumer_tag") + + # Now lets publish a message and see if our consumer gets it. To do + # this we need to import the Content class. + body = "Hello World!" + channel.message_transfer(destination="test", + routing_key="key", + body = body) + + # Now we'll wait for the message to arrive. We can use the timeout + # argument in case the server hangs. By default queue.get() will wait + # until a message arrives or the connection to the server dies. + msg = queue.get(timeout=10) + + # And check that we got the right response with assertEqual + self.assertEqual(body, msg.body) + + # Now acknowledge the message. + msg.ok() + diff --git a/qpid/python/tests_0-9/exchange.py b/qpid/python/tests_0-9/exchange.py new file mode 100644 index 0000000000..3a47ffff8c --- /dev/null +++ b/qpid/python/tests_0-9/exchange.py @@ -0,0 +1,327 @@ +# +# 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. +# + +""" +Tests for exchange behaviour. + +Test classes ending in 'RuleTests' are derived from rules in amqp.xml. +""" + +import Queue, logging +from qpid.testlib import TestBase +from qpid.content import Content +from qpid.client import Closed + + +class StandardExchangeVerifier: + """Verifies standard exchange behavior. + + Used as base class for classes that test standard exchanges.""" + + def verifyDirectExchange(self, ex): + """Verify that ex behaves like a direct exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") + self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") + try: + self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") + self.fail("Expected Empty exception") + except Queue.Empty: None # Expected + + def verifyFanOutExchange(self, ex): + """Verify that ex behaves like a fanout exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex) + self.queue_declare(queue="p") + self.channel.queue_bind(queue="p", exchange=ex) + for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) + + def verifyTopicExchange(self, ex): + """Verify that ex behaves like a topic exchange""" + self.queue_declare(queue="a") + self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + q = self.consume("a") + self.assertPublishGet(q, ex, "a.b.x") + self.assertPublishGet(q, ex, "a.x.b.x") + self.assertPublishGet(q, ex, "a.x.x.b.x") + # Shouldn't match + self.channel.message_transfer(destination=ex, routing_key="a.b", body="") + self.channel.message_transfer(destination=ex, routing_key="a.b.x.y", body="") + self.channel.message_transfer(destination=ex, routing_key="x.a.b.x", body="") + self.channel.message_transfer(destination=ex, routing_key="a.b", body="") + self.assert_(q.empty()) + + def verifyHeadersExchange(self, ex): + """Verify that ex is a headers exchange""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) + q = self.consume("q") + headers = {"name":"fred", "age":3} + self.assertPublishGet(q, exchange=ex, properties=headers) + self.channel.message_transfer(destination=ex, body="") # No headers, won't deliver + self.assertEmpty(q); + + +class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server SHOULD implement these standard exchange types: topic, headers. + + Client attempts to declare an exchange with each of these standard types. + """ + + def testDirect(self): + """Declare and test a direct exchange""" + self.exchange_declare(0, exchange="d", type="direct") + self.verifyDirectExchange("d") + + def testFanout(self): + """Declare and test a fanout exchange""" + self.exchange_declare(0, exchange="f", type="fanout") + self.verifyFanOutExchange("f") + + def testTopic(self): + """Declare and test a topic exchange""" + self.exchange_declare(0, exchange="t", type="topic") + self.verifyTopicExchange("t") + + def testHeaders(self): + """Declare and test a headers exchange""" + self.exchange_declare(0, exchange="h", type="headers") + self.verifyHeadersExchange("h") + + +class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST, in each virtual host, pre-declare an exchange instance + for each standard exchange type that it implements, where the name of the + exchange instance is amq. followed by the exchange type name. + + Client creates a temporary queue and attempts to bind to each required + exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if + those types are defined). + """ + def testAmqDirect(self): self.verifyDirectExchange("amq.direct") + + def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") + + def testAmqTopic(self): self.verifyTopicExchange("amq.topic") + + def testAmqMatch(self): self.verifyHeadersExchange("amq.match") + +class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST predeclare a direct exchange to act as the default exchange + for content Publish methods and for default queue bindings. + + Client checks that the default exchange is active by specifying a queue + binding with no exchange name, and publishing a message with a suitable + routing key but without specifying the exchange name, then ensuring that + the message arrives in the queue correctly. + """ + def testDefaultExchange(self): + # Test automatic binding by queue name. + self.queue_declare(queue="d") + self.assertPublishConsume(queue="d", routing_key="d") + # Test explicit bind to default queue + self.verifyDirectExchange("") + + +# TODO aconway 2006-09-27: Fill in empty tests: + +class DefaultAccessRuleTests(TestBase): + """ + The server MUST NOT allow clients to access the default exchange except + by specifying an empty exchange name in the Queue.Bind and content Publish + methods. + """ + +class ExtensionsRuleTests(TestBase): + """ + The server MAY implement other exchange types as wanted. + """ + + +class DeclareMethodMinimumRuleTests(TestBase): + """ + The server SHOULD support a minimum of 16 exchanges per virtual host and + ideally, impose no limit except as defined by available resources. + + The client creates as many exchanges as it can until the server reports + an error; the number of exchanges successfuly created must be at least + sixteen. + """ + + +class DeclareMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access to + the realm in which the exchange exists or will be created, or "passive" + access if the if-exists flag is set. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeclareMethodExchangeFieldReservedRuleTests(TestBase): + """ + Exchange names starting with "amq." are reserved for predeclared and + standardised exchanges. The client MUST NOT attempt to create an exchange + starting with "amq.". + + + """ + + +class DeclareMethodTypeFieldTypedRuleTests(TestBase): + """ + Exchanges cannot be redeclared with different types. The client MUST not + attempt to redeclare an existing exchange with a different type than used + in the original Exchange.Declare method. + + + """ + + +class DeclareMethodTypeFieldSupportRuleTests(TestBase): + """ + The client MUST NOT attempt to create an exchange with a type that the + server does not support. + + + """ + + +class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): + """ + If set, and the exchange does not already exist, the server MUST raise a + channel exception with reply code 404 (not found). + """ + def test(self): + try: + self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) + self.fail("Expected 404 for passive declaration of unknown exchange.") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + +class DeclareMethodDurableFieldSupportRuleTests(TestBase): + """ + The server MUST support both durable and transient exchanges. + + + """ + + +class DeclareMethodDurableFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the durable field if the exchange already exists. + + + """ + + +class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the auto-delete field if the exchange already + exists. + + + """ + + +class DeleteMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access + rights to the exchange's access realm. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeleteMethodExchangeFieldExistsRuleTests(TestBase): + """ + The client MUST NOT attempt to delete an exchange that does not exist. + """ + + +class HeadersExchangeTests(TestBase): + """ + Tests for headers exchange functionality. + """ + def setUp(self): + TestBase.setUp(self) + self.queue_declare(queue="q") + self.q = self.consume("q") + + def myAssertPublishGet(self, headers): + self.assertPublishGet(self.q, exchange="amq.match", properties=headers) + + def myBasicPublish(self, headers): + self.channel.message_transfer(destination="amq.match", body="foobar", application_headers=headers) + + def testMatchAll(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) + + # None of these should match + self.myBasicPublish({}) + self.myBasicPublish({"name":"barney"}) + self.myBasicPublish({"name":10}) + self.myBasicPublish({"name":"fred", "age":2}) + self.assertEmpty(self.q) + + def testMatchAny(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred"}) + self.myAssertPublishGet({"name":"fred", "ignoreme":10}) + self.myAssertPublishGet({"ignoreme":10, "age":3}) + + # Wont match + self.myBasicPublish({}) + self.myBasicPublish({"irrelevant":0}) + self.assertEmpty(self.q) + + +class MiscellaneousErrorsTests(TestBase): + """ + Test some miscellaneous error conditions + """ + def testTypeNotKnown(self): + try: + self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") + self.fail("Expected 503 for declaration of unknown exchange type.") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def testDifferentDeclaredType(self): + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + try: + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + self.fail("Expected 530 for redeclaration of exchange with different type.") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + #cleanup + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + c2.exchange_delete(exchange="test_different_declared_type_exchange") + diff --git a/qpid/python/tests_0-9/message.py b/qpid/python/tests_0-9/message.py new file mode 100644 index 0000000000..8da9978792 --- /dev/null +++ b/qpid/python/tests_0-9/message.py @@ -0,0 +1,651 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from qpid.reference import Reference, ReferenceId + +class MessageTests(TestBase): + """Tests for 'methods' on the amqp message 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.message_consume(destination="local_included", queue="test-queue-1a") + channel.message_consume(destination="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local") + channel.message_transfer(routing_key="test-queue-1b", body="consume_no_local") + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.message_consume(destination="first", queue="test-queue-2", exclusive=True) + try: + channel.message_consume(destination="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.message_consume(destination="first", queue="test-queue-2") + try: + channel.message_consume(destination="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.message_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.message_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.message_consume(destination="first", queue="test-queue-3") + try: + channel.message_consume(destination="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.message_consume(destination="my-consumer", queue="test-queue-4") + channel.message_transfer(routing_key="test-queue-4", body="One") + + #cancel should stop messages being delivered + channel.message_cancel(destination="my-consumer") + channel.message_transfer(routing_key="test-queue-4", body="Two") + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.message_cancel(destination="my-consumer") + channel.message_cancel(destination="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True) + + channel.message_consume(queue="test-ack-queue", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(routing_key="test-ack-queue", body="One") + channel.message_transfer(routing_key="test-ack-queue", body="Two") + channel.message_transfer(routing_key="test-ack-queue", body="Three") + channel.message_transfer(routing_key="test-ack-queue", body="Four") + channel.message_transfer(routing_key="test-ack-queue", body="Five") + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + msg1.ok(batchoffset=1)#One and Two + msg4.ok() + + channel.message_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True) + + channel.message_consume(queue="test-requeue", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(routing_key="test-requeue", body="One") + channel.message_transfer(routing_key="test-requeue", body="Two") + channel.message_transfer(routing_key="test-requeue", body="Three") + channel.message_transfer(routing_key="test-requeue", body="Four") + channel.message_transfer(routing_key="test-requeue", body="Five") + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + msg1.ok(batchoffset=1) #One and Two + msg4.ok() #Four + + channel.message_cancel(destination="consumer_tag") + channel.message_consume(queue="test-requeue", destination="consumer_tag") + queue2 = self.client.queue("consumer_tag") + + channel.message_recover(requeue=True) + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) + + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.message_consume(queue="test-prefetch-count", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + #set prefetch to 5: + channel.message_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + #todo: once batching is implmented, send a single response for all messages + msg.ok(batchoffset=-4)#1-5 + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + msg.ok(batchoffset=-4)#6-10 + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.message_consume(queue="test-prefetch-size", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.message_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + msg.ok(batchoffset=-4)#1-5 + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + msg.ok(batchoffset=-4)#6-10 + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.message_transfer(routing_key="test-prefetch-size", body=large) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.body) + + def test_get(self): + """ + Test message_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.message_transfer(routing_key="test-get", body="Message %d" % i) + + #use message_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + tag = "queue %d" % i + reply = channel.message_get(no_ack=True, queue="test-get", destination=tag) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "ok") + self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.message_transfer(routing_key="test-get", body="Message %d" % i) + + for i in range(11, 21): + tag = "queue %d" % i + reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "ok") + msg = self.client.queue(tag).get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + if (i==13): + msg.ok(batchoffset=-2)#11, 12 & 13 + if(i in [15, 17, 19]): + msg.ok() + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + #recover(requeue=True) + channel.message_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + tag = "queue %d" % i + reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "ok") + msg = self.client.queue(tag).get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() + #channel.message_ack(delivery_tag=reply.delivery_tag) + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + channel.message_recover(requeue=True) + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + def test_reference_simple(self): + """ + Test basic ability to handle references + """ + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.message_append(reference=refId, bytes="abcd") + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) + channel.synchronous = True + + channel.message_append(reference=refId, bytes="efgh") + channel.message_append(reference=refId, bytes="ijkl") + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + self.assertDataEquals(channel, queue.get(timeout=1), "abcdefghijkl") + + + def test_reference_large(self): + """ + Test basic ability to handle references whose content exceeds max frame size + """ + channel = self.channel + self.queue_declare(queue="ref_queue") + + #generate a big data string (> max frame size of consumer): + data = "0123456789" + for i in range(0, 10): + data += data + #send it inline + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=data) + channel.synchronous = True + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + #create a new connection for consumer, with specific max frame size (< data) + other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0}) + ch2 = other.channel(1) + ch2.channel_open() + ch2.message_consume(queue="ref_queue", destination="c1") + queue = other.queue("c1") + + msg = queue.get(timeout=1) + self.assertTrue(isinstance(msg.body, ReferenceId)) + self.assertTrue(msg.reference) + self.assertEquals(data, msg.reference.get_complete()) + + def test_reference_completion(self): + """ + Test that reference transfer are not deemed complete until + closed (therefore are not acked or routed until that point) + """ + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.message_append(reference=refId, bytes="abcd") + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) + channel.synchronous = True + + try: + msg = queue.get(timeout=1) + self.fail("Got unexpected message on queue: " + msg) + except Empty: None + + self.assertTrue(not ack.is_complete()) + + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + self.assertDataEquals(channel, queue.get(timeout=1), "abcd") + + def test_reference_multi_transfer(self): + """ + Test that multiple transfer requests for the same reference are + correctly handled. + """ + channel = self.channel + #declare and consume from two queues + channel.queue_declare(queue="q-one", exclusive=True) + channel.queue_declare(queue="q-two", exclusive=True) + channel.message_consume(queue="q-one", destination="q-one") + channel.message_consume(queue="q-two", destination="q-two") + queue1 = self.client.queue("q-one") + queue2 = self.client.queue("q-two") + + #transfer a single ref to both queues (in separate commands) + channel.message_open(reference="my-ref") + channel.synchronous = False + ack1 = channel.message_transfer(routing_key="q-one", body=ReferenceId("my-ref")) + channel.message_append(reference="my-ref", bytes="my data") + ack2 = channel.message_transfer(routing_key="q-two", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + #check that both queues have the message + self.assertDataEquals(channel, queue1.get(timeout=1), "my data") + self.assertDataEquals(channel, queue2.get(timeout=1), "my data") + self.assertEmpty(queue1) + self.assertEmpty(queue2) + + #transfer a single ref to the same queue twice (in separate commands) + channel.message_open(reference="my-ref") + channel.synchronous = False + ack1 = channel.message_transfer(routing_key="q-one", message_id="abc", body=ReferenceId("my-ref")) + channel.message_append(reference="my-ref", bytes="second message") + ack2 = channel.message_transfer(routing_key="q-one", message_id="xyz", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + msg1 = queue1.get(timeout=1) + msg2 = queue1.get(timeout=1) + #order is undefined + if msg1.message_id == "abc": + self.assertEquals(msg2.message_id, "xyz") + else: + self.assertEquals(msg1.message_id, "xyz") + self.assertEquals(msg2.message_id, "abc") + + #would be legal for the incoming messages to be transfered + #inline or by reference in any combination + + if isinstance(msg1.body, ReferenceId): + self.assertEquals("second message", msg1.reference.get_complete()) + if isinstance(msg2.body, ReferenceId): + if msg1.body != msg2.body: + self.assertEquals("second message", msg2.reference.get_complete()) + #else ok, as same ref as msg1 + else: + self.assertEquals("second message", msg1.body) + if isinstance(msg2.body, ReferenceId): + self.assertEquals("second message", msg2.reference.get_complete()) + else: + self.assertEquals("second message", msg2.body) + + self.assertEmpty(queue1) + + def test_reference_unopened_on_append_error(self): + channel = self.channel + try: + channel.message_append(reference="unopened") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_unopened_on_close_error(self): + channel = self.channel + try: + channel.message_close(reference="unopened") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_unopened_on_transfer_error(self): + channel = self.channel + try: + channel.message_transfer(body=ReferenceId("unopened")) + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_already_opened_error(self): + channel = self.channel + channel.message_open(reference="a") + try: + channel.message_open(reference="a") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_empty_reference(self): + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", message_id="empty-msg", body=ReferenceId(refId)) + channel.synchronous = True + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + msg = queue.get(timeout=1) + self.assertEquals(msg.message_id, "empty-msg") + self.assertDataEquals(channel, msg, "") + + def test_reject(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + + channel.message_consume(queue = "q", destination = "consumer") + channel.message_transfer(routing_key = "q", body="blah, blah") + msg = self.client.queue("consumer").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + channel.message_cancel(destination = "consumer") + msg.reject() + + channel.message_consume(queue = "q", destination = "checker") + msg = self.client.queue("checker").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + + def test_checkpoint(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + + channel.message_open(reference="my-ref") + channel.message_append(reference="my-ref", bytes="abcdefgh") + channel.message_append(reference="my-ref", bytes="ijklmnop") + channel.message_checkpoint(reference="my-ref", identifier="my-checkpoint") + channel.channel_close() + + channel = self.client.channel(2) + channel.channel_open() + channel.message_consume(queue = "q", destination = "consumer") + offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value + self.assertTrue(offset<=16) + channel.message_append(reference="my-ref", bytes="qrstuvwxyz") + channel.synchronous = False + channel.message_transfer(routing_key="q-one", message_id="abcd", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz") + self.assertEmpty(self.client.queue("consumer")) + + + def assertDataEquals(self, channel, msg, expected): + if isinstance(msg.body, ReferenceId): + data = msg.reference.get_complete() + else: + data = msg.body + self.assertEquals(expected, data) diff --git a/qpid/python/tests_0-9/queue.py b/qpid/python/tests_0-9/queue.py new file mode 100644 index 0000000000..b94b8b7739 --- /dev/null +++ b/qpid/python/tests_0-9/queue.py @@ -0,0 +1,306 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueueTests(TestBase): + """Tests for 'methods' on the amqp queue 'class'""" + + def test_purge(self): + """ + Test that the purge method removes messages from the queue + """ + channel = self.channel + #setup, declare a queue and add some messages to it: + channel.exchange_declare(exchange="test-exchange", type="direct") + channel.queue_declare(queue="test-queue", exclusive=True) + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + channel.message_transfer(destination="test-exchange", routing_key="key", body="one") + channel.message_transfer(destination="test-exchange", routing_key="key", body="two") + channel.message_transfer(destination="test-exchange", routing_key="key", body="three") + + #check that the queue now reports 3 messages: + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(3, reply.message_count) + + #now do the purge, then test that three messages are purged and the count drops to 0 + reply = channel.queue_purge(queue="test-queue"); + self.assertEqual(3, reply.message_count) + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(0, reply.message_count) + + #send a further message and consume it, ensuring that the other messages are really gone + channel.message_transfer(destination="test-exchange", routing_key="key", body="four") + channel.message_consume(queue="test-queue", destination="tag", no_ack=True) + queue = self.client.queue("tag") + msg = queue.get(timeout=1) + self.assertEqual("four", msg.body) + + #check error conditions (use new channels): + channel = self.client.channel(2) + channel.channel_open() + try: + #queue specified but doesn't exist: + channel.queue_purge(queue="invalid-queue") + self.fail("Expected failure when purging non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(3) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.queue_purge() + self.fail("Expected failure when purging unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.exchange_delete(exchange="test-exchange") + + def test_declare_exclusive(self): + """ + Test that the exclusive field is honoured in queue.declare + """ + # TestBase.setUp has already opened channel(1) + c1 = self.channel + # Here we open a second separate connection: + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + + #declare an exclusive queue: + c1.queue_declare(queue="exclusive-queue", exclusive="True") + try: + #other connection should not be allowed to declare this: + c2.queue_declare(queue="exclusive-queue", exclusive="True") + self.fail("Expected second exclusive queue_declare to raise a channel exception") + except Closed, e: + self.assertChannelException(405, e.args[0]) + + + def test_declare_passive(self): + """ + Test that the passive field is honoured in queue.declare + """ + channel = self.channel + #declare an exclusive queue: + channel.queue_declare(queue="passive-queue-1", exclusive="True") + channel.queue_declare(queue="passive-queue-1", passive="True") + try: + #other connection should not be allowed to declare this: + channel.queue_declare(queue="passive-queue-2", passive="True") + self.fail("Expected passive declaration of non-existant queue to raise a channel exception") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_bind(self): + """ + Test various permutations of the queue.bind method + """ + channel = self.channel + channel.queue_declare(queue="queue-1", exclusive="True") + + #straightforward case, both exchange & queue exist so no errors expected: + channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + + #bind the default queue for the channel (i.e. last one declared): + channel.queue_bind(exchange="amq.direct", routing_key="key2") + + #use the queue name where neither routing key nor queue are specified: + channel.queue_bind(exchange="amq.direct") + + #try and bind to non-existant exchange + try: + channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + self.fail("Expected bind to non-existant exchange to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #need to reopen a channel: + channel = self.client.channel(2) + channel.channel_open() + + #try and bind non-existant queue: + try: + channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + self.fail("Expected bind of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_unbind_direct(self): + self.unbind_test(exchange="amq.direct", routing_key="key") + + def test_unbind_topic(self): + self.unbind_test(exchange="amq.topic", routing_key="key") + + def test_unbind_fanout(self): + self.unbind_test(exchange="amq.fanout") + + def test_unbind_headers(self): + self.unbind_test(exchange="amq.match", args={ "x-match":"all", "a":"b"}, headers={"a":"b"}) + + def unbind_test(self, exchange, routing_key="", args=None, headers={}): + #bind two queues and consume from them + channel = self.channel + + channel.queue_declare(queue="queue-1", exclusive="True") + channel.queue_declare(queue="queue-2", exclusive="True") + + channel.message_consume(queue="queue-1", destination="queue-1", no_ack=True) + channel.message_consume(queue="queue-2", destination="queue-2", no_ack=True) + + queue1 = self.client.queue("queue-1") + queue2 = self.client.queue("queue-2") + + channel.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) + + #send a message that will match both bindings + channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="one") + + #unbind first queue + channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + + #send another message + channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="two") + + #check one queue has both messages and the other has only one + self.assertEquals("one", queue1.get(timeout=1).body) + try: + msg = queue1.get(timeout=1) + self.fail("Got extra message: %s" % msg.body) + except Empty: pass + + self.assertEquals("one", queue2.get(timeout=1).body) + self.assertEquals("two", queue2.get(timeout=1).body) + try: + msg = queue2.get(timeout=1) + self.fail("Got extra message: " + msg) + except Empty: pass + + + def test_delete_simple(self): + """ + Test core queue deletion behaviour + """ + channel = self.channel + + #straight-forward case: + channel.queue_declare(queue="delete-me") + channel.message_transfer(routing_key="delete-me", body="a") + channel.message_transfer(routing_key="delete-me", body="b") + channel.message_transfer(routing_key="delete-me", body="c") + reply = channel.queue_delete(queue="delete-me") + self.assertEqual(3, reply.message_count) + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="delete-me", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #check attempted deletion of non-existant queue is handled correctly: + channel = self.client.channel(2) + channel.channel_open() + try: + channel.queue_delete(queue="i-dont-exist", if_empty="True") + self.fail("Expected delete of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + + def test_delete_ifempty(self): + """ + Test that if_empty field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and add a message to it (use default binding): + channel.queue_declare(queue="delete-me-2") + channel.queue_declare(queue="delete-me-2", passive="True") + channel.message_transfer(routing_key="delete-me-2", body="message") + + #try to delete, but only if empty: + try: + channel.queue_delete(queue="delete-me-2", if_empty="True") + self.fail("Expected delete if_empty to fail for non-empty queue") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + #need new channel now: + channel = self.client.channel(2) + channel.channel_open() + + #empty queue: + channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True) + queue = self.client.queue("consumer_tag") + msg = queue.get(timeout=1) + self.assertEqual("message", msg.body) + channel.message_cancel(destination="consumer_tag") + + #retry deletion on empty queue: + channel.queue_delete(queue="delete-me-2", if_empty="True") + + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-2", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_delete_ifunused(self): + """ + Test that if_unused field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and register a consumer: + channel.queue_declare(queue="delete-me-3") + channel.queue_declare(queue="delete-me-3", passive="True") + channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True) + + #need new channel now: + channel2 = self.client.channel(2) + channel2.channel_open() + #try to delete, but only if empty: + try: + channel2.queue_delete(queue="delete-me-3", if_unused="True") + self.fail("Expected delete if_unused to fail for queue with existing consumer") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + + channel.message_cancel(destination="consumer_tag") + channel.queue_delete(queue="delete-me-3", if_unused="True") + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-3", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + diff --git a/qpid/python/tests_0-9/testlib.py b/qpid/python/tests_0-9/testlib.py new file mode 100644 index 0000000000..f345fbbd80 --- /dev/null +++ b/qpid/python/tests_0-9/testlib.py @@ -0,0 +1,66 @@ +# +# 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. +# + +# +# Tests for the testlib itself. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from Queue import Empty + +import sys +from traceback import * + +def mytrace(frame, event, arg): + print_stack(frame); + print "====" + return mytrace + +class TestBaseTest(TestBase): + """Verify TestBase functions work as expected""" + + def testAssertEmptyPass(self): + """Test assert empty works""" + self.queue_declare(queue="empty") + q = self.consume("empty") + self.assertEmpty(q) + try: + q.get(timeout=1) + self.fail("Queue is not empty.") + except Empty: None # Ignore + + def testAssertEmptyFail(self): + self.queue_declare(queue="full") + q = self.consume("full") + self.channel.message_transfer(routing_key="full", body="") + try: + self.assertEmpty(q); + self.fail("assertEmpty did not assert on non-empty queue") + except AssertionError: None # Ignore + + def testMessageProperties(self): + """Verify properties are passed with message""" + props={"x":1, "y":2} + self.queue_declare(queue="q") + q = self.consume("q") + self.assertPublishGet(q, routing_key="q", properties=props) + + + diff --git a/qpid/python/tests_0-9/tx.py b/qpid/python/tests_0-9/tx.py new file mode 100644 index 0000000000..0f6b4f5cd1 --- /dev/null +++ b/qpid/python/tests_0-9/tx.py @@ -0,0 +1,188 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class TxTests(TestBase): + """ + Tests for 'methods' on the amqp tx 'class' + """ + + def test_commit(self): + """ + Test that commited publishes are delivered and commited acks are not re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c") + channel.tx_commit() + + #check results + for i in range(1, 5): + msg = queue_c.get(timeout=1) + self.assertEqual("TxMessage %d" % i, msg.body) + msg.ok() + + msg = queue_b.get(timeout=1) + self.assertEqual("TxMessage 6", msg.body) + msg.ok() + + msg = queue_a.get(timeout=1) + self.assertEqual("TxMessage 7", msg.body) + msg.ok() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def test_auto_rollback(self): + """ + Test that a channel closed with an open transaction is effectively rolled back + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.body) + msg.ok() + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.body) + msg.ok() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def test_rollback(self): + """ + Test that rolled back publishes are not delivered and rolled back acks are re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.body) + msg.ok() + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.body) + msg.ok() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def perform_txn_work(self, channel, name_a, name_b, name_c): + """ + Utility method that does some setup and some work under a transaction. Used for testing both + commit and rollback + """ + #setup: + channel.queue_declare(queue=name_a, exclusive=True) + channel.queue_declare(queue=name_b, exclusive=True) + channel.queue_declare(queue=name_c, exclusive=True) + + key = "my_key_" + name_b + topic = "my_topic_" + name_c + + channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) + channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) + + for i in range(1, 5): + channel.message_transfer(routing_key=name_a, body="Message %d" % i) + + channel.message_transfer(routing_key=key, destination="amq.direct", body="Message 6") + channel.message_transfer(routing_key=topic, destination="amq.topic", body="Message 7") + + channel.tx_select() + + #consume and ack messages + channel.message_consume(queue=name_a, destination="sub_a", no_ack=False) + queue_a = self.client.queue("sub_a") + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + msg.ok(batchoffset=-3) + + channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) + queue_b = self.client.queue("sub_b") + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.body) + msg.ok() + + sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False) + queue_c = self.client.queue("sub_c") + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.body) + msg.ok() + + #publish messages + for i in range(1, 5): + channel.message_transfer(routing_key=topic, destination="amq.topic", body="TxMessage %d" % i) + + channel.message_transfer(routing_key=key, destination="amq.direct", body="TxMessage 6") + channel.message_transfer(routing_key=name_a, body="TxMessage 7") + + return queue_a, queue_b, queue_c -- cgit v1.2.1 From 698283620afcb267bda2752b746c3e19641150ef Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 21 Mar 2007 02:03:49 +0000 Subject: * python/qpid/*.py:: apply 0-9 changes missed by merge. * python/cpp_failing_0-9.txt: update tests to refer to tess_0-9 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520704 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-9.txt | 6 +++--- qpid/python/qpid/client.py | 3 +++ qpid/python/qpid/message.py | 9 ++++++--- qpid/python/qpid/peer.py | 15 ++++++++++----- 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-9.txt b/qpid/python/cpp_failing_0-9.txt index 5d74874d52..06c31080fb 100644 --- a/qpid/python/cpp_failing_0-9.txt +++ b/qpid/python/cpp_failing_0-9.txt @@ -1,4 +1,4 @@ -tests.message.MessageTests.test_checkpoint -tests.message.MessageTests.test_reject -tests.basic.BasicTests.test_get +tests_0-9.message.MessageTests.test_checkpoint +tests_0-9.message.MessageTests.test_reject +tests_0-9.basic.BasicTests.test_get diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index e548ef0e99..cdceb87bdf 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -131,6 +131,9 @@ class ClientDelegate(Delegate): def basic_deliver(self, ch, msg): self.client.queue(msg.consumer_tag).put(msg) + def channel_pong(self, ch, msg): + msg.ok() + def channel_close(self, ch, msg): ch.close(msg) diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index 29c8654937..f80293180e 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -47,9 +47,12 @@ class Message: else: for r in self.method.responses: if attr == r.name: - result = lambda *args, **kwargs: \ - self.channel.respond(Method(r, r.arguments(*args, **kwargs)), - self.frame) + def respond(*args, **kwargs): + batch=0 + if kwargs.has_key("batchoffset"): + batch=kwargs.pop("batchoffset") + self.channel.respond(Method(r, r.arguments(*args, **kwargs)), batch, self.frame) + result = respond break else: raise AttributeError(attr) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 28db20f187..72e6a19bc7 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -129,6 +129,8 @@ class Peer: content = None self.delegate(channel, Message(channel, frame, content)) + except QueueClosed, e: + self.close(e) except: self.fatal() @@ -156,12 +158,15 @@ class Responder: self.write = writer self.sequence = Sequence(1) - def respond(self, method, request): + def respond(self, method, batch, request): if isinstance(request, Method): self.write(method) else: - # XXX: batching - frame = Response(self.sequence.next(), request.id, 0, method) + # allow batching from frame at either end + if batch<0: + frame = Response(self.sequence.next(), request.id+batch, -batch, method) + else: + frame = Response(self.sequence.next(), request.id, batch, method) self.write(frame) class Closed(Exception): pass @@ -238,8 +243,8 @@ class Channel: def request(self, method, listener, content = None): self.requester.request(method, listener, content) - def respond(self, method, request): - self.responder.respond(method, request) + def respond(self, method, batch, request): + self.responder.respond(method, batch, request) def invoke(self, type, args, kwargs): content = kwargs.pop("content", None) -- cgit v1.2.1 From cbea321af3ebc1a4bdd5acb11ae935845751447f Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 21 Mar 2007 02:09:17 +0000 Subject: removed python/tests: replaced by tests_0-8/tests_0-9 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@520707 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/__init__.py | 20 -- qpid/python/tests/basic.py | 394 ------------------------- qpid/python/tests/broker.py | 109 ------- qpid/python/tests/example.py | 94 ------ qpid/python/tests/exchange.py | 327 --------------------- qpid/python/tests/message.py | 659 ------------------------------------------ qpid/python/tests/queue.py | 255 ---------------- qpid/python/tests/testlib.py | 66 ----- qpid/python/tests/tx.py | 188 ------------ 9 files changed, 2112 deletions(-) delete mode 100644 qpid/python/tests/__init__.py delete mode 100644 qpid/python/tests/basic.py delete mode 100644 qpid/python/tests/broker.py delete mode 100644 qpid/python/tests/example.py delete mode 100644 qpid/python/tests/exchange.py delete mode 100644 qpid/python/tests/message.py delete mode 100644 qpid/python/tests/queue.py delete mode 100644 qpid/python/tests/testlib.py delete mode 100644 qpid/python/tests/tx.py (limited to 'qpid/python') diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py deleted file mode 100644 index 9a09d2d04f..0000000000 --- a/qpid/python/tests/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# Do not delete - marks this directory as a python package. - -# -# 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. -# diff --git a/qpid/python/tests/basic.py b/qpid/python/tests/basic.py deleted file mode 100644 index 0100b8ee3e..0000000000 --- a/qpid/python/tests/basic.py +++ /dev/null @@ -1,394 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class BasicTests(TestBase): - """Tests for 'methods' on the amqp basic 'class'""" - - def test_consume_no_local(self): - """ - Test that the no_local flag is honoured in the consume method - """ - channel = self.channel - #setup, declare two queues: - channel.queue_declare(queue="test-queue-1a", exclusive=True) - channel.queue_declare(queue="test-queue-1b", exclusive=True) - #establish two consumers one of which excludes delivery of locally sent messages - channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a") - channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) - - #send a message - channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local")) - channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local")) - - #check the queues of the two consumers - excluded = self.client.queue("local_excluded") - included = self.client.queue("local_included") - msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.content.body) - try: - excluded.get(timeout=1) - self.fail("Received locally published message though no_local=true") - except Empty: None - - - def test_consume_exclusive(self): - """ - Test that the exclusive flag is honoured in the consume method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-2", exclusive=True) - - #check that an exclusive consumer prevents other consumer being created: - channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True) - try: - channel.basic_consume(consumer_tag="second", queue="test-queue-2") - self.fail("Expected consume request to fail due to previous exclusive consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - #open new channel and cleanup last consumer: - channel = self.client.channel(2) - channel.channel_open() - - #check that an exclusive consumer cannot be created if a consumer already exists: - channel.basic_consume(consumer_tag="first", queue="test-queue-2") - try: - channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True) - self.fail("Expected exclusive consume request to fail due to previous consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - def test_consume_queue_errors(self): - """ - Test error conditions associated with the queue field of the consume method: - """ - channel = self.channel - try: - #queue specified but doesn't exist: - channel.basic_consume(queue="invalid-queue") - self.fail("Expected failure when consuming from non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - channel = self.client.channel(2) - channel.channel_open() - try: - #queue not specified and none previously declared for channel: - channel.basic_consume(queue="") - self.fail("Expected failure when consuming from unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_consume_unique_consumers(self): - """ - Ensure unique consumer tags are enforced - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-3", exclusive=True) - - #check that attempts to use duplicate tags are detected and prevented: - channel.basic_consume(consumer_tag="first", queue="test-queue-3") - try: - channel.basic_consume(consumer_tag="first", queue="test-queue-3") - self.fail("Expected consume request to fail due to non-unique tag") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_cancel(self): - """ - Test compliance of the basic.cancel method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-4", exclusive=True) - channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") - channel.basic_publish(routing_key="test-queue-4", content=Content("One")) - - #cancel should stop messages being delivered - channel.basic_cancel(consumer_tag="my-consumer") - channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) - myqueue = self.client.queue("my-consumer") - msg = myqueue.get(timeout=1) - self.assertEqual("One", msg.content.body) - try: - msg = myqueue.get(timeout=1) - self.fail("Got message after cancellation: " + msg) - except Empty: None - - #cancellation of non-existant consumers should be handled without error - channel.basic_cancel(consumer_tag="my-consumer") - channel.basic_cancel(consumer_tag="this-never-existed") - - - def test_ack(self): - """ - Test basic ack/recover behaviour - """ - channel = self.channel - self.queue_declare(queue="test-ack-queue", exclusive=True) - - reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) - queue = self.client.queue(reply.consumer_tag) - - channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four - - channel.basic_recover(requeue=False) - - msg3b = queue.get(timeout=1) - msg5b = queue.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - def test_recover_requeue(self): - """ - Test requeing on recovery - """ - channel = self.channel - channel.queue_declare(queue="test-requeue", exclusive=True) - - subscription = channel.basic_consume(queue="test-requeue", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) - - channel.basic_publish(routing_key="test-requeue", content=Content("One")) - channel.basic_publish(routing_key="test-requeue", content=Content("Two")) - channel.basic_publish(routing_key="test-requeue", content=Content("Three")) - channel.basic_publish(routing_key="test-requeue", content=Content("Four")) - channel.basic_publish(routing_key="test-requeue", content=Content("Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four - - channel.basic_cancel(consumer_tag=subscription.consumer_tag) - subscription2 = channel.basic_consume(queue="test-requeue") - queue2 = self.client.queue(subscription2.consumer_tag) - - channel.basic_recover(requeue=True) - - msg3b = queue2.get(timeout=1) - msg5b = queue2.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - self.assertEqual(True, msg3b.redelivered) - self.assertEqual(True, msg5b.redelivered) - - try: - extra = queue2.get(timeout=1) - self.fail("Got unexpected message in second queue: " + extra.content.body) - except Empty: None - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message in original queue: " + extra.content.body) - except Empty: None - - - def test_qos_prefetch_count(self): - """ - Test that the prefetch count specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-count", exclusive=True) - subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) - - #set prefetch to 5: - channel.basic_qos(prefetch_count=5) - - #publish 10 messages: - for i in range(1, 11): - channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i)) - - #only 5 messages should have been delivered: - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - - - def test_qos_prefetch_size(self): - """ - Test that the prefetch size specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-size", exclusive=True) - subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) - - #set prefetch to 50 bytes (each message is 9 or 10 bytes): - channel.basic_qos(prefetch_size=50) - - #publish 10 messages: - for i in range(1, 11): - channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i)) - - #only 5 messages should have been delivered (i.e. 45 bytes worth): - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - #make sure that a single oversized message still gets delivered - large = "abcdefghijklmnopqrstuvwxyz" - large = large + "-" + large; - channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) - msg = queue.get(timeout=1) - self.assertEqual(large, msg.content.body) - - def test_get(self): - """ - Test basic_get method - """ - channel = self.channel - channel.queue_declare(queue="test-get", exclusive=True) - - #publish some messages (no_ack=True) - for i in range(1, 11): - channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) - - #use basic_get to read back the messages, and check that we get an empty at the end - for i in range(1, 11): - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_ok") - self.assertEqual("Message %d" % i, reply.content.body) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") - - #repeat for no_ack=False - for i in range(11, 21): - channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) - - for i in range(11, 21): - reply = channel.basic_get(no_ack=False) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_ok") - self.assertEqual("Message %d" % i, reply.content.body) - if(i == 13): - channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) - if(i in [15, 17, 19]): - channel.basic_ack(delivery_tag=reply.delivery_tag) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") - - #recover(requeue=True) - channel.basic_recover(requeue=True) - - #get the unacked messages again (14, 16, 18, 20) - for i in [14, 16, 18, 20]: - reply = channel.basic_get(no_ack=False) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_ok") - self.assertEqual("Message %d" % i, reply.content.body) - channel.basic_ack(delivery_tag=reply.delivery_tag) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") - - channel.basic_recover(requeue=True) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") diff --git a/qpid/python/tests/broker.py b/qpid/python/tests/broker.py deleted file mode 100644 index ebb3a525f5..0000000000 --- a/qpid/python/tests/broker.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# 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. -# -from qpid.client import Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class BrokerTests(TestBase): - """Tests for basic Broker functionality""" - - def test_ack_and_no_ack(self): - """ - First, this test tries to receive a message with a no-ack - consumer. Second, this test tries to explicitely receive and - acknowledge a message with an acknowledging consumer. - """ - ch = self.channel - self.queue_declare(ch, queue = "myqueue") - - # No ack consumer - ctag = "tag1" - ch.message_consume(queue = "myqueue", destination = ctag, no_ack = True) - body = "test no-ack" - ch.message_transfer(routing_key = "myqueue", body = body) - msg = self.client.queue(ctag).get(timeout = 5) - self.assert_(msg.body == body) - - # Acknowledging consumer - self.queue_declare(ch, queue = "otherqueue") - ctag = "tag2" - ch.message_consume(queue = "otherqueue", destination = ctag, no_ack = False) - body = "test ack" - ch.message_transfer(routing_key = "otherqueue", body = body) - msg = self.client.queue(ctag).get(timeout = 5) - msg.ok() - self.assert_(msg.body == body) - - def test_simple_delivery_immediate(self): - """ - Test simple message delivery where consume is issued before publish - """ - channel = self.channel - self.exchange_declare(channel, exchange="test-exchange", type="direct") - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - consumer_tag = "tag1" - channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) - queue = self.client.queue(consumer_tag) - - body = "Immediate Delivery" - channel.message_transfer(destination="test-exchange", routing_key="key", body=body, immediate=True) - msg = queue.get(timeout=5) - self.assert_(msg.body == body) - - # TODO: Ensure we fail if immediate=True and there's no consumer. - - - def test_simple_delivery_queued(self): - """ - Test basic message delivery where publish is issued before consume - (i.e. requires queueing of the message) - """ - channel = self.channel - self.exchange_declare(channel, exchange="test-exchange", type="direct") - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - body = "Queued Delivery" - channel.message_transfer(destination="test-exchange", routing_key="key", body=body) - - consumer_tag = "tag1" - channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) - queue = self.client.queue(consumer_tag) - msg = queue.get(timeout=5) - self.assert_(msg.body == body) - - def test_invalid_channel(self): - channel = self.client.channel(200) - try: - channel.queue_declare(exclusive=True) - self.fail("Expected error on queue_declare for invalid channel") - except Closed, e: - self.assertConnectionException(504, e.args[0]) - - def test_closed_channel(self): - channel = self.client.channel(200) - channel.channel_open() - channel.channel_close() - try: - channel.queue_declare(exclusive=True) - self.fail("Expected error on queue_declare for closed channel") - except Closed, e: - self.assertConnectionException(504, e.args[0]) - diff --git a/qpid/python/tests/example.py b/qpid/python/tests/example.py deleted file mode 100644 index 7ab4cc7d0a..0000000000 --- a/qpid/python/tests/example.py +++ /dev/null @@ -1,94 +0,0 @@ -# -# 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. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class ExampleTest (TestBase): - """ - An example Qpid test, illustrating the unittest frameowkr and the - python Qpid client. The test class must inherit TestCase. The - test code uses the Qpid client to interact with a qpid broker and - verify it behaves as expected. - """ - - def test_example(self): - """ - An example test. Note that test functions must start with 'test_' - to be recognized by the test framework. - """ - - # By inheriting TestBase, self.client is automatically connected - # and self.channel is automatically opened as channel(1) - # Other channel methods mimic the protocol. - channel = self.channel - - # Now we can send regular commands. If you want to see what the method - # arguments mean or what other commands are available, you can use the - # python builtin help() method. For example: - #help(chan) - #help(chan.exchange_declare) - - # If you want browse the available protocol methods without being - # connected to a live server you can use the amqp-doc utility: - # - # Usage amqp-doc [] [ ... ] - # - # Options: - # -e, --regexp use regex instead of glob when matching - - # Now that we know what commands are available we can use them to - # interact with the server. - - # Here we use ordinal arguments. - self.exchange_declare(channel, 0, "test", "direct") - - # Here we use keyword arguments. - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") - - # Call Channel.basic_consume to register as a consumer. - # All the protocol methods return a message object. The message object - # has fields corresponding to the reply method fields, plus a content - # field that is filled if the reply includes content. In this case the - # interesting field is the consumer_tag. - channel.message_consume(queue="test-queue", destination="consumer_tag") - - # We can use the Client.queue(...) method to access the queue - # corresponding to our consumer_tag. - queue = self.client.queue("consumer_tag") - - # Now lets publish a message and see if our consumer gets it. To do - # this we need to import the Content class. - body = "Hello World!" - channel.message_transfer(destination="test", - routing_key="key", - body = body) - - # Now we'll wait for the message to arrive. We can use the timeout - # argument in case the server hangs. By default queue.get() will wait - # until a message arrives or the connection to the server dies. - msg = queue.get(timeout=10) - - # And check that we got the right response with assertEqual - self.assertEqual(body, msg.body) - - # Now acknowledge the message. - msg.ok() - diff --git a/qpid/python/tests/exchange.py b/qpid/python/tests/exchange.py deleted file mode 100644 index 54c462de24..0000000000 --- a/qpid/python/tests/exchange.py +++ /dev/null @@ -1,327 +0,0 @@ -# -# 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. -# - -""" -Tests for exchange behaviour. - -Test classes ending in 'RuleTests' are derived from rules in amqp.xml. -""" - -import Queue, logging -from qpid.testlib import TestBase -from qpid.content import Content -from qpid.client import Closed - - -class StandardExchangeVerifier: - """Verifies standard exchange behavior. - - Used as base class for classes that test standard exchanges.""" - - def verifyDirectExchange(self, ex): - """Verify that ex behaves like a direct exchange.""" - self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") - self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") - try: - self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") - self.fail("Expected Empty exception") - except Queue.Empty: None # Expected - - def verifyFanOutExchange(self, ex): - """Verify that ex behaves like a fanout exchange.""" - self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex) - self.queue_declare(queue="p") - self.channel.queue_bind(queue="p", exchange=ex) - for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) - - def verifyTopicExchange(self, ex): - """Verify that ex behaves like a topic exchange""" - self.queue_declare(queue="a") - self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") - q = self.consume("a") - self.assertPublishGet(q, ex, "a.b.x") - self.assertPublishGet(q, ex, "a.x.b.x") - self.assertPublishGet(q, ex, "a.x.x.b.x") - # Shouldn't match - self.channel.message_transfer(destination=ex, routing_key="a.b") - self.channel.message_transfer(destination=ex, routing_key="a.b.x.y") - self.channel.message_transfer(destination=ex, routing_key="x.a.b.x") - self.channel.message_transfer(destination=ex, routing_key="a.b") - self.assert_(q.empty()) - - def verifyHeadersExchange(self, ex): - """Verify that ex is a headers exchange""" - self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) - q = self.consume("q") - headers = {"name":"fred", "age":3} - self.assertPublishGet(q, exchange=ex, properties=headers) - self.channel.message_transfer(destination=ex, body="") # No headers, won't deliver - self.assertEmpty(q); - - -class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): - """ - The server SHOULD implement these standard exchange types: topic, headers. - - Client attempts to declare an exchange with each of these standard types. - """ - - def testDirect(self): - """Declare and test a direct exchange""" - self.exchange_declare(0, exchange="d", type="direct") - self.verifyDirectExchange("d") - - def testFanout(self): - """Declare and test a fanout exchange""" - self.exchange_declare(0, exchange="f", type="fanout") - self.verifyFanOutExchange("f") - - def testTopic(self): - """Declare and test a topic exchange""" - self.exchange_declare(0, exchange="t", type="topic") - self.verifyTopicExchange("t") - - def testHeaders(self): - """Declare and test a headers exchange""" - self.exchange_declare(0, exchange="h", type="headers") - self.verifyHeadersExchange("h") - - -class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): - """ - The server MUST, in each virtual host, pre-declare an exchange instance - for each standard exchange type that it implements, where the name of the - exchange instance is amq. followed by the exchange type name. - - Client creates a temporary queue and attempts to bind to each required - exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if - those types are defined). - """ - def testAmqDirect(self): self.verifyDirectExchange("amq.direct") - - def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") - - def testAmqTopic(self): self.verifyTopicExchange("amq.topic") - - def testAmqMatch(self): self.verifyHeadersExchange("amq.match") - -class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): - """ - The server MUST predeclare a direct exchange to act as the default exchange - for content Publish methods and for default queue bindings. - - Client checks that the default exchange is active by specifying a queue - binding with no exchange name, and publishing a message with a suitable - routing key but without specifying the exchange name, then ensuring that - the message arrives in the queue correctly. - """ - def testDefaultExchange(self): - # Test automatic binding by queue name. - self.queue_declare(queue="d") - self.assertPublishConsume(queue="d", routing_key="d") - # Test explicit bind to default queue - self.verifyDirectExchange("") - - -# TODO aconway 2006-09-27: Fill in empty tests: - -class DefaultAccessRuleTests(TestBase): - """ - The server MUST NOT allow clients to access the default exchange except - by specifying an empty exchange name in the Queue.Bind and content Publish - methods. - """ - -class ExtensionsRuleTests(TestBase): - """ - The server MAY implement other exchange types as wanted. - """ - - -class DeclareMethodMinimumRuleTests(TestBase): - """ - The server SHOULD support a minimum of 16 exchanges per virtual host and - ideally, impose no limit except as defined by available resources. - - The client creates as many exchanges as it can until the server reports - an error; the number of exchanges successfuly created must be at least - sixteen. - """ - - -class DeclareMethodTicketFieldValidityRuleTests(TestBase): - """ - The client MUST provide a valid access ticket giving "active" access to - the realm in which the exchange exists or will be created, or "passive" - access if the if-exists flag is set. - - Client creates access ticket with wrong access rights and attempts to use - in this method. - """ - - -class DeclareMethodExchangeFieldReservedRuleTests(TestBase): - """ - Exchange names starting with "amq." are reserved for predeclared and - standardised exchanges. The client MUST NOT attempt to create an exchange - starting with "amq.". - - - """ - - -class DeclareMethodTypeFieldTypedRuleTests(TestBase): - """ - Exchanges cannot be redeclared with different types. The client MUST not - attempt to redeclare an existing exchange with a different type than used - in the original Exchange.Declare method. - - - """ - - -class DeclareMethodTypeFieldSupportRuleTests(TestBase): - """ - The client MUST NOT attempt to create an exchange with a type that the - server does not support. - - - """ - - -class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): - """ - If set, and the exchange does not already exist, the server MUST raise a - channel exception with reply code 404 (not found). - """ - def test(self): - try: - self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) - self.fail("Expected 404 for passive declaration of unknown exchange.") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - -class DeclareMethodDurableFieldSupportRuleTests(TestBase): - """ - The server MUST support both durable and transient exchanges. - - - """ - - -class DeclareMethodDurableFieldStickyRuleTests(TestBase): - """ - The server MUST ignore the durable field if the exchange already exists. - - - """ - - -class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): - """ - The server MUST ignore the auto-delete field if the exchange already - exists. - - - """ - - -class DeleteMethodTicketFieldValidityRuleTests(TestBase): - """ - The client MUST provide a valid access ticket giving "active" access - rights to the exchange's access realm. - - Client creates access ticket with wrong access rights and attempts to use - in this method. - """ - - -class DeleteMethodExchangeFieldExistsRuleTests(TestBase): - """ - The client MUST NOT attempt to delete an exchange that does not exist. - """ - - -class HeadersExchangeTests(TestBase): - """ - Tests for headers exchange functionality. - """ - def setUp(self): - TestBase.setUp(self) - self.queue_declare(queue="q") - self.q = self.consume("q") - - def myAssertPublishGet(self, headers): - self.assertPublishGet(self.q, exchange="amq.match", properties=headers) - - def myBasicPublish(self, headers): - self.channel.message_transfer(destination="amq.match", body="foobar", application_headers=headers) - - def testMatchAll(self): - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) - self.myAssertPublishGet({"name":"fred", "age":3}) - self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) - - # None of these should match - self.myBasicPublish({}) - self.myBasicPublish({"name":"barney"}) - self.myBasicPublish({"name":10}) - self.myBasicPublish({"name":"fred", "age":2}) - self.assertEmpty(self.q) - - def testMatchAny(self): - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) - self.myAssertPublishGet({"name":"fred"}) - self.myAssertPublishGet({"name":"fred", "ignoreme":10}) - self.myAssertPublishGet({"ignoreme":10, "age":3}) - - # Wont match - self.myBasicPublish({}) - self.myBasicPublish({"irrelevant":0}) - self.assertEmpty(self.q) - - -class MiscellaneousErrorsTests(TestBase): - """ - Test some miscellaneous error conditions - """ - def testTypeNotKnown(self): - try: - self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") - self.fail("Expected 503 for declaration of unknown exchange type.") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def testDifferentDeclaredType(self): - self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") - try: - self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") - self.fail("Expected 530 for redeclaration of exchange with different type.") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - #cleanup - other = self.connect() - c2 = other.channel(1) - c2.channel_open() - c2.exchange_delete(exchange="test_different_declared_type_exchange") - diff --git a/qpid/python/tests/message.py b/qpid/python/tests/message.py deleted file mode 100644 index d5f5d4dbc2..0000000000 --- a/qpid/python/tests/message.py +++ /dev/null @@ -1,659 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from qpid.reference import Reference, ReferenceId - -class MessageTests(TestBase): - """Tests for 'methods' on the amqp message 'class'""" - - def test_consume_no_local(self): - """ - Test that the no_local flag is honoured in the consume method - """ - channel = self.channel - #setup, declare two queues: - channel.queue_declare(queue="test-queue-1a", exclusive=True) - channel.queue_declare(queue="test-queue-1b", exclusive=True) - #establish two consumers one of which excludes delivery of locally sent messages - channel.message_consume(destination="local_included", queue="test-queue-1a") - channel.message_consume(destination="local_excluded", queue="test-queue-1b", no_local=True) - - #send a message - channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local") - channel.message_transfer(routing_key="test-queue-1b", body="consume_no_local") - - #check the queues of the two consumers - excluded = self.client.queue("local_excluded") - included = self.client.queue("local_included") - msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.body) - try: - excluded.get(timeout=1) - self.fail("Received locally published message though no_local=true") - except Empty: None - - - def test_consume_exclusive(self): - """ - Test that the exclusive flag is honoured in the consume method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-2", exclusive=True) - - #check that an exclusive consumer prevents other consumer being created: - channel.message_consume(destination="first", queue="test-queue-2", exclusive=True) - try: - channel.message_consume(destination="second", queue="test-queue-2") - self.fail("Expected consume request to fail due to previous exclusive consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - #open new channel and cleanup last consumer: - channel = self.client.channel(2) - channel.channel_open() - - #check that an exclusive consumer cannot be created if a consumer already exists: - channel.message_consume(destination="first", queue="test-queue-2") - try: - channel.message_consume(destination="second", queue="test-queue-2", exclusive=True) - self.fail("Expected exclusive consume request to fail due to previous consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - def test_consume_queue_errors(self): - """ - Test error conditions associated with the queue field of the consume method: - """ - channel = self.channel - try: - #queue specified but doesn't exist: - channel.message_consume(queue="invalid-queue") - self.fail("Expected failure when consuming from non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - channel = self.client.channel(2) - channel.channel_open() - try: - #queue not specified and none previously declared for channel: - channel.message_consume(queue="") - self.fail("Expected failure when consuming from unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_consume_unique_consumers(self): - """ - Ensure unique consumer tags are enforced - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-3", exclusive=True) - - #check that attempts to use duplicate tags are detected and prevented: - channel.message_consume(destination="first", queue="test-queue-3") - try: - channel.message_consume(destination="first", queue="test-queue-3") - self.fail("Expected consume request to fail due to non-unique tag") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_cancel(self): - """ - Test compliance of the basic.cancel method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-4", exclusive=True) - channel.message_consume(destination="my-consumer", queue="test-queue-4") - channel.message_transfer(routing_key="test-queue-4", body="One") - - #cancel should stop messages being delivered - channel.message_cancel(destination="my-consumer") - channel.message_transfer(routing_key="test-queue-4", body="Two") - myqueue = self.client.queue("my-consumer") - msg = myqueue.get(timeout=1) - self.assertEqual("One", msg.body) - try: - msg = myqueue.get(timeout=1) - self.fail("Got message after cancellation: " + msg) - except Empty: None - - #cancellation of non-existant consumers should be handled without error - channel.message_cancel(destination="my-consumer") - channel.message_cancel(destination="this-never-existed") - - - def test_ack(self): - """ - Test basic ack/recover behaviour - """ - channel = self.channel - channel.queue_declare(queue="test-ack-queue", exclusive=True) - - channel.message_consume(queue="test-ack-queue", destination="consumer_tag", no_ack=False) - queue = self.client.queue("consumer_tag") - - channel.message_transfer(routing_key="test-ack-queue", body="One") - channel.message_transfer(routing_key="test-ack-queue", body="Two") - channel.message_transfer(routing_key="test-ack-queue", body="Three") - channel.message_transfer(routing_key="test-ack-queue", body="Four") - channel.message_transfer(routing_key="test-ack-queue", body="Five") - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.body) - self.assertEqual("Two", msg2.body) - self.assertEqual("Three", msg3.body) - self.assertEqual("Four", msg4.body) - self.assertEqual("Five", msg5.body) - - msg1.ok(batchoffset=1)#One and Two - msg4.ok() - - channel.message_recover(requeue=False) - - msg3b = queue.get(timeout=1) - msg5b = queue.get(timeout=1) - - self.assertEqual("Three", msg3b.body) - self.assertEqual("Five", msg5b.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) - except Empty: None - - def test_recover_requeue(self): - """ - Test requeing on recovery - """ - channel = self.channel - channel.queue_declare(queue="test-requeue", exclusive=True) - - channel.message_consume(queue="test-requeue", destination="consumer_tag", no_ack=False) - queue = self.client.queue("consumer_tag") - - channel.message_transfer(routing_key="test-requeue", body="One") - channel.message_transfer(routing_key="test-requeue", body="Two") - channel.message_transfer(routing_key="test-requeue", body="Three") - channel.message_transfer(routing_key="test-requeue", body="Four") - channel.message_transfer(routing_key="test-requeue", body="Five") - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.body) - self.assertEqual("Two", msg2.body) - self.assertEqual("Three", msg3.body) - self.assertEqual("Four", msg4.body) - self.assertEqual("Five", msg5.body) - - msg1.ok(batchoffset=1) #One and Two - msg4.ok() #Four - - channel.message_cancel(destination="consumer_tag") - channel.message_consume(queue="test-requeue", destination="consumer_tag") - queue2 = self.client.queue("consumer_tag") - - channel.message_recover(requeue=True) - - msg3b = queue2.get(timeout=1) - msg5b = queue2.get(timeout=1) - - self.assertEqual("Three", msg3b.body) - self.assertEqual("Five", msg5b.body) - - self.assertEqual(True, msg3b.redelivered) - self.assertEqual(True, msg5b.redelivered) - - try: - extra = queue2.get(timeout=1) - self.fail("Got unexpected message in second queue: " + extra.body) - except Empty: None - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message in original queue: " + extra.body) - except Empty: None - - - def test_qos_prefetch_count(self): - """ - Test that the prefetch count specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-count", exclusive=True) - subscription = channel.message_consume(queue="test-prefetch-count", destination="consumer_tag", no_ack=False) - queue = self.client.queue("consumer_tag") - - #set prefetch to 5: - channel.message_qos(prefetch_count=5) - - #publish 10 messages: - for i in range(1, 11): - channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) - - #only 5 messages should have been delivered: - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - #todo: once batching is implmented, send a single response for all messages - msg.ok(batchoffset=-4)#1-5 - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - msg.ok(batchoffset=-4)#6-10 - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.body) - except Empty: None - - - - def test_qos_prefetch_size(self): - """ - Test that the prefetch size specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-size", exclusive=True) - subscription = channel.message_consume(queue="test-prefetch-size", destination="consumer_tag", no_ack=False) - queue = self.client.queue("consumer_tag") - - #set prefetch to 50 bytes (each message is 9 or 10 bytes): - channel.message_qos(prefetch_size=50) - - #publish 10 messages: - for i in range(1, 11): - channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) - - #only 5 messages should have been delivered (i.e. 45 bytes worth): - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - msg.ok(batchoffset=-4)#1-5 - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - msg.ok(batchoffset=-4)#6-10 - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.body) - except Empty: None - - #make sure that a single oversized message still gets delivered - large = "abcdefghijklmnopqrstuvwxyz" - large = large + "-" + large; - channel.message_transfer(routing_key="test-prefetch-size", body=large) - msg = queue.get(timeout=1) - self.assertEqual(large, msg.body) - - def test_get(self): - """ - Test message_get method - """ - channel = self.channel - channel.queue_declare(queue="test-get", exclusive=True) - - #publish some messages (no_ack=True) - for i in range(1, 11): - channel.message_transfer(routing_key="test-get", body="Message %d" % i) - - #use message_get to read back the messages, and check that we get an empty at the end - for i in range(1, 11): - tag = "queue %d" % i - reply = channel.message_get(no_ack=True, queue="test-get", destination=tag) - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "ok") - self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-empty") - - #repeat for no_ack=False - for i in range(11, 21): - channel.message_transfer(routing_key="test-get", body="Message %d" % i) - - for i in range(11, 21): - tag = "queue %d" % i - reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "ok") - msg = self.client.queue(tag).get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - if (i==13): - msg.ok(batchoffset=-2)#11, 12 & 13 - if(i in [15, 17, 19]): - msg.ok() - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-empty") - - #recover(requeue=True) - channel.message_recover(requeue=True) - - #get the unacked messages again (14, 16, 18, 20) - for i in [14, 16, 18, 20]: - tag = "queue %d" % i - reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "ok") - self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) - reply.ok() - #channel.message_ack(delivery_tag=reply.delivery_tag) - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-empty") - - channel.message_recover(requeue=True) - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "get-empty") - - def test_reference_simple(self): - """ - Test basic ability to handle references - """ - channel = self.channel - channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_consume(queue="ref_queue", destination="c1") - queue = self.client.queue("c1") - - refId = "myref" - channel.message_open(reference=refId) - channel.message_append(reference=refId, bytes="abcd") - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) - channel.synchronous = True - - channel.message_append(reference=refId, bytes="efgh") - channel.message_append(reference=refId, bytes="ijkl") - channel.message_close(reference=refId) - - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - msg = queue.get(timeout=1) - if isinstance(msg, Reference): - #should we force broker to deliver as reference by frame - #size limit? or test that separately? for compliance, - #allowing either seems best for now... - data = msg.get_complete() - else: - data = msg.body - self.assertEquals("abcdefghijkl", data) - - - def test_reference_large(self): - """ - Test basic ability to handle references whose content exceeds max frame size - """ - channel = self.channel - self.queue_declare(queue="ref_queue") - - #generate a big data string (> max frame size of consumer): - data = "0123456789" - for i in range(0, 10): - data += data - #send it inline - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", body=data) - channel.synchronous = True - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - #create a new connection for consumer, with specific max frame size (< data) - other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0}) - ch2 = other.channel(1) - ch2.channel_open() - ch2.message_consume(queue="ref_queue", destination="c1") - queue = other.queue("c1") - - msg = queue.get(timeout=1) - self.assertTrue(isinstance(msg.body, ReferenceId)) - self.assertTrue(msg.reference) - self.assertEquals(data, msg.reference.get_complete()) - - def test_reference_completion(self): - """ - Test that reference transfer are not deemed complete until - closed (therefore are not acked or routed until that point) - """ - channel = self.channel - channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_consume(queue="ref_queue", destination="c1") - queue = self.client.queue("c1") - - refId = "myref" - channel.message_open(reference=refId) - channel.message_append(reference=refId, bytes="abcd") - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) - channel.synchronous = True - - try: - msg = queue.get(timeout=1) - self.fail("Got unexpected message on queue: " + msg) - except Empty: None - - self.assertTrue(not ack.is_complete()) - - channel.message_close(reference=refId) - - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - self.assertDataEquals(channel, queue.get(timeout=1), "abcd") - - def test_reference_multi_transfer(self): - """ - Test that multiple transfer requests for the same reference are - correctly handled. - """ - channel = self.channel - #declare and consume from two queues - channel.queue_declare(queue="q-one", exclusive=True) - channel.queue_declare(queue="q-two", exclusive=True) - channel.message_consume(queue="q-one", destination="q-one") - channel.message_consume(queue="q-two", destination="q-two") - queue1 = self.client.queue("q-one") - queue2 = self.client.queue("q-two") - - #transfer a single ref to both queues (in separate commands) - channel.message_open(reference="my-ref") - channel.synchronous = False - ack1 = channel.message_transfer(routing_key="q-one", body=ReferenceId("my-ref")) - channel.message_append(reference="my-ref", bytes="my data") - ack2 = channel.message_transfer(routing_key="q-two", body=ReferenceId("my-ref")) - channel.synchronous = True - channel.message_close(reference="my-ref") - - #check that both queues have the message - self.assertDataEquals(channel, queue1.get(timeout=1), "my data") - self.assertDataEquals(channel, queue2.get(timeout=1), "my data") - self.assertEmpty(queue1) - self.assertEmpty(queue2) - - #transfer a single ref to the same queue twice (in separate commands) - channel.message_open(reference="my-ref") - channel.synchronous = False - ack1 = channel.message_transfer(routing_key="q-one", message_id="abc", body=ReferenceId("my-ref")) - channel.message_append(reference="my-ref", bytes="second message") - ack2 = channel.message_transfer(routing_key="q-one", message_id="xyz", body=ReferenceId("my-ref")) - channel.synchronous = True - channel.message_close(reference="my-ref") - - msg1 = queue1.get(timeout=1) - msg2 = queue1.get(timeout=1) - #order is undefined - if msg1.message_id == "abc": - self.assertEquals(msg2.message_id, "xyz") - else: - self.assertEquals(msg1.message_id, "xyz") - self.assertEquals(msg2.message_id, "abc") - - #would be legal for the incoming messages to be transfered - #inline or by reference in any combination - - if isinstance(msg1.body, ReferenceId): - self.assertEquals("second message", msg1.reference.get_complete()) - if isinstance(msg2.body, ReferenceId): - if msg1.body != msg2.body: - self.assertEquals("second message", msg2.reference.get_complete()) - #else ok, as same ref as msg1 - else: - self.assertEquals("second message", msg1.body) - if isinstance(msg2.body, ReferenceId): - self.assertEquals("second message", msg2.reference.get_complete()) - else: - self.assertEquals("second message", msg2.body) - - self.assertEmpty(queue1) - - def test_reference_unopened_on_append_error(self): - channel = self.channel - try: - channel.message_append(reference="unopened") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_reference_unopened_on_close_error(self): - channel = self.channel - try: - channel.message_close(reference="unopened") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_reference_unopened_on_transfer_error(self): - channel = self.channel - try: - channel.message_transfer(body=ReferenceId("unopened")) - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_reference_already_opened_error(self): - channel = self.channel - channel.message_open(reference="a") - try: - channel.message_open(reference="a") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_empty_reference(self): - channel = self.channel - channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_consume(queue="ref_queue", destination="c1") - queue = self.client.queue("c1") - - refId = "myref" - channel.message_open(reference=refId) - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", message_id="empty-msg", body=ReferenceId(refId)) - channel.synchronous = True - channel.message_close(reference=refId) - - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - msg = queue.get(timeout=1) - self.assertEquals(msg.message_id, "empty-msg") - self.assertDataEquals(channel, msg, "") - - def test_reject(self): - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) - - channel.message_consume(queue = "q", destination = "consumer") - channel.message_transfer(routing_key = "q", body="blah, blah") - msg = self.client.queue("consumer").get(timeout = 1) - self.assertEquals(msg.body, "blah, blah") - channel.message_cancel(destination = "consumer") - msg.reject() - - channel.message_consume(queue = "q", destination = "checker") - msg = self.client.queue("checker").get(timeout = 1) - self.assertEquals(msg.body, "blah, blah") - - def test_checkpoint(self): - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) - - channel.message_open(reference="my-ref") - channel.message_append(reference="my-ref", bytes="abcdefgh") - channel.message_append(reference="my-ref", bytes="ijklmnop") - channel.message_checkpoint(reference="my-ref", identifier="my-checkpoint") - channel.channel_close() - - channel = self.client.channel(2) - channel.channel_open() - channel.message_consume(queue = "q", destination = "consumer") - offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value - self.assertEquals(offset, 16) - channel.message_append(reference="my-ref", bytes="qrstuvwxyz") - channel.synchronous = False - channel.message_transfer(routing_key="q-one", message_id="abcd", body=ReferenceId("my-ref")) - channel.synchronous = True - channel.message_close(reference="my-ref") - - self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz") - self.assertEmpty(self.client.queue("consumer")) - - - def assertDataEquals(self, channel, msg, expected): - if isinstance(msg.body, ReferenceId): - data = msg.reference.get_complete() - else: - data = msg.body - self.assertEquals("abcdefghijkl", data) - diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py deleted file mode 100644 index d85f04c4c2..0000000000 --- a/qpid/python/tests/queue.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class QueueTests(TestBase): - """Tests for 'methods' on the amqp queue 'class'""" - - def test_purge(self): - """ - Test that the purge method removes messages from the queue - """ - channel = self.channel - #setup, declare a queue and add some messages to it: - channel.exchange_declare(exchange="test-exchange", type="direct") - channel.queue_declare(queue="test-queue", exclusive=True) - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - channel.message_transfer(destination="test-exchange", routing_key="key", body="one") - channel.message_transfer(destination="test-exchange", routing_key="key", body="two") - channel.message_transfer(destination="test-exchange", routing_key="key", body="three") - - #check that the queue now reports 3 messages: - reply = channel.queue_declare(queue="test-queue") - self.assertEqual(3, reply.message_count) - - #now do the purge, then test that three messages are purged and the count drops to 0 - reply = channel.queue_purge(queue="test-queue"); - self.assertEqual(3, reply.message_count) - reply = channel.queue_declare(queue="test-queue") - self.assertEqual(0, reply.message_count) - - #send a further message and consume it, ensuring that the other messages are really gone - channel.message_transfer(destination="test-exchange", routing_key="key", content=Content("four")) - channel.message_consume(queue="test-queue", destination="tag", no_ack=True) - queue = self.client.queue("tag") - msg = queue.get(timeout=1) - self.assertEqual("four", msg.content.body) - - #check error conditions (use new channels): - channel = self.client.channel(2) - channel.channel_open() - try: - #queue specified but doesn't exist: - channel.queue_purge(queue="invalid-queue") - self.fail("Expected failure when purging non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - channel = self.client.channel(3) - channel.channel_open() - try: - #queue not specified and none previously declared for channel: - channel.queue_purge() - self.fail("Expected failure when purging unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - #cleanup - other = self.connect() - channel = other.channel(1) - channel.channel_open() - channel.exchange_delete(exchange="test-exchange") - - def test_declare_exclusive(self): - """ - Test that the exclusive field is honoured in queue.declare - """ - # TestBase.setUp has already opened channel(1) - c1 = self.channel - # Here we open a second separate connection: - other = self.connect() - c2 = other.channel(1) - c2.channel_open() - - #declare an exclusive queue: - c1.queue_declare(queue="exclusive-queue", exclusive="True") - try: - #other connection should not be allowed to declare this: - c2.queue_declare(queue="exclusive-queue", exclusive="True") - self.fail("Expected second exclusive queue_declare to raise a channel exception") - except Closed, e: - self.assertChannelException(405, e.args[0]) - - - def test_declare_passive(self): - """ - Test that the passive field is honoured in queue.declare - """ - channel = self.channel - #declare an exclusive queue: - channel.queue_declare(queue="passive-queue-1", exclusive="True") - channel.queue_declare(queue="passive-queue-1", passive="True") - try: - #other connection should not be allowed to declare this: - channel.queue_declare(queue="passive-queue-2", passive="True") - self.fail("Expected passive declaration of non-existant queue to raise a channel exception") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - - def test_bind(self): - """ - Test various permutations of the queue.bind method - """ - channel = self.channel - channel.queue_declare(queue="queue-1", exclusive="True") - - #straightforward case, both exchange & queue exist so no errors expected: - channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") - - #bind the default queue for the channel (i.e. last one declared): - channel.queue_bind(exchange="amq.direct", routing_key="key2") - - #use the queue name where neither routing key nor queue are specified: - channel.queue_bind(exchange="amq.direct") - - #try and bind to non-existant exchange - try: - channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") - self.fail("Expected bind to non-existant exchange to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - #need to reopen a channel: - channel = self.client.channel(2) - channel.channel_open() - - #try and bind non-existant queue: - try: - channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") - self.fail("Expected bind of non-existant queue to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - - def test_delete_simple(self): - """ - Test core queue deletion behaviour - """ - channel = self.channel - - #straight-forward case: - channel.queue_declare(queue="delete-me") - channel.message_transfer(routing_key="delete-me", body="a") - channel.message_transfer(routing_key="delete-me", body="b") - channel.message_transfer(routing_key="delete-me", body="c") - reply = channel.queue_delete(queue="delete-me") - self.assertEqual(3, reply.message_count) - #check that it has gone be declaring passively - try: - channel.queue_declare(queue="delete-me", passive="True") - self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - #check attempted deletion of non-existant queue is handled correctly: - channel = self.client.channel(2) - channel.channel_open() - try: - channel.queue_delete(queue="i-dont-exist", if_empty="True") - self.fail("Expected delete of non-existant queue to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - - - def test_delete_ifempty(self): - """ - Test that if_empty field of queue_delete is honoured - """ - channel = self.channel - - #create a queue and add a message to it (use default binding): - channel.queue_declare(queue="delete-me-2") - channel.queue_declare(queue="delete-me-2", passive="True") - channel.message_transfer(routing_key="delete-me-2", body="message") - - #try to delete, but only if empty: - try: - channel.queue_delete(queue="delete-me-2", if_empty="True") - self.fail("Expected delete if_empty to fail for non-empty queue") - except Closed, e: - self.assertChannelException(406, e.args[0]) - - #need new channel now: - channel = self.client.channel(2) - channel.channel_open() - - #empty queue: - channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True) - queue = self.client.queue("consumer_tag") - msg = queue.get(timeout=1) - self.assertEqual("message", msg.content.body) - channel.message_cancel(destination="consumer_tag") - - #retry deletion on empty queue: - channel.queue_delete(queue="delete-me-2", if_empty="True") - - #check that it has gone by declaring passively: - try: - channel.queue_declare(queue="delete-me-2", passive="True") - self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - def test_delete_ifunused(self): - """ - Test that if_unused field of queue_delete is honoured - """ - channel = self.channel - - #create a queue and register a consumer: - channel.queue_declare(queue="delete-me-3") - channel.queue_declare(queue="delete-me-3", passive="True") - channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True) - - #need new channel now: - channel2 = self.client.channel(2) - channel2.channel_open() - #try to delete, but only if empty: - try: - channel2.queue_delete(queue="delete-me-3", if_unused="True") - self.fail("Expected delete if_unused to fail for queue with existing consumer") - except Closed, e: - self.assertChannelException(406, e.args[0]) - - - channel.message_cancel(destination="consumer_tag") - channel.queue_delete(queue="delete-me-3", if_unused="True") - #check that it has gone by declaring passively: - try: - channel.queue_declare(queue="delete-me-3", passive="True") - self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - diff --git a/qpid/python/tests/testlib.py b/qpid/python/tests/testlib.py deleted file mode 100644 index f345fbbd80..0000000000 --- a/qpid/python/tests/testlib.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# 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. -# - -# -# Tests for the testlib itself. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from Queue import Empty - -import sys -from traceback import * - -def mytrace(frame, event, arg): - print_stack(frame); - print "====" - return mytrace - -class TestBaseTest(TestBase): - """Verify TestBase functions work as expected""" - - def testAssertEmptyPass(self): - """Test assert empty works""" - self.queue_declare(queue="empty") - q = self.consume("empty") - self.assertEmpty(q) - try: - q.get(timeout=1) - self.fail("Queue is not empty.") - except Empty: None # Ignore - - def testAssertEmptyFail(self): - self.queue_declare(queue="full") - q = self.consume("full") - self.channel.message_transfer(routing_key="full", body="") - try: - self.assertEmpty(q); - self.fail("assertEmpty did not assert on non-empty queue") - except AssertionError: None # Ignore - - def testMessageProperties(self): - """Verify properties are passed with message""" - props={"x":1, "y":2} - self.queue_declare(queue="q") - q = self.consume("q") - self.assertPublishGet(q, routing_key="q", properties=props) - - - diff --git a/qpid/python/tests/tx.py b/qpid/python/tests/tx.py deleted file mode 100644 index 0f6b4f5cd1..0000000000 --- a/qpid/python/tests/tx.py +++ /dev/null @@ -1,188 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class TxTests(TestBase): - """ - Tests for 'methods' on the amqp tx 'class' - """ - - def test_commit(self): - """ - Test that commited publishes are delivered and commited acks are not re-delivered - """ - channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c") - channel.tx_commit() - - #check results - for i in range(1, 5): - msg = queue_c.get(timeout=1) - self.assertEqual("TxMessage %d" % i, msg.body) - msg.ok() - - msg = queue_b.get(timeout=1) - self.assertEqual("TxMessage 6", msg.body) - msg.ok() - - msg = queue_a.get(timeout=1) - self.assertEqual("TxMessage 7", msg.body) - msg.ok() - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) - except Empty: None - - #cleanup - channel.tx_commit() - - def test_auto_rollback(self): - """ - Test that a channel closed with an open transaction is effectively rolled back - """ - channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) - except Empty: None - - channel.tx_rollback() - - #check results - for i in range(1, 5): - msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - msg.ok() - - msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.body) - msg.ok() - - msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.body) - msg.ok() - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) - except Empty: None - - #cleanup - channel.tx_commit() - - def test_rollback(self): - """ - Test that rolled back publishes are not delivered and rolled back acks are re-delivered - """ - channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) - except Empty: None - - channel.tx_rollback() - - #check results - for i in range(1, 5): - msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - msg.ok() - - msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.body) - msg.ok() - - msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.body) - msg.ok() - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) - except Empty: None - - #cleanup - channel.tx_commit() - - def perform_txn_work(self, channel, name_a, name_b, name_c): - """ - Utility method that does some setup and some work under a transaction. Used for testing both - commit and rollback - """ - #setup: - channel.queue_declare(queue=name_a, exclusive=True) - channel.queue_declare(queue=name_b, exclusive=True) - channel.queue_declare(queue=name_c, exclusive=True) - - key = "my_key_" + name_b - topic = "my_topic_" + name_c - - channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) - channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) - - for i in range(1, 5): - channel.message_transfer(routing_key=name_a, body="Message %d" % i) - - channel.message_transfer(routing_key=key, destination="amq.direct", body="Message 6") - channel.message_transfer(routing_key=topic, destination="amq.topic", body="Message 7") - - channel.tx_select() - - #consume and ack messages - channel.message_consume(queue=name_a, destination="sub_a", no_ack=False) - queue_a = self.client.queue("sub_a") - for i in range(1, 5): - msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - msg.ok(batchoffset=-3) - - channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) - queue_b = self.client.queue("sub_b") - msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.body) - msg.ok() - - sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False) - queue_c = self.client.queue("sub_c") - msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.body) - msg.ok() - - #publish messages - for i in range(1, 5): - channel.message_transfer(routing_key=topic, destination="amq.topic", body="TxMessage %d" % i) - - channel.message_transfer(routing_key=key, destination="amq.direct", body="TxMessage 6") - channel.message_transfer(routing_key=name_a, body="TxMessage 7") - - return queue_a, queue_b, queue_c -- cgit v1.2.1 From 2182d9220f137f34677853f561876c642a7ce244 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 30 May 2007 22:09:08 +0000 Subject: Load domain docs into the python spec structure. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@542951 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 4f0661bcbc..bb0e7eb58c 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -85,6 +85,7 @@ class Spec(Metadata): self.minor = minor self.file = file self.constants = SpecContainer() + self.domains = SpecContainer() self.classes = SpecContainer() # methods indexed by classname_methname self.methods = {} @@ -139,6 +140,19 @@ class Constant(Metadata): self.klass = klass self.docs = docs +class Domain(Metadata): + + PRINT=["name", "type"] + + def __init__(self, spec, id, name, type, description, docs): + Metadata.__init__(self) + self.spec = spec + self.id = id + self.name = name + self.type = type + self.description = description + self.docs = docs + class Class(Metadata): PRINT=["name", "id"] @@ -251,13 +265,23 @@ class Field(Metadata): PRINT=["name", "id", "type"] - def __init__(self, name, id, type, docs): + def __init__(self, name, id, type, domain, description, docs): Metadata.__init__(self) self.name = name self.id = id self.type = type + self.domain = domain + self.description = description self.docs = docs +def get_desc(nd): + label = nd.get("@label") + if not label: + label = nd.text + if label: + label = label.strip() + return label + def get_docs(nd): return [n.text for n in nd["doc"]] @@ -267,9 +291,13 @@ def load_fields(nd, l, domains): type = f_nd["@domain"] except KeyError: type = f_nd["@type"] - while domains.has_key(type) and domains[type] != type: - type = domains[type] - l.add(Field(pythonize(f_nd["@name"]), f_nd.index(), type, get_docs(f_nd))) + type = pythonize(type) + domain = None + while domains.has_key(type) and domains[type].type != type: + domain = domains[type] + type = domain.type + l.add(Field(pythonize(f_nd["@name"]), f_nd.index(), type, domain, + get_desc(f_nd), get_docs(f_nd))) def load(specfile, *errata): doc = xmlutil.parse(specfile) @@ -284,9 +312,10 @@ def load(specfile, *errata): spec.constants.add(const) # domains are typedefs - domains = {} for nd in root["domain"]: - domains[nd["@name"]] = nd["@type"] + spec.domains.add(Domain(spec, nd.index(), pythonize(nd["@name"]), + pythonize(nd["@type"]), get_desc(nd), + get_docs(nd))) # classes for c_nd in root["class"]: @@ -299,7 +328,7 @@ def load(specfile, *errata): klass = spec.classes.byname[cname] added_methods = [] - load_fields(c_nd, klass.fields, domains) + load_fields(c_nd, klass.fields, spec.domains.byname) for m_nd in c_nd["method"]: mname = pythonize(m_nd["@name"]) if root == spec_root: @@ -308,13 +337,13 @@ def load(specfile, *errata): m_nd.get_bool("@content", False), [pythonize(nd["@name"]) for nd in m_nd["response"]], m_nd.get_bool("@synchronous", False), - m_nd.text, + get_desc(m_nd), get_docs(m_nd)) klass.methods.add(meth) added_methods.append(meth) else: meth = klass.methods.byname[mname] - load_fields(m_nd, meth.fields, domains) + load_fields(m_nd, meth.fields, spec.domains.byname) # resolve the responses for m in added_methods: m.responses = [klass.methods.byname[r] for r in m.responses] -- cgit v1.2.1 From bee2ac89c72ee96396e8fb3756fab5002a2c5d96 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 30 May 2007 22:09:47 +0000 Subject: example mods git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@542954 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/server | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/server b/qpid/python/server index 4204bc8515..56f0f32081 100755 --- a/qpid/python/server +++ b/qpid/python/server @@ -1,5 +1,5 @@ #!/usr/bin/env python -from qpid import spec +import qpid from qpid.connection import Connection, listen from qpid.delegate import Delegate from qpid.peer import Peer @@ -18,7 +18,7 @@ class Server(Delegate): msg.ok() -spec = spec.load("../specs/amqp.0-9.xml") +spec = qpid.spec.load("../specs/amqp.0-9.xml") for io in listen("0.0.0.0", 5672): c = Connection(io, spec) -- cgit v1.2.1 From 841952bc7ff00003df619701ac481e27d7bf9635 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 30 May 2007 22:11:24 +0000 Subject: added listener support to queues, also added support for non version specific tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@542955 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegate.py | 13 +- qpid/python/qpid/queue.py | 30 ++- qpid/python/qpid/testlib.py | 5 +- qpid/python/tests/__init__.py | 20 ++ qpid/python/tests/codec.py | 611 ++++++++++++++++++++++++++++++++++++++++++ qpid/python/tests/queue.py | 76 ++++++ 6 files changed, 742 insertions(+), 13 deletions(-) create mode 100644 qpid/python/tests/__init__.py create mode 100644 qpid/python/tests/codec.py create mode 100644 qpid/python/tests/queue.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py index 90e5c1edc8..8f5033e485 100644 --- a/qpid/python/qpid/delegate.py +++ b/qpid/python/qpid/delegate.py @@ -21,9 +21,12 @@ Delegate implementation intended for use with the peer module. """ -import threading, inspect +import threading, inspect, traceback, sys from connection import Method, Request, Response +def _handler_name(method): + return "%s_%s" % (method.klass.name, method.name) + class Delegate: def __init__(self): @@ -36,11 +39,15 @@ class Delegate: try: handler = self.handlers[method] except KeyError: - name = "%s_%s" % (method.klass.name, method.name) + name = _handler_name(method) handler = getattr(self, name) self.handlers[method] = handler - return handler(channel, frame) + try: + return handler(channel, frame) + except: + print >> sys.stderr, "Error in handler: %s\n\n%s" % \ + (_handler_name(method), traceback.format_exc()) def close(self, reason): print "Connection closed: %s" % reason diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index 5438b328ab..af0565b6cc 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -31,15 +31,29 @@ class Queue(BaseQueue): END = object() + def __init__(self, *args, **kwargs): + BaseQueue.__init__(self, *args, **kwargs) + self._real_put = self.put + self.listener = self._real_put + def close(self): self.put(Queue.END) def get(self, block = True, timeout = None): - result = BaseQueue.get(self, block, timeout) - if result == Queue.END: - # this guarantees that any other waiting threads or any future - # calls to get will also result in a Closed exception - self.put(Queue.END) - raise Closed() - else: - return result + self.put = self._real_put + try: + result = BaseQueue.get(self, block, timeout) + if result == Queue.END: + # this guarantees that any other waiting threads or any future + # calls to get will also result in a Closed exception + self.put(Queue.END) + raise Closed() + else: + return result + finally: + self.put = self.listener + pass + + def listen(self, listener): + self.listener = listener + self.put = self.listener diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index ba2dbe9fc3..2a5e4dcfcd 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -122,10 +122,11 @@ Options: print "Using specification from:", self.specfile self.spec = qpid.spec.load(self.specfile, *self.errata) if len(self.tests) == 0: + self.tests=findmodules("tests") if self.use08spec(): - self.tests=findmodules("tests_0-8") + self.tests+=findmodules("tests_0-8") else: - self.tests=findmodules("tests_0-9") + self.tests+=findmodules("tests_0-9") def testSuite(self): class IgnoringTestSuite(unittest.TestSuite): diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py new file mode 100644 index 0000000000..9a09d2d04f --- /dev/null +++ b/qpid/python/tests/__init__.py @@ -0,0 +1,20 @@ +# Do not delete - marks this directory as a python package. + +# +# 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. +# diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py new file mode 100644 index 0000000000..12dd5c85fc --- /dev/null +++ b/qpid/python/tests/codec.py @@ -0,0 +1,611 @@ +#!/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 unittest +from qpid.codec import Codec +from cStringIO import StringIO +from qpid.reference import ReferenceId + +__doc__ = """ + + This is a unit test script for qpid/codec.py + + It can be run standalone or as part of the existing test framework. + + To run standalone: + ------------------- + + Place in the qpid/python/tests/ directory and type... + + python codec.py + + A brief output will be printed on screen. The verbose output will be placed inn a file called + codec_unit_test_output.txt. [TODO: make this filename configurable] + + To run as part of the existing test framework: + ----------------------------------------------- + + python run-tests tests.codec + + Change History: + ----------------- + Jimmy John 05/19/2007 Initial draft + Jimmy John 05/22/2007 Implemented comments by Rafael Schloming + + +""" + + +# -------------------------------------- +# -------------------------------------- +class BaseDataTypes(unittest.TestCase): + + """ + Base class containing common functions + """ + + # --------------- + def setUp(self): + """ + standard setUp for unitetest (refer unittest documentation for details) + """ + self.codec = Codec(StringIO()) + + # ------------------ + def tearDown(self): + """ + standard tearDown for unitetest (refer unittest documentation for details) + """ + self.codec.stream.flush() + self.codec.stream.close() + + # ---------------------------------------- + def callFunc(self, functionName, *args): + """ + helper function - given a function name and arguments, calls the function with the args and + returns the contents of the stream + """ + getattr(self.codec, functionName)(args[0]) + return self.codec.stream.getvalue() + + # ---------------------------------------- + def readFunc(self, functionName, *args): + """ + helper function - creates a input stream and then calls the function with arguments as have been + supplied + """ + self.codec.stream = StringIO(args[0]) + return getattr(self.codec, functionName)() + + +# ---------------------------------------- +# ---------------------------------------- +class IntegerTestCase(BaseDataTypes): + + """ + Handles octet, short, long, long long + + """ + + # ------------------------- + def __init__(self, *args): + """ + sets constants for use in tests + """ + + BaseDataTypes.__init__(self, *args) + self.const_integer = 2 + self.const_integer_octet_encoded = '\x02' + self.const_integer_short_encoded = '\x00\x02' + self.const_integer_long_encoded = '\x00\x00\x00\x02' + self.const_integer_long_long_encoded = '\x00\x00\x00\x00\x00\x00\x00\x02' + + # -------------------------- # + # Unsigned Octect - 8 bits # + # -------------------------- # + + # -------------------------- + def test_unsigned_octet(self): + """ + ubyte format requires 0<=number<=255 + """ + self.failUnlessEqual(self.callFunc('encode_octet', self.const_integer), self.const_integer_octet_encoded, 'octect encoding FAILED...') + + # ------------------------------------------- + def test_octet_out_of_upper_range(self): + """ + testing for input above acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_octet, 256) + + # ------------------------------------------- + def test_uoctet_out_of_lower_range(self): + """ + testing for input below acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_octet, -1) + + # --------------------------------- + def test_uoctet_with_fraction(self): + """ + the fractional part should be ignored... + """ + self.failUnlessEqual(self.callFunc('encode_octet', 2.5), self.const_integer_octet_encoded, 'octect encoding FAILED with fractions...') + + # ------------------------------------ + def test_unsigned_octet_decode(self): + """ + octet decoding + """ + self.failUnlessEqual(self.readFunc('decode_octet', self.const_integer_octet_encoded), self.const_integer, 'octect decoding FAILED...') + + # ----------------------------------- # + # Unsigned Short Integers - 16 bits # + # ----------------------------------- # + + # ----------------------- + def test_ushort_int(self): + """ + testing unsigned short integer + """ + self.failUnlessEqual(self.callFunc('encode_short', self.const_integer), self.const_integer_short_encoded, 'short encoding FAILED...') + + # ------------------------------------------- + def test_ushort_int_out_of_upper_range(self): + """ + testing for input above acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_short, 65536) + + # ------------------------------------------- + def test_ushort_int_out_of_lower_range(self): + """ + testing for input below acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_short, -1) + + # --------------------------------- + def test_ushort_int_with_fraction(self): + """ + the fractional part should be ignored... + """ + self.failUnlessEqual(self.callFunc('encode_short', 2.5), self.const_integer_short_encoded, 'short encoding FAILED with fractions...') + + # ------------------------------------ + def test_ushort_int_decode(self): + """ + unsigned short decoding + """ + self.failUnlessEqual(self.readFunc('decode_short', self.const_integer_short_encoded), self.const_integer, 'unsigned short decoding FAILED...') + + + # ---------------------------------- # + # Unsigned Long Integers - 32 bits # + # ---------------------------------- # + + # ----------------------- + def test_ulong_int(self): + """ + testing unsigned long iteger + """ + self.failUnlessEqual(self.callFunc('encode_long', self.const_integer), self.const_integer_long_encoded, 'long encoding FAILED...') + + # ------------------------------------------- + def test_ulong_int_out_of_upper_range(self): + """ + testing for input above acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_long, 4294967296) + + # ------------------------------------------- + def test_ulong_int_out_of_lower_range(self): + """ + testing for input below acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_long, -1) + + # --------------------------------- + def test_ulong_int_with_fraction(self): + """ + the fractional part should be ignored... + """ + self.failUnlessEqual(self.callFunc('encode_long', 2.5), self.const_integer_long_encoded, 'long encoding FAILED with fractions...') + + # ------------------------------- + def test_ulong_int_decode(self): + """ + unsigned long decoding + """ + self.failUnlessEqual(self.readFunc('decode_long', self.const_integer_long_encoded), self.const_integer, 'unsigned long decoding FAILED...') + + + # --------------------------------------- # + # Unsigned Long Long Integers - 64 bits # + # --------------------------------------- # + + # ----------------------- + def test_ulong_long_int(self): + """ + testing unsinged long long integer + """ + self.failUnlessEqual(self.callFunc('encode_longlong', self.const_integer), self.const_integer_long_long_encoded, 'long long encoding FAILED...') + + # ------------------------------------------- + def test_ulong_long_int_out_of_upper_range(self): + """ + testing for input above acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_longlong, 18446744073709551616) + + # ------------------------------------------- + def test_ulong_long_int_out_of_lower_range(self): + """ + testing for input below acceptable range + """ + self.failUnlessRaises(Exception, self.codec.encode_longlong, -1) + + # --------------------------------- + def test_ulong_long_int_with_fraction(self): + """ + the fractional part should be ignored... + """ + self.failUnlessEqual(self.callFunc('encode_longlong', 2.5), self.const_integer_long_long_encoded, 'long long encoding FAILED with fractions...') + + # ------------------------------------ + def test_ulong_long_int_decode(self): + """ + unsigned long long decoding + """ + self.failUnlessEqual(self.readFunc('decode_longlong', self.const_integer_long_long_encoded), self.const_integer, 'unsigned long long decoding FAILED...') + +# ----------------------------------- +# ----------------------------------- +class BitTestCase(BaseDataTypes): + + """ + Handles bits + """ + + # ---------------------------------------------- + def callFunc(self, functionName, *args): + """ + helper function + """ + for ele in args: + getattr(self.codec, functionName)(ele) + + self.codec.flush() + return self.codec.stream.getvalue() + + # ------------------- + def test_bit1(self): + """ + sends in 11 + """ + self.failUnlessEqual(self.callFunc('encode_bit', 1, 1), '\x03', '11 bit encoding FAILED...') + + # ------------------- + def test_bit2(self): + """ + sends in 10011 + """ + self.failUnlessEqual(self.callFunc('encode_bit', 1, 1, 0, 0, 1), '\x13', '10011 bit encoding FAILED...') + + # ------------------- + def test_bit3(self): + """ + sends in 1110100111 [10 bits(right to left), should be compressed into two octets] + """ + self.failUnlessEqual(self.callFunc('encode_bit', 1,1,1,0,0,1,0,1,1,1), '\xa7\x03', '1110100111(right to left) bit encoding FAILED...') + + # ------------------------------------ + def test_bit_decode_1(self): + """ + decode bit 1 + """ + self.failUnlessEqual(self.readFunc('decode_bit', '\x01'), 1, 'decode bit 1 FAILED...') + + # ------------------------------------ + def test_bit_decode_0(self): + """ + decode bit 0 + """ + self.failUnlessEqual(self.readFunc('decode_bit', '\x00'), 0, 'decode bit 0 FAILED...') + +# ----------------------------------- +# ----------------------------------- +class StringTestCase(BaseDataTypes): + + """ + Handles short strings, long strings + """ + + # ------------------------------------------------------------- # + # Short Strings - 8 bit length followed by zero or more octets # + # ------------------------------------------------------------- # + + # --------------------------------------- + def test_short_string_zero_length(self): + """ + 0 length short string + """ + self.failUnlessEqual(self.callFunc('encode_shortstr', ''), '\x00', '0 length short string encoding FAILED...') + + # ------------------------------------------- + def test_short_string_positive_length(self): + """ + positive length short string + """ + self.failUnlessEqual(self.callFunc('encode_shortstr', 'hello world'), '\x0bhello world', 'positive length short string encoding FAILED...') + + # ------------------------------------------- + def test_short_string_out_of_upper_range(self): + """ + string length > 255 + """ + self.failUnlessRaises(Exception, self.codec.encode_shortstr, 'x'*256) + + # ------------------------------------ + def test_short_string_decode(self): + """ + short string decode + """ + self.failUnlessEqual(self.readFunc('decode_shortstr', '\x0bhello world'), 'hello world', 'short string decode FAILED...') + + + # ------------------------------------------------------------- # + # Long Strings - 32 bit length followed by zero or more octets # + # ------------------------------------------------------------- # + + # --------------------------------------- + def test_long_string_zero_length(self): + """ + 0 length long string + """ + self.failUnlessEqual(self.callFunc('encode_longstr', ''), '\x00\x00\x00\x00', '0 length long string encoding FAILED...') + + # ------------------------------------------- + def test_long_string_positive_length(self): + """ + positive length long string + """ + self.failUnlessEqual(self.callFunc('encode_longstr', 'hello world'), '\x00\x00\x00\x0bhello world', 'positive length long string encoding FAILED...') + + # ------------------------------------ + def test_long_string_decode(self): + """ + long string decode + """ + self.failUnlessEqual(self.readFunc('decode_longstr', '\x00\x00\x00\x0bhello world'), 'hello world', 'long string decode FAILED...') + + +# -------------------------------------- +# -------------------------------------- +class TimestampTestCase(BaseDataTypes): + + """ + No need of any test cases here as timestamps are implemented as long long which is tested above + """ + pass + +# --------------------------------------- +# --------------------------------------- +class FieldTableTestCase(BaseDataTypes): + + """ + Handles Field Tables + + Only S/I type messages seem to be implemented currently + """ + + # ------------------------- + def __init__(self, *args): + """ + sets constants for use in tests + """ + + BaseDataTypes.__init__(self, *args) + self.const_field_table_dummy_dict = {'$key1':'value1','$key2':'value2'} + self.const_field_table_dummy_dict_encoded = '\x00\x00\x00\x22\x05$key2S\x00\x00\x00\x06value2\x05$key1S\x00\x00\x00\x06value1' + + # ------------------------------------------- + def test_field_table_name_value_pair(self): + """ + valid name value pair + """ + self.failUnlessEqual(self.callFunc('encode_table', {'$key1':'value1'}), '\x00\x00\x00\x11\x05$key1S\x00\x00\x00\x06value1', 'valid name value pair encoding FAILED...') + + # ------------------------------------------- + def test_field_table_invalid_field_name(self): + """ + invalid field name + """ + self.failUnlessRaises(Exception, self.codec.encode_table, {'1key1':'value1'}) + + # ---------------------------------------------------- + def test_field_table_invalid_field_name_length(self): + """ + field names can have a maximum length of 128 chars + """ + self.failUnlessRaises(Exception, self.codec.encode_table, {'x'*129:'value1'}) + + # --------------------------------------------------- + def test_field_table_multiple_name_value_pair(self): + """ + multiple name value pair + """ + self.failUnlessEqual(self.callFunc('encode_table', self.const_field_table_dummy_dict), self.const_field_table_dummy_dict_encoded, 'multiple name value pair encoding FAILED...') + + # ------------------------------------ + def test_field_table_decode(self): + """ + field table decode + """ + self.failUnlessEqual(self.readFunc('decode_table', self.const_field_table_dummy_dict_encoded), self.const_field_table_dummy_dict, 'field table decode FAILED...') + + +# ------------------------------------ +# ------------------------------------ +class ContentTestCase(BaseDataTypes): + + """ + Handles Content data types + """ + + # ----------------------------- + def test_content_inline(self): + """ + inline content + """ + self.failUnlessEqual(self.callFunc('encode_content', 'hello inline message'), '\x00\x00\x00\x00\x14hello inline message', 'inline content encoding FAILED...') + + # -------------------------------- + def test_content_reference(self): + """ + reference content + """ + self.failUnlessEqual(self.callFunc('encode_content', ReferenceId('dummyId')), '\x01\x00\x00\x00\x07dummyId', 'reference content encoding FAILED...') + + # ------------------------------------ + def test_content_inline_decode(self): + """ + inline content decode + """ + self.failUnlessEqual(self.readFunc('decode_content', '\x00\x00\x00\x00\x14hello inline message'), 'hello inline message', 'inline content decode FAILED...') + + # ------------------------------------ + def test_content_reference_decode(self): + """ + reference content decode + """ + self.failUnlessEqual(self.readFunc('decode_content', '\x01\x00\x00\x00\x07dummyId').id, 'dummyId', 'reference content decode FAILED...') + +# ------------------------ # +# Pre - existing test code # +# ------------------------ # + +# --------------------- +def test(type, value): + """ + old test function cut/copy/paste from qpid/codec.py + """ + if isinstance(value, (list, tuple)): + values = value + else: + values = [value] + stream = StringIO() + codec = Codec(stream) + for v in values: + codec.encode(type, v) + codec.flush() + enc = stream.getvalue() + stream.reset() + dup = [] + for i in xrange(len(values)): + dup.append(codec.decode(type)) + if values != dup: + raise AssertionError("%r --> %r --> %r" % (values, enc, dup)) + +# ----------------------- +def dotest(type, value): + """ + old test function cut/copy/paste from qpid/codec.py + """ + args = (type, value) + test(*args) + +# ------------- +def oldtests(): + """ + old test function cut/copy/paste from qpid/codec.py + """ + for value in ("1", "0", "110", "011", "11001", "10101", "10011"): + for i in range(10): + dotest("bit", map(lambda x: x == "1", value*i)) + + for value in ({}, {"asdf": "fdsa", "fdsa": 1, "three": 3}, {"one": 1}): + dotest("table", value) + + for type in ("octet", "short", "long", "longlong"): + for value in range(0, 256): + dotest(type, value) + + for type in ("shortstr", "longstr"): + for value in ("", "a", "asdf"): + dotest(type, value) + +# ----------------------------------------- +class oldTests(unittest.TestCase): + + """ + class to handle pre-existing test cases + """ + + # --------------------------- + def test_oldtestcases(self): + """ + call the old tests + """ + return oldtests() + +# --------------------------- +# --------------------------- +if __name__ == '__main__': + + codec_test_suite = unittest.TestSuite() + + #adding all the test suites... + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(IntegerTestCase)) + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(BitTestCase)) + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(StringTestCase)) + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TimestampTestCase)) + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(FieldTableTestCase)) + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(ContentTestCase)) + + #loading pre-existing test case from qpid/codec.py + codec_test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(oldTests)) + + run_output_stream = StringIO() + test_runner = unittest.TextTestRunner(run_output_stream, '', '') + test_result = test_runner.run(codec_test_suite) + + print '\n%d test run...' % (test_result.testsRun) + + if test_result.wasSuccessful(): + print '\nAll tests successful\n' + + if test_result.failures: + print '\n----------' + print '%d FAILURES:' % (len(test_result.failures)) + print '----------\n' + for failure in test_result.failures: + print str(failure[0]) + ' ... FAIL' + + if test_result.errors: + print '\n---------' + print '%d ERRORS:' % (len(test_result.errors)) + print '---------\n' + + for error in test_result.errors: + print str(error[0]) + ' ... ERROR' + + f = open('codec_unit_test_output.txt', 'w') + f.write(str(run_output_stream.getvalue())) + f.close() diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py new file mode 100644 index 0000000000..d2e495d207 --- /dev/null +++ b/qpid/python/tests/queue.py @@ -0,0 +1,76 @@ +# Do not delete - marks this directory as a python package. + +# +# 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 threading, time +from unittest import TestCase +from qpid.queue import Queue, Empty, Closed + + +class QueueTest (TestCase): + + # The qpid queue class just provides sime simple extensions to + # python's standard queue data structure, so we don't need to test + # all the queue functionality. + + def test_listen(self): + LISTEN = object() + GET = object() + EMPTY = object() + + q = Queue(0) + values = [] + q.listen(lambda x: values.append((LISTEN, x))) + q.put(1) + assert values[-1] == (LISTEN, 1) + q.put(2) + assert values[-1] == (LISTEN, 2) + + class Getter(threading.Thread): + + def run(self): + try: + values.append((GET, q.get(timeout=10))) + except Empty: + values.append(EMPTY) + + g = Getter() + g.start() + # let the other thread reach the get + time.sleep(2) + q.put(3) + g.join() + + assert values[-1] == (GET, 3) + + q.put(4) + assert values[-1] == (LISTEN, 4) + + def test_close(self): + q = Queue(0) + q.put(1); q.put(2); q.put(3); q.close() + assert q.get() == 1 + assert q.get() == 2 + assert q.get() == 3 + for i in range(10): + try: + q.get() + raise AssertionError("expected Closed") + except Closed: + pass -- cgit v1.2.1 From 6b965ef06930f48540723a3712cc5d9aa71a6e7f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 30 May 2007 22:12:19 +0000 Subject: tests moved to test directory git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@542957 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 3c1e73c2e6..b0b596b89c 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -214,41 +214,3 @@ class Codec: return self.decode_longstr() else: return ReferenceId(self.decode_longstr()) - -def test(type, value): - if isinstance(value, (list, tuple)): - values = value - else: - values = [value] - stream = StringIO() - codec = Codec(stream) - for v in values: - codec.encode(type, v) - codec.flush() - enc = stream.getvalue() - stream.reset() - dup = [] - for i in xrange(len(values)): - dup.append(codec.decode(type)) - if values != dup: - raise AssertionError("%r --> %r --> %r" % (values, enc, dup)) - -if __name__ == "__main__": - def dotest(type, value): - args = (type, value) - test(*args) - - for value in ("1", "0", "110", "011", "11001", "10101", "10011"): - for i in range(10): - dotest("bit", map(lambda x: x == "1", value*i)) - - for value in ({}, {"asdf": "fdsa", "fdsa": 1, "three": 3}, {"one": 1}): - dotest("table", value) - - for type in ("octet", "short", "long", "longlong"): - for value in range(0, 256): - dotest(type, value) - - for type in ("shortstr", "longstr"): - for value in ("", "a", "asdf"): - dotest(type, value) -- cgit v1.2.1 From e013e7138f157bfb916cf2de57564bac8ca192fd Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 31 May 2007 12:39:40 +0000 Subject: disabled the tests directory until the failures are fixed git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@543123 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 2a5e4dcfcd..48a6755d25 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -122,7 +122,7 @@ Options: print "Using specification from:", self.specfile self.spec = qpid.spec.load(self.specfile, *self.errata) if len(self.tests) == 0: - self.tests=findmodules("tests") +# self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") else: -- cgit v1.2.1 From d276a19bff4afbe03d458e82e5ef35c64efe892b Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 5 Jun 2007 15:54:22 +0000 Subject: Some tests and fixes for dtx preview. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@544522 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/amqp-doc | 6 +- qpid/python/qpid/spec.py | 18 +- qpid/python/qpid/testlib.py | 6 +- qpid/python/tests_0-9/dtx.py | 540 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+), 13 deletions(-) create mode 100644 qpid/python/tests_0-9/dtx.py (limited to 'qpid/python') diff --git a/qpid/python/amqp-doc b/qpid/python/amqp-doc index 00226d63cb..1f5910f942 100755 --- a/qpid/python/amqp-doc +++ b/qpid/python/amqp-doc @@ -37,15 +37,17 @@ Options: """ % (msg, sys.argv[0])).strip() try: - opts, args = getopt(sys.argv[1:], "s:e", ["regexp", "spec="]) + opts, args = getopt(sys.argv[1:], "s:ea:", ["regexp", "spec=", "additional="]) except GetoptError, e: die(str(e)) regexp = False spec = "../specs/amqp.0-9.xml" +errata = [] for k, v in opts: if k == "-e" or k == "--regexp": regexp = True if k == "-s" or k == "--spec": spec = v + if k == "-a" or k == "--additional": errata.append(v) if regexp: def match(pattern, value): @@ -57,7 +59,7 @@ else: def match(pattern, value): return fnmatch(value, pattern) -spec = load(spec) +spec = load(spec, *errata) methods = {} patterns = args for pattern in patterns: diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index bb0e7eb58c..f8e37737e2 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -309,8 +309,10 @@ def load(specfile, *errata): for nd in root["constant"]: const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), nd.get("@class"), get_docs(nd)) - spec.constants.add(const) - + try: + spec.constants.add(const) + except ValueError, e: + print "Warning:", e # domains are typedefs for nd in root["domain"]: spec.domains.add(Domain(spec, nd.index(), pythonize(nd["@name"]), @@ -320,18 +322,20 @@ def load(specfile, *errata): # classes for c_nd in root["class"]: cname = pythonize(c_nd["@name"]) - if root == spec_root: + if spec.classes.byname.has_key(cname): + klass = spec.classes.byname[cname] + else: klass = Class(spec, cname, int(c_nd["@index"]), c_nd["@handler"], get_docs(c_nd)) spec.classes.add(klass) - else: - klass = spec.classes.byname[cname] added_methods = [] load_fields(c_nd, klass.fields, spec.domains.byname) for m_nd in c_nd["method"]: mname = pythonize(m_nd["@name"]) - if root == spec_root: + if klass.methods.byname.has_key(mname): + meth = klass.methods.byname[mname] + else: meth = Method(klass, mname, int(m_nd["@index"]), m_nd.get_bool("@content", False), @@ -341,8 +345,6 @@ def load(specfile, *errata): get_docs(m_nd)) klass.methods.add(meth) added_methods.append(meth) - else: - meth = klass.methods.byname[mname] load_fields(m_nd, meth.fields, spec.domains.byname) # resolve the responses for m in added_methods: diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 48a6755d25..fa904ff029 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -99,7 +99,7 @@ Options: self.specfile = "0-8" self.errata = [] try: - opts, self.tests = getopt(args, "s:b:h?dvi:I:", ["help", "spec", "server", "verbose", "ignore", "ignore-file"]) + opts, self.tests = getopt(args, "s:e:b:h?dvi:I:", ["help", "spec", "errata=", "server", "verbose", "ignore", "ignore-file"]) except GetoptError, e: self._die(str(e)) for opt, value in opts: @@ -278,14 +278,14 @@ class TestBase(unittest.TestCase): self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) def assertChannelException(self, expectedCode, message): - if not isinstance(message, Message): self.fail("expected channel_close method") + if not isinstance(message, Message): self.fail("expected channel_close method, got %s" % (message)) self.assertEqual("channel", message.method.klass.name) self.assertEqual("close", message.method.name) self.assertEqual(expectedCode, message.reply_code) def assertConnectionException(self, expectedCode, message): - if not isinstance(message, Message): self.fail("expected connection_close method") + if not isinstance(message, Message): self.fail("expected connection_close method, got %s" % (message)) self.assertEqual("connection", message.method.klass.name) self.assertEqual("close", message.method.name) self.assertEqual(expectedCode, message.reply_code) diff --git a/qpid/python/tests_0-9/dtx.py b/qpid/python/tests_0-9/dtx.py new file mode 100644 index 0000000000..ec82c72d49 --- /dev/null +++ b/qpid/python/tests_0-9/dtx.py @@ -0,0 +1,540 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from struct import pack, unpack + +class DtxTests(TestBase): + """ + Tests for the amqp dtx related classes. + + Tests of the form test_simple_xxx test the basic transactional + behaviour. The approach here is to 'swap' a message from one queue + to another by consuming and re-publishing in the same + transaction. That transaction is then completed in different ways + and the appropriate result verified. + + The other tests enforce more specific rules and behaviour on a + per-method or per-field basis. + """ + + XA_RBROLLBACK = 1 + XA_OK = 8 + + def test_simple_commit(self): + """ + Test basic one-phase commit behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "commit") + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #commit + self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).flags) + + #check result + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(1, "queue-b") + self.assertMessageId("commit", "queue-b") + + def test_simple_prepare_commit(self): + """ + Test basic two-phase commit behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "prepare-commit") + + #prepare + self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).flags) + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #commit + self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).flags) + + #check result + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(1, "queue-b") + self.assertMessageId("prepare-commit", "queue-b") + + + def test_simple_rollback(self): + """ + Test basic rollback behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "rollback") + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #rollback + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).flags) + + #check result + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("rollback", "queue-a") + + def test_simple_prepare_rollback(self): + """ + Test basic rollback behaviour after the transaction has been prepared. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "prepare-rollback") + + #prepare + self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).flags) + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #rollback + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).flags) + + #check result + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("prepare-rollback", "queue-a") + + def test_select_required(self): + """ + check that an error is flagged if select is not issued before + start or end + """ + channel = self.channel + tx = self.xid("dummy") + try: + channel.dtx_demarcation_start(xid=tx) + + #if we get here we have failed, but need to do some cleanup: + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + self.fail("Channel not selected for use with dtx, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_start_already_known(self): + """ + Verify that an attempt to start an association with a + transaction that is already known is not allowed (unless the + join flag is set). + """ + #create two channels on different connection & select them for use with dtx: + channel1 = self.channel + channel1.dtx_demarcation_select() + + other = self.connect() + channel2 = other.channel(1) + channel2.channel_open() + channel2.dtx_demarcation_select() + + #create a xid + tx = self.xid("dummy") + #start work on one channel under that xid: + channel1.dtx_demarcation_start(xid=tx) + #then start on the other without the join set + failed = False + try: + channel2.dtx_demarcation_start(xid=tx) + except Closed, e: + failed = True + error = e + + #cleanup: + if not failed: + channel2.dtx_demarcation_end(xid=tx) + other.close() + channel1.dtx_demarcation_end(xid=tx) + channel1.dtx_coordination_rollback(xid=tx) + + #verification: + if failed: self.assertConnectionException(503, e.args[0]) + else: self.fail("Xid already known, expected exception!") + + def test_forget_xid_on_completion(self): + """ + Verify that a xid is 'forgotten' - and can therefore be used + again - once it is completed. + """ + channel = self.channel + #do some transactional work & complete the transaction + self.test_simple_commit() + + #start association for the same xid as the previously completed txn + tx = self.xid("my-xid") + channel.dtx_demarcation_start(xid=tx) + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + + def test_start_join_and_resume(self): + """ + Ensure the correct error is signalled when both the join and + resume flags are set on starting an association between a + channel and a transcation. + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("dummy") + try: + channel.dtx_demarcation_start(xid=tx, join=True, resume=True) + #failed, but need some cleanup: + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + self.fail("Join and resume both set, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_start_join(self): + """ + Verify 'join' behaviour, where a channel is associated with a + transaction that is already associated with another channel. + """ + #create two channels & select them for use with dtx: + channel1 = self.channel + channel1.dtx_demarcation_select() + + channel2 = self.client.channel(2) + channel2.channel_open() + channel2.dtx_demarcation_select() + + #setup + channel1.queue_declare(queue="one", exclusive=True) + channel1.queue_declare(queue="two", exclusive=True) + channel1.message_transfer(routing_key="one", message_id="a", body="DtxMessage") + channel1.message_transfer(routing_key="two", message_id="b", body="DtxMessage") + + #create a xid + tx = self.xid("dummy") + #start work on one channel under that xid: + channel1.dtx_demarcation_start(xid=tx) + #then start on the other with the join flag set + channel2.dtx_demarcation_start(xid=tx, join=True) + + #do work through each channel + self.swap(channel1, "one", "two")#swap 'a' from 'one' to 'two' + self.swap(channel2, "two", "one")#swap 'b' from 'two' to 'one' + + #mark end on both channels + channel1.dtx_demarcation_end(xid=tx) + channel2.dtx_demarcation_end(xid=tx) + + #commit and check + channel1.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + + def test_suspend_resume(self): + """ + Test suspension and resumption of an association + """ + channel = self.channel + channel.dtx_demarcation_select() + + #setup + channel.queue_declare(queue="one", exclusive=True) + channel.queue_declare(queue="two", exclusive=True) + channel.message_transfer(routing_key="one", message_id="a", body="DtxMessage") + channel.message_transfer(routing_key="two", message_id="b", body="DtxMessage") + + tx = self.xid("dummy") + + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' + channel.dtx_demarcation_end(xid=tx, suspend=True) + + channel.dtx_demarcation_start(xid=tx, resume=True) + self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' + channel.dtx_demarcation_end(xid=tx) + + #commit and check + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + def test_end_suspend_and_fail(self): + """ + Verify that the correct error is signalled if the suspend and + fail flag are both set when disassociating a transaction from + the channel + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("suspend_and_fail") + channel.dtx_demarcation_start(xid=tx) + try: + channel.dtx_demarcation_end(xid=tx, suspend=True, fail=True) + self.fail("Suspend and fail both set, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.dtx_coordination_rollback(xid=tx) + channel.channel_close() + other.close() + + + def test_end_unknown_xid(self): + """ + Verifies that the correct exception is thrown when an attempt + is made to end the association for a xid not previously + associated with the channel + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("unknown-xid") + try: + channel.dtx_demarcation_end(xid=tx) + self.fail("Attempted to end association with unknown xid, expected exception!") + except Closed, e: + #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming... + self.assertConnectionException(503, e.args[0]) + + def test_end(self): + """ + Verify that the association is terminated by end and subsequent + operations are non-transactional + """ + channel = self.client.channel(2) + channel.channel_open() + channel.queue_declare(queue="tx-queue", exclusive=True) + + #publish a message under a transaction + channel.dtx_demarcation_select() + tx = self.xid("dummy") + channel.dtx_demarcation_start(xid=tx) + channel.message_transfer(routing_key="tx-queue", message_id="one", body="DtxMessage") + channel.dtx_demarcation_end(xid=tx) + + #now that association with txn is ended, publish another message + channel.message_transfer(routing_key="tx-queue", message_id="two", body="DtxMessage") + + #check the second message is available, but not the first + self.assertMessageCount(1, "tx-queue") + channel.message_consume(queue="tx-queue", destination="results", no_ack=False) + msg = self.client.queue("results").get(timeout=1) + self.assertEqual("two", msg.message_id) + channel.message_cancel(destination="results") + #ack the message then close the channel + msg.ok() + channel.channel_close() + + channel = self.channel + #commit the transaction and check that the first message (and + #only the first message) is then delivered + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "tx-queue") + self.assertMessageId("one", "tx-queue") + + def test_invalid_commit_one_phase_true(self): + """ + Test that a commit with one_phase = True is rejected if the + transaction in question has already been prepared. + """ + other = self.connect() + tester = other.channel(1) + tester.channel_open() + tester.queue_declare(queue="dummy", exclusive=True) + tester.dtx_demarcation_select() + tx = self.xid("dummy") + tester.dtx_demarcation_start(xid=tx) + tester.message_transfer(routing_key="dummy", body="whatever") + tester.dtx_demarcation_end(xid=tx) + tester.dtx_coordination_prepare(xid=tx) + failed = False + try: + tester.dtx_coordination_commit(xid=tx, one_phase=True) + except Closed, e: + failed = True + error = e + + if failed: + self.channel.dtx_coordination_rollback(xid=tx) + self.assertConnectionException(503, e.args[0]) + else: + tester.channel_close() + other.close() + self.fail("Invalid use of one_phase=True, expected exception!") + + def test_invalid_commit_one_phase_false(self): + """ + Test that a commit with one_phase = False is rejected if the + transaction in question has not yet been prepared. + """ + """ + Test that a commit with one_phase = True is rejected if the + transaction in question has already been prepared. + """ + other = self.connect() + tester = other.channel(1) + tester.channel_open() + tester.queue_declare(queue="dummy", exclusive=True) + tester.dtx_demarcation_select() + tx = self.xid("dummy") + tester.dtx_demarcation_start(xid=tx) + tester.message_transfer(routing_key="dummy", body="whatever") + tester.dtx_demarcation_end(xid=tx) + failed = False + try: + tester.dtx_coordination_commit(xid=tx, one_phase=False) + except Closed, e: + failed = True + error = e + + if failed: + self.channel.dtx_coordination_rollback(xid=tx) + self.assertConnectionException(503, e.args[0]) + else: + tester.channel_close() + other.close() + self.fail("Invalid use of one_phase=False, expected exception!") + + def test_implicit_end(self): + """ + Test that an association is implicitly ended when the channel + is closed (whether by exception or explicit client request) + and the transaction in question is marked as rollback only. + """ + channel1 = self.channel + channel2 = self.client.channel(2) + channel2.channel_open() + + #setup: + channel2.queue_declare(queue="dummy", exclusive=True) + channel2.message_transfer(routing_key="dummy", body="whatever") + tx = self.xid("dummy") + + channel2.dtx_demarcation_select() + channel2.dtx_demarcation_start(xid=tx) + channel2.message_get(queue="dummy", destination="dummy") + self.client.queue("dummy").get(timeout=1).ok() + channel2.message_transfer(routing_key="dummy", body="whatever") + channel2.channel_close() + + self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).flags) + channel1.dtx_coordination_rollback(xid=tx) + + def test_recover(self): + """ + Test basic recover behaviour + """ + channel = self.channel + + channel.dtx_demarcation_select() + channel.queue_declare(queue="dummy", exclusive=True) + + prepared = [] + for i in range(1, 10): + tx = self.xid("tx%s" % (i)) + channel.dtx_demarcation_start(xid=tx) + channel.message_transfer(routing_key="dummy", body="message%s" % (i)) + channel.dtx_demarcation_end(xid=tx) + if i in [2, 5, 6, 8]: + channel.dtx_coordination_prepare(xid=tx) + prepared.append(tx) + else: + channel.dtx_coordination_rollback(xid=tx) + + indoubt = channel.dtx_coordination_recover().xids + #convert indoubt table to a list of xids (note: this will change for 0-10) + data = indoubt["xids"] + xids = [] + pos = 0 + while pos < len(data): + size = unpack("!B", data[pos])[0] + start = pos + 1 + end = start + size + xid = data[start:end] + xids.append(xid) + pos = end + + #rollback the prepared transactions returned by recover + for x in xids: + channel.dtx_coordination_rollback(xid=x) + + #validate against the expected list of prepared transactions + actual = set(xids) + expected = set(prepared) + intersection = actual.intersection(expected) + + if intersection != expected: + missing = expected.difference(actual) + extra = actual.difference(expected) + for x in missing: + channel.dtx_coordination_rollback(xid=x) + self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) + + def xid(self, txid, branchqual = ''): + return pack('LBB', 0, len(txid), len(branchqual)) + txid + branchqual + + def txswap(self, tx, id): + channel = self.channel + #declare two queues: + channel.queue_declare(queue="queue-a", exclusive=True) + channel.queue_declare(queue="queue-b", exclusive=True) + #put message with specified id on one queue: + channel.message_transfer(routing_key="queue-a", message_id=id, body="DtxMessage") + + #start the transaction: + channel.dtx_demarcation_select() + self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_start(xid=tx).flags) + + #'swap' the message from one queue to the other, under that transaction: + self.swap(self.channel, "queue-a", "queue-b") + + #mark the end of the transactional work: + self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_end(xid=tx).flags) + + def swap(self, channel, src, dest): + #consume from src: + channel.message_get(destination="temp-swap", queue=src) + msg = self.client.queue("temp-swap").get(timeout=1) + msg.ok(); + + #re-publish to dest + channel.message_transfer(routing_key=dest, message_id=msg.message_id, body=msg.body) + + def assertMessageCount(self, expected, queue): + self.assertEqual(expected, self.channel.queue_declare(queue=queue, passive=True).message_count) + + def assertMessageId(self, expected, queue): + self.channel.message_consume(queue=queue, destination="results", no_ack=True) + self.assertEqual(expected, self.client.queue("results").get(timeout=1).message_id) + self.channel.message_cancel(destination="results") -- cgit v1.2.1 From 638e704cbfb99eb912619bb5d54793c52f7182ab Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 6 Jun 2007 16:39:03 +0000 Subject: Merged in channel.flow implementation and interoperability tests. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@544879 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/broker.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/broker.py b/qpid/python/tests_0-9/broker.py index a978993891..30eda03d48 100644 --- a/qpid/python/tests_0-9/broker.py +++ b/qpid/python/tests_0-9/broker.py @@ -114,3 +114,20 @@ class BrokerTests(TestBase): self.assertEqual(reply.method.klass.name, "channel") self.assertEqual(reply.method.name, "ok") #todo: provide a way to get notified of incoming pongs... + + def test_channel_flow(self): + channel = self.channel + channel.queue_declare(queue="flow_test_queue", exclusive=True) + channel.message_consume(destination="my-tag", queue="flow_test_queue") + incoming = self.client.queue("my-tag") + + channel.channel_flow(active=False) + channel.message_transfer(routing_key="flow_test_queue", body="abcdefghijklmnopqrstuvwxyz") + try: + incoming.get(timeout=1) + self.fail("Received message when flow turned off.") + except Empty: None + + channel.channel_flow(active=True) + msg = incoming.get(timeout=1) + self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.body) -- cgit v1.2.1 From 2373874e23fb62e502963878f5e7cd68e100021e Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 8 Jun 2007 15:24:12 +0000 Subject: Timeout handling for dtx, plus tests. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@545531 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/dtx.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/dtx.py b/qpid/python/tests_0-9/dtx.py index ec82c72d49..bc268f4129 100644 --- a/qpid/python/tests_0-9/dtx.py +++ b/qpid/python/tests_0-9/dtx.py @@ -21,6 +21,7 @@ from qpid.queue import Empty from qpid.content import Content from qpid.testlib import testrunner, TestBase from struct import pack, unpack +from time import sleep class DtxTests(TestBase): """ @@ -37,6 +38,7 @@ class DtxTests(TestBase): """ XA_RBROLLBACK = 1 + XA_RBTIMEOUT = 2 XA_OK = 8 def test_simple_commit(self): @@ -451,6 +453,51 @@ class DtxTests(TestBase): self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).flags) channel1.dtx_coordination_rollback(xid=tx) + def test_get_timeout(self): + """ + Check that get-timeout returns the correct value, (and that a + transaction with a timeout can complete normally) + """ + channel = self.channel + tx = self.xid("dummy") + + channel.dtx_demarcation_select() + channel.dtx_demarcation_start(xid=tx) + self.assertEqual(0, channel.dtx_coordination_get_timeout(xid=tx).timeout) + channel.dtx_coordination_set_timeout(xid=tx, timeout=60) + self.assertEqual(60, channel.dtx_coordination_get_timeout(xid=tx).timeout) + self.assertEqual(self.XA_OK, channel.dtx_demarcation_end(xid=tx).flags) + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).flags) + + def test_set_timeout(self): + """ + Test the timeout of a transaction results in the expected + behaviour + """ + #open new channel to allow self.channel to be used in checking te queue + channel = self.client.channel(2) + channel.channel_open() + #setup: + tx = self.xid("dummy") + channel.queue_declare(queue="queue-a", exclusive=True) + channel.queue_declare(queue="queue-b", exclusive=True) + channel.message_transfer(routing_key="queue-a", message_id="timeout", body="DtxMessage") + + channel.dtx_demarcation_select() + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "queue-a", "queue-b") + channel.dtx_coordination_set_timeout(xid=tx, timeout=2) + sleep(3) + #check that the work has been rolled back already + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("timeout", "queue-a") + #check the correct codes are returned when we try to complete the txn + self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_demarcation_end(xid=tx).flags) + self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_coordination_rollback(xid=tx).flags) + + + def test_recover(self): """ Test basic recover behaviour -- cgit v1.2.1 From c88e805ef2e27ffd0286903570c25b07547a2416 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 27 Jun 2007 12:39:49 +0000 Subject: Added preview of exchange- and binding- query methods that have been approved for 0-10. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@551144 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/query.py | 155 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 qpid/python/tests_0-9/query.py (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/query.py b/qpid/python/tests_0-9/query.py new file mode 100644 index 0000000000..69111f03fa --- /dev/null +++ b/qpid/python/tests_0-9/query.py @@ -0,0 +1,155 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueryTests(TestBase): + """Tests for various query methods introduced in 0-10 and available in 0-9 for preview""" + + def test_exchange_query(self): + """ + Test that the exchange_query method works as expected + """ + channel = self.channel + #check returned type for the standard exchanges + self.assertEqual("direct", channel.exchange_query(name="amq.direct").type) + self.assertEqual("topic", channel.exchange_query(name="amq.topic").type) + self.assertEqual("fanout", channel.exchange_query(name="amq.fanout").type) + self.assertEqual("headers", channel.exchange_query(name="amq.match").type) + self.assertEqual("direct", channel.exchange_query(name="").type) + #declare an exchange + channel.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) + #check that the result of a query is as expected + response = channel.exchange_query(name="my-test-exchange") + self.assertEqual("direct", response.type) + self.assertEqual(False, response.durable) + self.assertEqual(False, response.not_found) + #delete the exchange + channel.exchange_delete(exchange="my-test-exchange") + #check that the query now reports not-found + self.assertEqual(True, channel.exchange_query(name="my-test-exchange").not_found) + + def test_binding_query_direct(self): + """ + Test that the binding_query method works as expected with the direct exchange + """ + self.binding_query_with_key("amq.direct") + + def test_binding_query_topic(self): + """ + Test that the binding_query method works as expected with the direct exchange + """ + self.binding_query_with_key("amq.topic") + + def binding_query_with_key(self, exchange_name): + channel = self.channel + #setup: create two queues + channel.queue_declare(queue="used-queue", exclusive=True) + channel.queue_declare(queue="unused-queue", exclusive=True) + + channel.queue_bind(exchange=exchange_name, queue="used-queue", routing_key="used-key") + + # test detection of any binding to specific queue + response = channel.binding_query(exchange=exchange_name, queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test detection of specific binding to any queue + response = channel.binding_query(exchange=exchange_name, routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.key_not_matched) + + # test detection of specific binding to specific queue + response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(False, response.key_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + # test unspecified queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.key_not_matched) + + # test matched queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(True, response.key_not_matched) + + # test unmatched queue, matched binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(False, response.key_not_matched) + + # test unmatched queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(True, response.key_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange=exchange_name, queue="unknown-queue").queue_not_found) + + + def test_binding_query_fanout(self): + """ + Test that the binding_query method works as expected with fanout exchange + """ + channel = self.channel + #setup + channel.queue_declare(queue="used-queue", exclusive=True) + channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_bind(exchange="amq.fanout", queue="used-queue") + + response = channel.binding_query(exchange="amq.fanout", queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + + def test_binding_query_header(self): + """ + Test that the binding_query method works as expected with headers exchanges + """ + channel = self.channel + #setup + channel.queue_declare(queue="used-queue", exclusive=True) + channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) + + response = channel.binding_query(exchange="amq.match", queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + + -- cgit v1.2.1 From a5f95fd26f628f6ae45bd276505aaeb5177a1949 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 27 Jun 2007 15:29:17 +0000 Subject: Fixes and tests: * ExchangeRegistry::get() caused a pair to be inserted with a 'null' pointer if the xchange didn't exist * HeadersExchange::isBound() didn't check queue param git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@551197 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/query.py | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/query.py b/qpid/python/tests_0-9/query.py index 69111f03fa..c2e08c003c 100644 --- a/qpid/python/tests_0-9/query.py +++ b/qpid/python/tests_0-9/query.py @@ -136,8 +136,23 @@ class QueryTests(TestBase): channel.queue_declare(queue="unused-queue", exclusive=True) channel.queue_bind(exchange="amq.fanout", queue="used-queue") + # test detection of any binding to specific queue response = channel.binding_query(exchange="amq.fanout", queue="used-queue") self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange="amq.fanout", queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange="amq.fanout", queue="unknown-queue").queue_not_found) def test_binding_query_header(self): """ @@ -149,7 +164,61 @@ class QueryTests(TestBase): channel.queue_declare(queue="unused-queue", exclusive=True) channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) + # test detection of any binding to specific queue response = channel.binding_query(exchange="amq.match", queue="used-queue") self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + # test detection of specific binding to any queue + response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.args_not_matched) + + # test detection of specific binding to specific queue + response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(False, response.args_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + # test unspecified queue, unmatched binding + response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.args_not_matched) + + # test matched queue, unmatched binding + response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(True, response.args_not_matched) + + # test unmatched queue, matched binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(False, response.args_not_matched) + + # test unmatched queue, unmatched binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(True, response.args_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange="amq.match", queue="unknown-queue").queue_not_found) -- cgit v1.2.1 From a7a3409349acfaa659e32ddc7e3cf015d03ae749 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 3 Jul 2007 15:18:03 +0000 Subject: Changes to python tests for QPID-533 and QPID-407. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@552872 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/basic.py | 5 +++-- qpid/python/tests_0-9/message.py | 10 ++++++++-- qpid/python/tests_0-9/queue.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py index 140576540a..0ad06e10a3 100644 --- a/qpid/python/tests_0-9/basic.py +++ b/qpid/python/tests_0-9/basic.py @@ -218,10 +218,11 @@ class BasicTests(TestBase): channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four channel.basic_cancel(consumer_tag=subscription.consumer_tag) - subscription2 = channel.basic_consume(queue="test-requeue") - queue2 = self.client.queue(subscription2.consumer_tag) channel.basic_recover(requeue=True) + + subscription2 = channel.basic_consume(queue="test-requeue") + queue2 = self.client.queue(subscription2.consumer_tag) msg3b = queue2.get(timeout=1) msg5b = queue2.get(timeout=1) diff --git a/qpid/python/tests_0-9/message.py b/qpid/python/tests_0-9/message.py index 8da9978792..b25016e680 100644 --- a/qpid/python/tests_0-9/message.py +++ b/qpid/python/tests_0-9/message.py @@ -219,10 +219,14 @@ class MessageTests(TestBase): msg4.ok() #Four channel.message_cancel(destination="consumer_tag") - channel.message_consume(queue="test-requeue", destination="consumer_tag") - queue2 = self.client.queue("consumer_tag") + #publish a new message + channel.message_transfer(routing_key="test-requeue", body="Six") + #requeue unacked messages (Three and Five) channel.message_recover(requeue=True) + + channel.message_consume(queue="test-requeue", destination="consumer_tag") + queue2 = self.client.queue("consumer_tag") msg3b = queue2.get(timeout=1) msg5b = queue2.get(timeout=1) @@ -233,6 +237,8 @@ class MessageTests(TestBase): self.assertEqual(True, msg3b.redelivered) self.assertEqual(True, msg5b.redelivered) + self.assertEqual("Six", queue2.get(timeout=1).body) + try: extra = queue2.get(timeout=1) self.fail("Got unexpected message in second queue: " + extra.body) diff --git a/qpid/python/tests_0-9/queue.py b/qpid/python/tests_0-9/queue.py index b94b8b7739..e7fe0b3ed4 100644 --- a/qpid/python/tests_0-9/queue.py +++ b/qpid/python/tests_0-9/queue.py @@ -304,3 +304,37 @@ class QueueTests(TestBase): self.assertChannelException(404, e.args[0]) + def test_autodelete_shared(self): + """ + Test auto-deletion (of non-exclusive queues) + """ + channel = self.channel + other = self.connect() + channel2 = other.channel(1) + channel2.channel_open() + + channel.queue_declare(queue="auto-delete-me", auto_delete=True) + + #consume from both channels + reply = channel.basic_consume(queue="auto-delete-me", no_ack=True) + channel2.basic_consume(queue="auto-delete-me", no_ack=True) + + #implicit cancel + channel2.channel_close() + + #check it is still there + channel.queue_declare(queue="auto-delete-me", passive=True) + + #explicit cancel => queue is now unused again: + channel.basic_cancel(consumer_tag=reply.consumer_tag) + + #NOTE: this assumes there is no timeout in use + + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="auto-delete-me", passive=True) + self.fail("Expected queue to have been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + -- cgit v1.2.1 From 045c1497132a51a1f0d1d325fc233a97b6281748 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Tue, 17 Jul 2007 20:28:47 +0000 Subject: cancel test assumes threading model -- added sleep, once we move to 0-10 should wait for publish ack git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@557047 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py index 0ad06e10a3..ad4cb29c71 100644 --- a/qpid/python/tests_0-9/basic.py +++ b/qpid/python/tests_0-9/basic.py @@ -125,12 +125,12 @@ class BasicTests(TestBase): channel.queue_declare(queue="test-queue-4", exclusive=True) channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") channel.basic_publish(routing_key="test-queue-4", content=Content("One")) - + import time; time.sleep(5) # this should be replace waiting for confirm of publish once we have message class #cancel should stop messages being delivered channel.basic_cancel(consumer_tag="my-consumer") channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) myqueue = self.client.queue("my-consumer") - msg = myqueue.get(timeout=1) + msg = myqueue.get(timeout=5) self.assertEqual("One", msg.content.body) try: msg = myqueue.get(timeout=1) -- cgit v1.2.1 From 35d50318300dcad92e5f7c7210fc74f880c3e655 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Tue, 17 Jul 2007 21:12:08 +0000 Subject: fixed raise issue in test git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@557057 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/basic.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py index ad4cb29c71..75633908cd 100644 --- a/qpid/python/tests_0-9/basic.py +++ b/qpid/python/tests_0-9/basic.py @@ -125,13 +125,14 @@ class BasicTests(TestBase): channel.queue_declare(queue="test-queue-4", exclusive=True) channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") channel.basic_publish(routing_key="test-queue-4", content=Content("One")) - import time; time.sleep(5) # this should be replace waiting for confirm of publish once we have message class - #cancel should stop messages being delivered - channel.basic_cancel(consumer_tag="my-consumer") - channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) + myqueue = self.client.queue("my-consumer") msg = myqueue.get(timeout=5) self.assertEqual("One", msg.content.body) + + #cancel should stop messages being delivered + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) try: msg = myqueue.get(timeout=1) self.fail("Got message after cancellation: " + msg) -- cgit v1.2.1 From 13310282457c729ff5a7f7d11855a1db2f274d49 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Tue, 17 Jul 2007 21:19:01 +0000 Subject: fix 0-8 cancel test also git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@557058 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-8/basic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-8/basic.py b/qpid/python/tests_0-8/basic.py index 140576540a..95ca0d7287 100644 --- a/qpid/python/tests_0-8/basic.py +++ b/qpid/python/tests_0-8/basic.py @@ -126,12 +126,13 @@ class BasicTests(TestBase): channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") channel.basic_publish(routing_key="test-queue-4", content=Content("One")) - #cancel should stop messages being delivered - channel.basic_cancel(consumer_tag="my-consumer") - channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) myqueue = self.client.queue("my-consumer") msg = myqueue.get(timeout=1) self.assertEqual("One", msg.content.body) + + #cancel should stop messages being delivered + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) try: msg = myqueue.get(timeout=1) self.fail("Got message after cancellation: " + msg) -- cgit v1.2.1 From 346d178a06b5dc1c4065450e988f9934390c561c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 23 Jul 2007 12:29:17 +0000 Subject: Added initial 'execution-layer' to try out methods form the 0-10 execution class. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@558700 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/client.py | 3 +++ qpid/python/qpid/peer.py | 30 ++++++++++++++++++++++++++++++ qpid/python/tests_0-9/basic.py | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index cdceb87bdf..f1800204db 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -140,6 +140,9 @@ class ClientDelegate(Delegate): def connection_close(self, ch, msg): self.client.peer.close(msg) + def execution_complete(self, ch, msg): + ch.completion.complete(msg.cumulative_execution_mark) + def close(self, reason): self.client.closed = True self.client.reason = reason diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 72e6a19bc7..9880eea19b 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -186,6 +186,8 @@ class Channel: self.requester = Requester(self.write) self.responder = Responder(self.write) + self.completion = ExecutionCompletion() + # Use reliable framing if version == 0-9. self.reliable = (spec.major == 0 and spec.minor == 9) self.synchronous = True @@ -247,6 +249,7 @@ class Channel: self.responder.respond(method, batch, request) def invoke(self, type, args, kwargs): + self.completion.next_command(type) content = kwargs.pop("content", None) frame = Method(type, type.arguments(*args, **kwargs)) if self.reliable: @@ -337,3 +340,30 @@ class Future: def is_complete(self): return self.completed.isSet() + +class ExecutionCompletion: + def __init__(self): + self.completed = threading.Event() + self.sequence = Sequence(0) + self.command_id = 0 + self.mark = 0 + + def next_command(self, method): + #the following test is a hack until the track/sub-channel is available + if method.klass.name != "execution": + self.command_id = self.sequence.next() + + def complete(self, mark): + self.mark = mark + self.completed.set() + self.completed.clear() + + def wait(self, point_of_interest=-1, timeout=None): + """ + todo: really want to allow different threads to call this with + different points of interest on the same channel, this is a quick + hack for now + """ + if point_of_interest == -1: point_of_interest = self.command_id + self.completed.wait(timeout) + return point_of_interest <= self.mark diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py index 75633908cd..e7d22e00da 100644 --- a/qpid/python/tests_0-9/basic.py +++ b/qpid/python/tests_0-9/basic.py @@ -127,7 +127,7 @@ class BasicTests(TestBase): channel.basic_publish(routing_key="test-queue-4", content=Content("One")) myqueue = self.client.queue("my-consumer") - msg = myqueue.get(timeout=5) + msg = myqueue.get(timeout=1) self.assertEqual("One", msg.content.body) #cancel should stop messages being delivered -- cgit v1.2.1 From 65e569509e375511e185d892537cb6831e82da6f Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 23 Jul 2007 12:46:48 +0000 Subject: Added extra test for new execution layer methods. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@558705 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/execution.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 qpid/python/tests_0-9/execution.py (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/execution.py b/qpid/python/tests_0-9/execution.py new file mode 100644 index 0000000000..f2facfe42b --- /dev/null +++ b/qpid/python/tests_0-9/execution.py @@ -0,0 +1,29 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExecutionTests (TestBase): + def test_flush(self): + channel = self.channel + for i in [1, 2, 3]: + channel.basic_publish() + channel.execution_flush() + assert(channel.completion.wait(channel.completion.command_id, timeout=1)) -- cgit v1.2.1 From 8091165787a3e106fcb1edb2f3effcca2f29a9bc Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 23 Jul 2007 14:34:45 +0000 Subject: Applied a modified version of the range checking patch from QPID-498. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@558741 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 158 +++++++++++++++++++++++++++++++++++++++----- qpid/python/qpid/testlib.py | 2 +- 2 files changed, 142 insertions(+), 18 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index b0b596b89c..b615a0f6ed 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -22,8 +22,11 @@ """ Utility code to translate between python objects and AMQP encoded data fields. + +The unit test for this module is located in tests/codec.py """ +import re from cStringIO import StringIO from struct import * from reference import ReferenceId @@ -33,7 +36,14 @@ class EOF(Exception): class Codec: + """ + class that handles encoding/decoding of AMQP primitives + """ + def __init__(self, stream): + """ + initializing the stream/fields used + """ self.stream = stream self.nwrote = 0 self.nread = 0 @@ -41,6 +51,9 @@ class Codec: self.outgoing_bits = [] def read(self, n): + """ + reads in 'n' bytes from the stream. Can raise EFO exception + """ data = self.stream.read(n) if n > 0 and len(data) == 0: raise EOF() @@ -48,15 +61,24 @@ class Codec: return data def write(self, s): + """ + writes data 's' to the stream + """ self.flushbits() self.stream.write(s) self.nwrote += len(s) def flush(self): + """ + flushes the bits and data present in the stream + """ self.flushbits() self.stream.flush() def flushbits(self): + """ + flushes the bits(compressed into octets) onto the stream + """ if len(self.outgoing_bits) > 0: bytes = [] index = 0 @@ -69,9 +91,15 @@ class Codec: self.encode_octet(byte) def pack(self, fmt, *args): + """ + packs the data 'args' as per the format 'fmt' and writes it to the stream + """ self.write(pack(fmt, *args)) def unpack(self, fmt): + """ + reads data from the stream and unpacks it as per the format 'fmt' + """ size = calcsize(fmt) data = self.read(size) values = unpack(fmt, data) @@ -81,85 +109,166 @@ class Codec: return values def encode(self, type, value): + """ + calls the appropriate encode function e.g. encode_octet, encode_short etc. + """ getattr(self, "encode_" + type)(value) def decode(self, type): + """ + calls the appropriate decode function e.g. decode_octet, decode_short etc. + """ return getattr(self, "decode_" + type)() - # bit def encode_bit(self, o): + """ + encodes a bit + """ if o: self.outgoing_bits.append(True) else: self.outgoing_bits.append(False) def decode_bit(self): + """ + decodes a bit + """ if len(self.incoming_bits) == 0: bits = self.decode_octet() for i in range(8): self.incoming_bits.append(bits >> i & 1 != 0) return self.incoming_bits.pop(0) - # octet def encode_octet(self, o): + """ + encodes octet (8 bits) data 'o' in network byte order + """ + + # octet's valid range is [0,255] + if (o < 0 or o > 255): + raise ValueError('Valid range of octet is [0,255]') + self.pack("!B", o) def decode_octet(self): + """ + decodes a octet (8 bits) encoded in network byte order + """ return self.unpack("!B") - # short def encode_short(self, o): + """ + encodes short (16 bits) data 'o' in network byte order + """ + + # short int's valid range is [0,65535] + if (o < 0 or o > 65535): + raise ValueError('Valid range of short int is [0,65535]') + self.pack("!H", o) def decode_short(self): + """ + decodes a short (16 bits) in network byte order + """ return self.unpack("!H") - # long def encode_long(self, o): + """ + encodes long (32 bits) data 'o' in network byte order + """ + + if (o < 0): + raise ValueError('unsinged long int cannot be less than 0') + self.pack("!L", o) def decode_long(self): + """ + decodes a long (32 bits) in network byte order + """ return self.unpack("!L") - # longlong def encode_longlong(self, o): + """ + encodes long long (64 bits) data 'o' in network byte order + """ self.pack("!Q", o) def decode_longlong(self): + """ + decodes a long long (64 bits) in network byte order + """ return self.unpack("!Q") def enc_str(self, fmt, s): + """ + encodes a string 's' in network byte order as per format 'fmt' + """ size = len(s) self.pack(fmt, size) self.write(s) def dec_str(self, fmt): + """ + decodes a string in network byte order as per format 'fmt' + """ size = self.unpack(fmt) return self.read(size) - # shortstr def encode_shortstr(self, s): + """ + encodes a short string 's' in network byte order + """ + + # short strings are limited to 255 octets + if len(s) > 255: + raise ValueError('Short strings are limited to 255 octets') + self.enc_str("!B", s) def decode_shortstr(self): + """ + decodes a short string in network byte order + """ return self.dec_str("!B") - # longstr def encode_longstr(self, s): + """ + encodes a long string 's' in network byte order + """ if isinstance(s, dict): self.encode_table(s) else: self.enc_str("!L", s) def decode_longstr(self): + """ + decodes a long string 's' in network byte order + """ return self.dec_str("!L") - # table + KEY_CHECK = re.compile(r"[\$#A-Za-z][\$#A-Za-z0-9_]*") + def encode_table(self, tbl): + """ + encodes a table data structure in network byte order + """ enc = StringIO() codec = Codec(enc) if tbl: for key, value in tbl.items(): + # Field names MUST start with a letter, '$' or '#' and may + # continue with letters, '$' or '#', digits, or underlines, to + # a maximum length of 128 characters. + + if len(key) > 128: + raise ValueError("field table key too long: '%s'" % key) + + m = Codec.KEY_CHECK.match(key) + if m == None or m.end() != len(key): + raise ValueError("invalid field table key: '%s'" % key) + codec.encode_shortstr(key) if isinstance(value, basestring): codec.write("S") @@ -172,6 +281,9 @@ class Codec: self.write(s) def decode_table(self): + """ + decodes a table data structure in network byte order + """ size = self.decode_long() start = self.nread result = {} @@ -188,27 +300,39 @@ class Codec: return result def encode_timestamp(self, t): - # XXX + """ + encodes a timestamp data structure in network byte order + """ self.encode_longlong(t) def decode_timestamp(self): - # XXX + """ + decodes a timestamp data structure in network byte order + """ return self.decode_longlong() def encode_content(self, s): - # content can be passed as a string in which case it is assumed to - # be inline data, or as an instance of ReferenceId indicating it is - # a reference id + """ + encodes a content data structure in network byte order + + content can be passed as a string in which case it is assumed to + be inline data, or as an instance of ReferenceId indicating it is + a reference id + """ if isinstance(s, ReferenceId): self.encode_octet(1) self.encode_longstr(s.id) - else: + else: self.encode_octet(0) self.encode_longstr(s) - def decode_content(self): - # return a string for inline data and a ReferenceId instance for - # references + def decode_content(self): + """ + decodes a content data structure in network byte order + + return a string for inline data and a ReferenceId instance for + references + """ type = self.decode_octet() if type == 0: return self.decode_longstr() diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index fa904ff029..4467a1f4cd 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -122,7 +122,7 @@ Options: print "Using specification from:", self.specfile self.spec = qpid.spec.load(self.specfile, *self.errata) if len(self.tests) == 0: -# self.tests=findmodules("tests") + self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") else: -- cgit v1.2.1 From 9174ccc18fa66e521752e74991d4d9e4ccdf8ab3 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 23 Jul 2007 14:36:05 +0000 Subject: Added a better XML library. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@558742 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/__init__.py | 60 ++++++++++ qpid/python/mllib/dom.py | 250 ++++++++++++++++++++++++++++++++++++++++ qpid/python/mllib/parsers.py | 139 ++++++++++++++++++++++ qpid/python/mllib/transforms.py | 164 ++++++++++++++++++++++++++ qpid/python/qpid/spec.py | 39 +++---- qpid/python/qpid/xmlutil.py | 119 ------------------- 6 files changed, 632 insertions(+), 139 deletions(-) create mode 100644 qpid/python/mllib/__init__.py create mode 100644 qpid/python/mllib/dom.py create mode 100644 qpid/python/mllib/parsers.py create mode 100644 qpid/python/mllib/transforms.py delete mode 100644 qpid/python/qpid/xmlutil.py (limited to 'qpid/python') diff --git a/qpid/python/mllib/__init__.py b/qpid/python/mllib/__init__.py new file mode 100644 index 0000000000..44b78126fb --- /dev/null +++ b/qpid/python/mllib/__init__.py @@ -0,0 +1,60 @@ +# +# 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. +# + +""" +This module provides document parsing and transformation utilities for +both SGML and XML. +""" + +import dom, transforms, parsers +import xml.sax, types +from cStringIO import StringIO + +def transform(node, *args): + result = node + for t in args: + if isinstance(t, types.ClassType): + t = t() + result = result.dispatch(t) + return result + +def sgml_parse(source): + if isinstance(source, basestring): + source = StringIO(source) + fname = "" + elif hasattr(source, "name"): + fname = source.name + p = parsers.SGMLParser() + num = 1 + for line in source: + p.feed(line) + p.parser.line(fname, num, None) + num += 1 + p.close() + return p.parser.tree + +def xml_parse(source): + p = parsers.XMLParser() + xml.sax.parse(source, p) + return p.parser.tree + +def sexp(node): + s = transforms.Sexp() + node.dispatch(s) + return s.out diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py new file mode 100644 index 0000000000..9b1740055b --- /dev/null +++ b/qpid/python/mllib/dom.py @@ -0,0 +1,250 @@ +# +# 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. +# + +""" +Simple DOM for both SGML and XML documents. +""" + +from transforms import Text + +class Container: + + def __init__(self): + self.children = [] + + def add(self, child): + child.parent = self + self.children.append(child) + + def extend(self, children): + for child in children: + child.parent = self + self.children.append(child) + +class Component: + + def __init__(self): + self.parent = None + + def index(self): + if self.parent: + return self.parent.children.index(self) + else: + return 0 + + def _line(self, file, line, column): + self.file = file + self.line = line + self.column = column + +class DispatchError(Exception): + + def __init__(self, scope, f): + msg = "no such attribtue" + +class Dispatcher: + + def is_type(self, type): + cls = self + while cls != None: + if cls.type == type: + return True + cls = cls.base + return False + + def dispatch(self, f): + cls = self + while cls != None: + if hasattr(f, cls.type): + return getattr(f, cls.type)(self) + else: + cls = cls.base + + cls = self + attrs = "" + while cls != None: + if attrs: + sep = ", " + if cls.base == None: + sep += "or " + else: + sep = "" + attrs += "%s'%s'" % (sep, cls.type) + cls = cls.base + + raise AttributeError("'%s' object has no attribute %s" % + (f.__class__.__name__, attrs)) + +class Node(Container, Component, Dispatcher): + + type = "node" + base = None + + def __init__(self): + Container.__init__(self) + Component.__init__(self) + self.query = Query([self]) + + def __getitem__(self, name): + for nd in self.query[name]: + return nd + + def text(self): + return self.dispatch(Text()) + + def tag(self, name, *attrs, **kwargs): + t = Tag(name, *attrs, **kwargs) + self.add(t) + return t + + def data(self, s): + d = Data(s) + self.add(d) + return d + + def entity(self, s): + e = Entity(s) + self.add(e) + return e + +class Tree(Node): + + type = "tree" + base = Node + +class Tag(Node): + + type = "tag" + base = Node + + def __init__(self, _name, *attrs, **kwargs): + Node.__init__(self) + self.name = _name + self.attrs = list(attrs) + self.attrs.extend(kwargs.items()) + self.singleton = False + + def get_attr(self, name): + for k, v in self.attrs: + if name == k: + return v + + def __getitem__(self, name): + if name and name[0] == "@": + return self.get_attr(name[1:]) + else: + for nd in self.query[name]: + return nd + return self.get_attr(name) + + def dispatch(self, f): + try: + method = getattr(f, "do_" + self.name) + except AttributeError: + return Dispatcher.dispatch(self, f) + return method(self) + +class Leaf(Component, Dispatcher): + + type = "leaf" + base = None + + def __init__(self, data): + assert isinstance(data, basestring) + self.data = data + +class Data(Leaf): + type = "data" + base = Leaf + +class Entity(Leaf): + type = "entity" + base = Leaf + +class Character(Leaf): + type = "character" + base = Leaf + +class Comment(Leaf): + type = "comment" + base = Leaf + +################### +## Query Classes ## +########################################################################### + +class View: + + def __init__(self, source): + self.source = source + +class Filter(View): + + def __init__(self, predicate, source): + View.__init__(self, source) + if callable(predicate): + self.predicate = predicate + elif predicate[0] == "#": + type = predicate[1:] + self.predicate = lambda x: x.is_type(type) + else: + self.predicate = lambda x: isinstance(x, Tag) and x.name == predicate + + def __iter__(self): + for nd in self.source: + if self.predicate(nd): yield nd + +class Flatten(View): + + def __iter__(self): + sources = [iter(self.source)] + while sources: + try: + nd = sources[-1].next() + if isinstance(nd, Tree): + sources.append(iter(nd.children)) + else: + yield nd + except StopIteration: + sources.pop() + +class Children(View): + + def __iter__(self): + for nd in self.source: + for child in nd.children: + yield child + +class Query(View): + + def __iter__(self): + for nd in self.source: + yield nd + + def __getitem__(self, predicate): + if isinstance(predicate, basestring): + path = predicate.split("/") + else: + path = [predicate] + + query = self.source + for p in path: + query = Query(Filter(p, Flatten(Children(query)))) + + return query diff --git a/qpid/python/mllib/parsers.py b/qpid/python/mllib/parsers.py new file mode 100644 index 0000000000..3e7cc10dc2 --- /dev/null +++ b/qpid/python/mllib/parsers.py @@ -0,0 +1,139 @@ +# +# 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. +# + +""" +Parsers for SGML and XML to dom. +""" + +import sgmllib, xml.sax.handler +from dom import * + +class Parser: + + def __init__(self): + self.tree = Tree() + self.node = self.tree + self.nodes = [] + + def line(self, id, lineno, colno): + while self.nodes: + n = self.nodes.pop() + n._line(id, lineno, colno) + + def add(self, node): + self.node.add(node) + self.nodes.append(node) + + def start(self, name, attrs): + tag = Tag(name, *attrs) + self.add(tag) + self.node = tag + + def end(self, name): + self.balance(name) + self.node = self.node.parent + + def data(self, data): + children = self.node.children + if children and isinstance(children[-1], Data): + children[-1].data += data + else: + self.add(Data(data)) + + def comment(self, comment): + self.add(Comment(comment)) + + def entity(self, ref): + self.add(Entity(ref)) + + def character(self, ref): + self.add(Character(ref)) + + def balance(self, name = None): + while self.node != self.tree and name != self.node.name: + self.node.parent.extend(self.node.children) + del self.node.children[:] + self.node.singleton = True + self.node = self.node.parent + + +class SGMLParser(sgmllib.SGMLParser): + + def __init__(self, entitydefs = None): + sgmllib.SGMLParser.__init__(self) + if entitydefs == None: + self.entitydefs = {} + else: + self.entitydefs = entitydefs + self.parser = Parser() + + def unknown_starttag(self, name, attrs): + self.parser.start(name, attrs) + + def handle_data(self, data): + self.parser.data(data) + + def handle_comment(self, comment): + self.parser.comment(comment) + + def unknown_entityref(self, ref): + self.parser.entity(ref) + + def unknown_charref(self, ref): + self.parser.character(ref) + + def unknown_endtag(self, name): + self.parser.end(name) + + def close(self): + sgmllib.SGMLParser.close(self) + self.parser.balance() + assert self.parser.node == self.parser.tree + +class XMLParser(xml.sax.handler.ContentHandler): + + def __init__(self): + self.parser = Parser() + self.locator = None + + def line(self): + if self.locator != None: + self.parser.line(self.locator.getSystemId(), + self.locator.getLineNumber(), + self.locator.getColumnNumber()) + + def setDocumentLocator(self, locator): + self.locator = locator + + def startElement(self, name, attrs): + self.parser.start(name, attrs.items()) + self.line() + + def endElement(self, name): + self.parser.end(name) + self.line() + + def characters(self, content): + self.parser.data(content) + self.line() + + def skippedEntity(self, name): + self.parser.entity(name) + self.line() + diff --git a/qpid/python/mllib/transforms.py b/qpid/python/mllib/transforms.py new file mode 100644 index 0000000000..bb79dcf192 --- /dev/null +++ b/qpid/python/mllib/transforms.py @@ -0,0 +1,164 @@ +# +# 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. +# + +""" +Useful transforms for dom objects. +""" + +from cStringIO import StringIO +from dom import * + +class Visitor: + + def descend(self, node): + for child in node.children: + child.dispatch(self) + + def node(self, node): + self.descend(node) + + def leaf(self, leaf): + pass + +class Identity: + + def descend(self, node): + result = [] + for child in node.children: + result.append(child.dispatch(self)) + return result + + def default(self, tag): + result = Tag(tag.name, *tag.attrs) + result.extend(self.descend(tag)) + return result + + def tree(self, tree): + result = Tree() + result.extend(self.descend(tree)) + return result + + def tag(self, tag): + return self.default(tag) + + def leaf(self, leaf): + return leaf.__class__(leaf.data) + +class Sexp(Identity): + + def __init__(self): + self.stack = [] + self.level = 0 + self.out = "" + + def open(self, s): + self.out += "(%s" % s + self.level += len(s) + 1 + self.stack.append(s) + + def line(self, s = ""): + self.out = self.out.rstrip() + self.out += "\n" + " "*self.level + s + + def close(self): + s = self.stack.pop() + self.level -= len(s) + 1 + self.out = self.out.rstrip() + self.out += ")" + + def tree(self, tree): + self.open("+ ") + for child in tree.children: + self.line(); child.dispatch(self) + self.close() + + def tag(self, tag): + self.open("Node(%s) " % tag.name) + for child in tag.children: + self.line(); child.dispatch(self) + self.close() + + def leaf(self, leaf): + self.line("%s(%s)" % (leaf.__class__.__name__, leaf.data)) + +class Output: + + def descend(self, node): + out = StringIO() + for child in node.children: + out.write(child.dispatch(self)) + return out.getvalue() + + def default(self, tag): + out = StringIO() + out.write("<%s" % tag.name) + for k, v in tag.attrs: + out.write(' %s="%s"' % (k, v)) + out.write(">") + out.write(self.descend(tag)) + if not tag.singleton: + out.write("" % tag.name) + return out.getvalue() + + def tree(self, tree): + return self.descend(tree) + + def tag(self, tag): + return self.default(tag) + + def data(self, leaf): + return leaf.data + + def entity(self, leaf): + return "&%s;" % leaf.data + + def character(self, leaf): + raise Exception("TODO") + + def comment(self, leaf): + return "" % leaf.data + +class Empty(Output): + + def tag(self, tag): + return self.descend(tag) + + def data(self, leaf): + return "" + + def entity(self, leaf): + return "" + + def character(self, leaf): + return "" + + def comment(self, leaf): + return "" + +class Text(Empty): + + def data(self, leaf): + return leaf.data + + def entity(self, leaf): + return "&%s;" % leaf.data + + def character(self, leaf): + # XXX: is this right? + return "&#%s;" % leaf.data diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index f8e37737e2..88faedd825 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -29,7 +29,7 @@ class so that the generated code can be reused in a variety of situations. """ -import re, textwrap, new, xmlutil +import re, textwrap, new, mllib class SpecContainer: @@ -275,21 +275,20 @@ class Field(Metadata): self.docs = docs def get_desc(nd): - label = nd.get("@label") + label = nd["@label"] if not label: - label = nd.text + label = nd.text() if label: label = label.strip() return label def get_docs(nd): - return [n.text for n in nd["doc"]] + return [n.text() for n in nd.query["doc"]] def load_fields(nd, l, domains): - for f_nd in nd["field"]: - try: - type = f_nd["@domain"] - except KeyError: + for f_nd in nd.query["field"]: + type = f_nd["@domain"] + if type == None: type = f_nd["@type"] type = pythonize(type) domain = None @@ -300,27 +299,27 @@ def load_fields(nd, l, domains): get_desc(f_nd), get_docs(f_nd))) def load(specfile, *errata): - doc = xmlutil.parse(specfile) - spec_root = doc["amqp"][0] + doc = mllib.xml_parse(specfile) + spec_root = doc["amqp"] spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile) - for root in [spec_root] + map(lambda x: xmlutil.parse(x)["amqp"][0], errata): + for root in [spec_root] + map(lambda x: mllib.xml_parse(x)["amqp"], errata): # constants - for nd in root["constant"]: + for nd in root.query["constant"]: const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), - nd.get("@class"), get_docs(nd)) + nd["@class"], get_docs(nd)) try: spec.constants.add(const) - except ValueError, e: + except ValueError, e: print "Warning:", e # domains are typedefs - for nd in root["domain"]: + for nd in root.query["domain"]: spec.domains.add(Domain(spec, nd.index(), pythonize(nd["@name"]), pythonize(nd["@type"]), get_desc(nd), get_docs(nd))) # classes - for c_nd in root["class"]: + for c_nd in root.query["class"]: cname = pythonize(c_nd["@name"]) if spec.classes.byname.has_key(cname): klass = spec.classes.byname[cname] @@ -331,16 +330,16 @@ def load(specfile, *errata): added_methods = [] load_fields(c_nd, klass.fields, spec.domains.byname) - for m_nd in c_nd["method"]: + for m_nd in c_nd.query["method"]: mname = pythonize(m_nd["@name"]) if klass.methods.byname.has_key(mname): meth = klass.methods.byname[mname] else: meth = Method(klass, mname, int(m_nd["@index"]), - m_nd.get_bool("@content", False), - [pythonize(nd["@name"]) for nd in m_nd["response"]], - m_nd.get_bool("@synchronous", False), + m_nd["@content"] == "1", + [pythonize(nd["@name"]) for nd in m_nd.query["response"]], + m_nd["@synchronous"] == "1", get_desc(m_nd), get_docs(m_nd)) klass.methods.add(meth) diff --git a/qpid/python/qpid/xmlutil.py b/qpid/python/qpid/xmlutil.py deleted file mode 100644 index 585516b44f..0000000000 --- a/qpid/python/qpid/xmlutil.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# 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. -# - -""" -XML utilities used by spec.py -""" - -import xml.sax -from xml.sax.handler import ContentHandler - -def parse(file): - doc = Node("root") - xml.sax.parse(file, Builder(doc)) - return doc - -class Node: - - def __init__(self, name, attrs = None, text = None, parent = None): - self.name = name - self.attrs = attrs - self.text = text - self.parent = parent - self.children = [] - if parent != None: - parent.children.append(self) - - def get_bool(self, key, default = False): - v = self.get(key) - if v == None: - return default - else: - return bool(int(v)) - - def index(self): - if self.parent: - return self.parent.children.index(self) - else: - return 0 - - def has(self, key): - try: - result = self[key] - return True - except KeyError: - return False - except IndexError: - return False - - def get(self, key, default = None): - if self.has(key): - return self[key] - else: - return default - - def __getitem__(self, key): - if callable(key): - return filter(key, self.children) - else: - t = key.__class__ - meth = "__get%s__" % t.__name__ - if hasattr(self, meth): - return getattr(self, meth)(key) - else: - raise KeyError(key) - - def __getstr__(self, name): - if name[:1] == "@": - return self.attrs[name[1:]] - else: - return self[lambda nd: nd.name == name] - - def __getint__(self, index): - return self.children[index] - - def __iter__(self): - return iter(self.children) - - def path(self): - if self.parent == None: - return "/%s" % self.name - else: - return "%s/%s" % (self.parent.path(), self.name) - -class Builder(ContentHandler): - - def __init__(self, start = None): - self.node = start - - def __setitem__(self, element, type): - self.types[element] = type - - def startElement(self, name, attrs): - self.node = Node(name, attrs, None, self.node) - - def endElement(self, name): - self.node = self.node.parent - - def characters(self, content): - if self.node.text == None: - self.node.text = content - else: - self.node.text += content - -- cgit v1.2.1 From dc07ee70d71b2347c95a63878ad1ac7fc954f703 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 24 Jul 2007 14:08:32 +0000 Subject: Some initial 0-10 support including placeholders for new domains, use of execution layer for synchronising methods with no explicit responses and a new set of tests (mainly just copies of the 0-9 ones, but these will be altered as 0-10 support progresses). git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@559055 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 4 + qpid/python/qpid/codec.py | 30 +- qpid/python/qpid/peer.py | 13 +- qpid/python/qpid/spec.py | 10 +- qpid/python/qpid/testlib.py | 6 +- qpid/python/tests/codec.py | 7 - qpid/python/tests_0-10/__init__.py | 20 + qpid/python/tests_0-10/alternate-exchange.py | 178 ++++++++ qpid/python/tests_0-10/basic.py | 396 ++++++++++++++++ qpid/python/tests_0-10/broker.py | 126 +++++ qpid/python/tests_0-10/dtx.py | 587 ++++++++++++++++++++++++ qpid/python/tests_0-10/example.py | 94 ++++ qpid/python/tests_0-10/exchange.py | 327 +++++++++++++ qpid/python/tests_0-10/execution.py | 29 ++ qpid/python/tests_0-10/message.py | 657 +++++++++++++++++++++++++++ qpid/python/tests_0-10/query.py | 224 +++++++++ qpid/python/tests_0-10/queue.py | 340 ++++++++++++++ qpid/python/tests_0-10/testlib.py | 66 +++ qpid/python/tests_0-10/tx.py | 188 ++++++++ qpid/python/tests_0-9/broker.py | 2 +- 20 files changed, 3283 insertions(+), 21 deletions(-) create mode 100644 qpid/python/cpp_failing_0-10.txt create mode 100644 qpid/python/tests_0-10/__init__.py create mode 100644 qpid/python/tests_0-10/alternate-exchange.py create mode 100644 qpid/python/tests_0-10/basic.py create mode 100644 qpid/python/tests_0-10/broker.py create mode 100644 qpid/python/tests_0-10/dtx.py create mode 100644 qpid/python/tests_0-10/example.py create mode 100644 qpid/python/tests_0-10/exchange.py create mode 100644 qpid/python/tests_0-10/execution.py create mode 100644 qpid/python/tests_0-10/message.py create mode 100644 qpid/python/tests_0-10/query.py create mode 100644 qpid/python/tests_0-10/queue.py create mode 100644 qpid/python/tests_0-10/testlib.py create mode 100644 qpid/python/tests_0-10/tx.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt new file mode 100644 index 0000000000..798d1769eb --- /dev/null +++ b/qpid/python/cpp_failing_0-10.txt @@ -0,0 +1,4 @@ +tests_0-10.message.MessageTests.test_checkpoint +tests_0-10.message.MessageTests.test_reject +tests_0-10.basic.BasicTests.test_get + diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index b615a0f6ed..02b924b6b8 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -258,17 +258,9 @@ class Codec: codec = Codec(enc) if tbl: for key, value in tbl.items(): - # Field names MUST start with a letter, '$' or '#' and may - # continue with letters, '$' or '#', digits, or underlines, to - # a maximum length of 128 characters. - if len(key) > 128: raise ValueError("field table key too long: '%s'" % key) - m = Codec.KEY_CHECK.match(key) - if m == None or m.end() != len(key): - raise ValueError("invalid field table key: '%s'" % key) - codec.encode_shortstr(key) if isinstance(value, basestring): codec.write("S") @@ -338,3 +330,25 @@ class Codec: return self.decode_longstr() else: return ReferenceId(self.decode_longstr()) + + # new domains for 0-10: + + def encode_uuid(self, s): + self.encode_longstr(s) + + def decode_uuid(self): + return self.decode_longstr() + + def encode_rfc1982_long(self, s): + self.encode_long(s) + + def decode_rfc1982_long(self): + return self.decode_long() + + #Not done yet + def encode_rfc1982_long_set(self, s): + self.encode_short(0) + + def decode_rfc1982_long_set(self): + self.decode_short() + return 0; diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 9880eea19b..d7ae85e345 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -189,7 +189,9 @@ class Channel: self.completion = ExecutionCompletion() # Use reliable framing if version == 0-9. - self.reliable = (spec.major == 0 and spec.minor == 9) + # (also for 0-10 while transitioning...) + self.reliable = (spec.major == 0 and (spec.minor == 9 or spec.minor == 10)) + self.use_execution_layer = (spec.major == 0 and spec.minor == 10) self.synchronous = True def close(self, reason): @@ -199,6 +201,7 @@ class Channel: self.reason = reason self.incoming.close() self.responses.close() + self.completion.close() def write(self, frame, content = None): if self.closed: @@ -261,6 +264,11 @@ class Channel: self.request(frame, self.queue_response, content) if not frame.method.responses: + if self.use_execution_layer and type.klass.name != "execution": + self.execution_flush() + self.completion.wait() + if self.closed: + raise Closed(self.reason) return None try: resp = self.responses.get() @@ -353,6 +361,9 @@ class ExecutionCompletion: if method.klass.name != "execution": self.command_id = self.sequence.next() + def close(self): + self.completed.set() + def complete(self, mark): self.mark = mark self.completed.set() diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 88faedd825..c537401164 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -237,7 +237,11 @@ class Method(Metadata): "long": 0, "longlong": 0, "timestamp": 0, - "content": None} + "content": None, + "uuid": "", + "rfc1982_long": 0, + "rfc1982_long_set": 0 + } def define_method(self, name): g = {Method.METHOD: self} @@ -306,7 +310,9 @@ def load(specfile, *errata): for root in [spec_root] + map(lambda x: mllib.xml_parse(x)["amqp"], errata): # constants for nd in root.query["constant"]: - const = Constant(spec, pythonize(nd["@name"]), int(nd["@value"]), + val = nd["@value"] + if val.startswith("0x"): continue + const = Constant(spec, pythonize(nd["@name"]), int(val), nd["@class"], get_docs(nd)) try: spec.constants.add(const) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 4467a1f4cd..2c22c04199 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -125,6 +125,8 @@ Options: self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") + elif self.spec.major == 0 and self.spec.minor == 10: + self.tests+=findmodules("tests_0-10") else: self.tests+=findmodules("tests_0-9") @@ -214,10 +216,10 @@ class TestBase(unittest.TestCase): def exchange_declare(self, channel=None, ticket=0, exchange='', type='', passive=False, durable=False, - auto_delete=False, internal=False, nowait=False, + auto_delete=False, arguments={}): channel = channel or self.channel - reply = channel.exchange_declare(ticket, exchange, type, passive, durable, auto_delete, internal, nowait, arguments) + reply = channel.exchange_declare(ticket=ticket, exchange=exchange, type=type, passive=passive,durable=durable, auto_delete=auto_delete, arguments=arguments) self.exchanges.append((channel,exchange)) return reply diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py index 12dd5c85fc..785f1aba6b 100644 --- a/qpid/python/tests/codec.py +++ b/qpid/python/tests/codec.py @@ -432,13 +432,6 @@ class FieldTableTestCase(BaseDataTypes): """ self.failUnlessEqual(self.callFunc('encode_table', {'$key1':'value1'}), '\x00\x00\x00\x11\x05$key1S\x00\x00\x00\x06value1', 'valid name value pair encoding FAILED...') - # ------------------------------------------- - def test_field_table_invalid_field_name(self): - """ - invalid field name - """ - self.failUnlessRaises(Exception, self.codec.encode_table, {'1key1':'value1'}) - # ---------------------------------------------------- def test_field_table_invalid_field_name_length(self): """ diff --git a/qpid/python/tests_0-10/__init__.py b/qpid/python/tests_0-10/__init__.py new file mode 100644 index 0000000000..9a09d2d04f --- /dev/null +++ b/qpid/python/tests_0-10/__init__.py @@ -0,0 +1,20 @@ +# Do not delete - marks this directory as a python package. + +# +# 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. +# diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py new file mode 100644 index 0000000000..19405a1c9f --- /dev/null +++ b/qpid/python/tests_0-10/alternate-exchange.py @@ -0,0 +1,178 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class AlternateExchangeTests(TestBase): + """ + Tests for the new mechanism for message returns introduced in 0-10 + and available in 0-9 for preview + """ + + def test_unroutable(self): + """ + Test that unroutable messages are delivered to the alternate-exchange if specified + """ + channel = self.channel + #create an exchange with an alternate defined + channel.exchange_declare(exchange="secondary", type="fanout") + channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") + + #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages + channel.queue_declare(queue="returns", exclusive=True) + channel.queue_bind(queue="returns", exchange="secondary") + channel.message_consume(destination="a", queue="returns") + returned = self.client.queue("a") + + #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages + channel.queue_declare(queue="processed", exclusive=True) + channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") + channel.message_consume(destination="b", queue="processed") + processed = self.client.queue("b") + + #publish to the primary exchange + #...one message that makes it to the 'processed' queue: + channel.message_transfer(destination="primary", routing_key="my-key", body="Good") + #...and one that does not: + channel.message_transfer(destination="primary", routing_key="unused-key", body="Bad") + + #delete the exchanges + channel.exchange_delete(exchange="primary") + channel.exchange_delete(exchange="secondary") + + #verify behaviour + self.assertEqual("Good", processed.get(timeout=1).body) + self.assertEqual("Bad", returned.get(timeout=1).body) + self.assertEmpty(processed) + self.assertEmpty(returned) + + def test_queue_delete(self): + """ + Test that messages in a queue being deleted are delivered to the alternate-exchange if specified + """ + channel = self.channel + #set up a 'dead letter queue': + channel.exchange_declare(exchange="dlq", type="fanout") + channel.queue_declare(queue="deleted", exclusive=True) + channel.queue_bind(exchange="dlq", queue="deleted") + channel.message_consume(destination="dlq", queue="deleted") + dlq = self.client.queue("dlq") + + #create a queue using the dlq as its alternate exchange: + channel.queue_declare(queue="delete-me", alternate_exchange="dlq") + #send it some messages: + channel.message_transfer(routing_key="delete-me", body="One") + channel.message_transfer(routing_key="delete-me", body="Two") + channel.message_transfer(routing_key="delete-me", body="Three") + #delete it: + channel.queue_delete(queue="delete-me") + #delete the dlq exchange: + channel.exchange_delete(exchange="dlq") + + #check the messages were delivered to the dlq: + self.assertEqual("One", dlq.get(timeout=1).body) + self.assertEqual("Two", dlq.get(timeout=1).body) + self.assertEqual("Three", dlq.get(timeout=1).body) + self.assertEmpty(dlq) + + + def test_immediate(self): + """ + Test that messages in a queue being deleted are delivered to the alternate-exchange if specified + """ + channel = self.channel + #set up a 'dead letter queue': + channel.exchange_declare(exchange="dlq", type="fanout") + channel.queue_declare(queue="immediate", exclusive=True) + channel.queue_bind(exchange="dlq", queue="immediate") + channel.message_consume(destination="dlq", queue="immediate") + dlq = self.client.queue("dlq") + + #create a queue using the dlq as its alternate exchange: + channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True) + #send it some messages: + channel.message_transfer(routing_key="no-consumers", body="no one wants me", immediate=True) + + #check the messages were delivered to the dlq: + self.assertEqual("no one wants me", dlq.get(timeout=1).body) + self.assertEmpty(dlq) + + #cleanup: + channel.queue_delete(queue="no-consumers") + channel.exchange_delete(exchange="dlq") + + + def test_delete_while_used_by_queue(self): + """ + Ensure an exchange still in use as an alternate-exchange for a + queue can't be deleted + """ + channel = self.channel + channel.exchange_declare(exchange="alternate", type="fanout") + channel.queue_declare(queue="q", exclusive=True, alternate_exchange="alternate") + try: + channel.exchange_delete(exchange="alternate") + self.fail("Expected deletion of in-use alternate-exchange to fail") + except Closed, e: + #cleanup: + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.exchange_delete(exchange="alternate") + channel.channel_close(200, "ok") + other.close() + + self.assertConnectionException(530, e.args[0]) + + + + def test_delete_while_used_by_exchange(self): + """ + Ensure an exchange still in use as an alternate-exchange for + another exchange can't be deleted + """ + channel = self.channel + channel.exchange_declare(exchange="alternate", type="fanout") + channel.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") + try: + channel.exchange_delete(exchange="alternate") + #cleanup: + channel.exchange_delete(exchange="e") + self.fail("Expected deletion of in-use alternate-exchange to fail") + except Closed, e: + #cleanup: + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.exchange_delete(exchange="e") + channel.exchange_delete(exchange="alternate") + channel.channel_close(200, "ok") + other.close() + + self.assertConnectionException(530, e.args[0]) + + + def assertEmpty(self, queue): + try: + msg = queue.get(timeout=1) + self.fail("Queue not empty: " + msg) + except Empty: None + diff --git a/qpid/python/tests_0-10/basic.py b/qpid/python/tests_0-10/basic.py new file mode 100644 index 0000000000..e7d22e00da --- /dev/null +++ b/qpid/python/tests_0-10/basic.py @@ -0,0 +1,396 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BasicTests(TestBase): + """Tests for 'methods' on the amqp basic 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a") + channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local")) + channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local")) + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True) + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.basic_consume(consumer_tag="first", queue="test-queue-2") + try: + channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.basic_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.basic_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + try: + channel.basic_consume(consumer_tag="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") + channel.basic_publish(routing_key="test-queue-4", content=Content("One")) + + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.content.body) + + #cancel should stop messages being delivered + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.basic_cancel(consumer_tag="my-consumer") + channel.basic_cancel(consumer_tag="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True) + + reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) + queue = self.client.queue(reply.consumer_tag) + + channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) + channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True) + + subscription = channel.basic_consume(queue="test-requeue", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + channel.basic_publish(routing_key="test-requeue", content=Content("One")) + channel.basic_publish(routing_key="test-requeue", content=Content("Two")) + channel.basic_publish(routing_key="test-requeue", content=Content("Three")) + channel.basic_publish(routing_key="test-requeue", content=Content("Four")) + channel.basic_publish(routing_key="test-requeue", content=Content("Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two + channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four + + channel.basic_cancel(consumer_tag=subscription.consumer_tag) + + channel.basic_recover(requeue=True) + + subscription2 = channel.basic_consume(queue="test-requeue") + queue2 = self.client.queue(subscription2.consumer_tag) + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.content.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.content.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 5: + channel.basic_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False) + queue = self.client.queue(subscription.consumer_tag) + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.basic_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i)) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.content.body) + + def test_get(self): + """ + Test basic_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + #use basic_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) + + for i in range(11, 21): + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + if(i == 13): + channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) + if(i in [15, 17, 19]): + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + #recover(requeue=True) + channel.basic_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + reply = channel.basic_get(no_ack=False) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_ok") + self.assertEqual("Message %d" % i, reply.content.body) + channel.basic_ack(delivery_tag=reply.delivery_tag) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") + + channel.basic_recover(requeue=True) + + reply = channel.basic_get(no_ack=True) + self.assertEqual(reply.method.klass.name, "basic") + self.assertEqual(reply.method.name, "get_empty") diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py new file mode 100644 index 0000000000..684b36597e --- /dev/null +++ b/qpid/python/tests_0-10/broker.py @@ -0,0 +1,126 @@ +# +# 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. +# +from qpid.client import Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BrokerTests(TestBase): + """Tests for basic Broker functionality""" + + def test_ack_and_no_ack(self): + """ + First, this test tries to receive a message with a no-ack + consumer. Second, this test tries to explicitly receive and + acknowledge a message with an acknowledging consumer. + """ + ch = self.channel + self.queue_declare(ch, queue = "myqueue") + + # No ack consumer + ctag = "tag1" + ch.message_consume(queue = "myqueue", destination = ctag, no_ack = True) + body = "test no-ack" + ch.message_transfer(routing_key = "myqueue", body = body) + msg = self.client.queue(ctag).get(timeout = 5) + self.assert_(msg.body == body) + + # Acknowledging consumer + self.queue_declare(ch, queue = "otherqueue") + ctag = "tag2" + ch.message_consume(queue = "otherqueue", destination = ctag, no_ack = False) + body = "test ack" + ch.message_transfer(routing_key = "otherqueue", body = body) + msg = self.client.queue(ctag).get(timeout = 5) + msg.ok() + self.assert_(msg.body == body) + + def test_simple_delivery_immediate(self): + """ + Test simple message delivery where consume is issued before publish + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + consumer_tag = "tag1" + channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + queue = self.client.queue(consumer_tag) + + body = "Immediate Delivery" + channel.message_transfer(destination="test-exchange", routing_key="key", body=body, immediate=True) + msg = queue.get(timeout=5) + self.assert_(msg.body == body) + + # TODO: Ensure we fail if immediate=True and there's no consumer. + + + def test_simple_delivery_queued(self): + """ + Test basic message delivery where publish is issued before consume + (i.e. requires queueing of the message) + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + body = "Queued Delivery" + channel.message_transfer(destination="test-exchange", routing_key="key", body=body) + + consumer_tag = "tag1" + channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + queue = self.client.queue(consumer_tag) + msg = queue.get(timeout=5) + self.assert_(msg.body == body) + + def test_invalid_channel(self): + channel = self.client.channel(200) + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for invalid channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) + + def test_closed_channel(self): + channel = self.client.channel(200) + channel.channel_open() + channel.channel_close() + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for closed channel") + except Closed, e: + if isinstance(e.args[0], str): self.fail(e) + self.assertConnectionException(504, e.args[0]) + + def test_channel_flow(self): + channel = self.channel + channel.queue_declare(queue="flow_test_queue", exclusive=True) + channel.message_consume(destination="my-tag", queue="flow_test_queue") + incoming = self.client.queue("my-tag") + + channel.channel_flow(active=False) + channel.message_transfer(routing_key="flow_test_queue", body="abcdefghijklmnopqrstuvwxyz") + try: + incoming.get(timeout=1) + self.fail("Received message when flow turned off.") + except Empty: None + + channel.channel_flow(active=True) + msg = incoming.get(timeout=1) + self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.body) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py new file mode 100644 index 0000000000..c0d1bd2b74 --- /dev/null +++ b/qpid/python/tests_0-10/dtx.py @@ -0,0 +1,587 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from struct import pack, unpack +from time import sleep + +class DtxTests(TestBase): + """ + Tests for the amqp dtx related classes. + + Tests of the form test_simple_xxx test the basic transactional + behaviour. The approach here is to 'swap' a message from one queue + to another by consuming and re-publishing in the same + transaction. That transaction is then completed in different ways + and the appropriate result verified. + + The other tests enforce more specific rules and behaviour on a + per-method or per-field basis. + """ + + XA_RBROLLBACK = 1 + XA_RBTIMEOUT = 2 + XA_OK = 8 + + def test_simple_commit(self): + """ + Test basic one-phase commit behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "commit") + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #commit + self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).status) + + #check result + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(1, "queue-b") + self.assertMessageId("commit", "queue-b") + + def test_simple_prepare_commit(self): + """ + Test basic two-phase commit behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "prepare-commit") + + #prepare + self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #commit + self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).status) + + #check result + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(1, "queue-b") + self.assertMessageId("prepare-commit", "queue-b") + + + def test_simple_rollback(self): + """ + Test basic rollback behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "rollback") + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #rollback + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + + #check result + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("rollback", "queue-a") + + def test_simple_prepare_rollback(self): + """ + Test basic rollback behaviour after the transaction has been prepared. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "prepare-rollback") + + #prepare + self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #rollback + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + + #check result + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("prepare-rollback", "queue-a") + + def test_select_required(self): + """ + check that an error is flagged if select is not issued before + start or end + """ + channel = self.channel + tx = self.xid("dummy") + try: + channel.dtx_demarcation_start(xid=tx) + + #if we get here we have failed, but need to do some cleanup: + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + self.fail("Channel not selected for use with dtx, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_start_already_known(self): + """ + Verify that an attempt to start an association with a + transaction that is already known is not allowed (unless the + join flag is set). + """ + #create two channels on different connection & select them for use with dtx: + channel1 = self.channel + channel1.dtx_demarcation_select() + + other = self.connect() + channel2 = other.channel(1) + channel2.channel_open() + channel2.dtx_demarcation_select() + + #create a xid + tx = self.xid("dummy") + #start work on one channel under that xid: + channel1.dtx_demarcation_start(xid=tx) + #then start on the other without the join set + failed = False + try: + channel2.dtx_demarcation_start(xid=tx) + except Closed, e: + failed = True + error = e + + #cleanup: + if not failed: + channel2.dtx_demarcation_end(xid=tx) + other.close() + channel1.dtx_demarcation_end(xid=tx) + channel1.dtx_coordination_rollback(xid=tx) + + #verification: + if failed: self.assertConnectionException(503, e.args[0]) + else: self.fail("Xid already known, expected exception!") + + def test_forget_xid_on_completion(self): + """ + Verify that a xid is 'forgotten' - and can therefore be used + again - once it is completed. + """ + channel = self.channel + #do some transactional work & complete the transaction + self.test_simple_commit() + + #start association for the same xid as the previously completed txn + tx = self.xid("my-xid") + channel.dtx_demarcation_start(xid=tx) + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + + def test_start_join_and_resume(self): + """ + Ensure the correct error is signalled when both the join and + resume flags are set on starting an association between a + channel and a transcation. + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("dummy") + try: + channel.dtx_demarcation_start(xid=tx, join=True, resume=True) + #failed, but need some cleanup: + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + self.fail("Join and resume both set, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_start_join(self): + """ + Verify 'join' behaviour, where a channel is associated with a + transaction that is already associated with another channel. + """ + #create two channels & select them for use with dtx: + channel1 = self.channel + channel1.dtx_demarcation_select() + + channel2 = self.client.channel(2) + channel2.channel_open() + channel2.dtx_demarcation_select() + + #setup + channel1.queue_declare(queue="one", exclusive=True) + channel1.queue_declare(queue="two", exclusive=True) + channel1.message_transfer(routing_key="one", message_id="a", body="DtxMessage") + channel1.message_transfer(routing_key="two", message_id="b", body="DtxMessage") + + #create a xid + tx = self.xid("dummy") + #start work on one channel under that xid: + channel1.dtx_demarcation_start(xid=tx) + #then start on the other with the join flag set + channel2.dtx_demarcation_start(xid=tx, join=True) + + #do work through each channel + self.swap(channel1, "one", "two")#swap 'a' from 'one' to 'two' + self.swap(channel2, "two", "one")#swap 'b' from 'two' to 'one' + + #mark end on both channels + channel1.dtx_demarcation_end(xid=tx) + channel2.dtx_demarcation_end(xid=tx) + + #commit and check + channel1.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + + def test_suspend_resume(self): + """ + Test suspension and resumption of an association + """ + channel = self.channel + channel.dtx_demarcation_select() + + #setup + channel.queue_declare(queue="one", exclusive=True) + channel.queue_declare(queue="two", exclusive=True) + channel.message_transfer(routing_key="one", message_id="a", body="DtxMessage") + channel.message_transfer(routing_key="two", message_id="b", body="DtxMessage") + + tx = self.xid("dummy") + + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' + channel.dtx_demarcation_end(xid=tx, suspend=True) + + channel.dtx_demarcation_start(xid=tx, resume=True) + self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' + channel.dtx_demarcation_end(xid=tx) + + #commit and check + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + def test_end_suspend_and_fail(self): + """ + Verify that the correct error is signalled if the suspend and + fail flag are both set when disassociating a transaction from + the channel + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("suspend_and_fail") + channel.dtx_demarcation_start(xid=tx) + try: + channel.dtx_demarcation_end(xid=tx, suspend=True, fail=True) + self.fail("Suspend and fail both set, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.dtx_coordination_rollback(xid=tx) + channel.channel_close() + other.close() + + + def test_end_unknown_xid(self): + """ + Verifies that the correct exception is thrown when an attempt + is made to end the association for a xid not previously + associated with the channel + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("unknown-xid") + try: + channel.dtx_demarcation_end(xid=tx) + self.fail("Attempted to end association with unknown xid, expected exception!") + except Closed, e: + #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming... + self.assertConnectionException(503, e.args[0]) + + def test_end(self): + """ + Verify that the association is terminated by end and subsequent + operations are non-transactional + """ + channel = self.client.channel(2) + channel.channel_open() + channel.queue_declare(queue="tx-queue", exclusive=True) + + #publish a message under a transaction + channel.dtx_demarcation_select() + tx = self.xid("dummy") + channel.dtx_demarcation_start(xid=tx) + channel.message_transfer(routing_key="tx-queue", message_id="one", body="DtxMessage") + channel.dtx_demarcation_end(xid=tx) + + #now that association with txn is ended, publish another message + channel.message_transfer(routing_key="tx-queue", message_id="two", body="DtxMessage") + + #check the second message is available, but not the first + self.assertMessageCount(1, "tx-queue") + channel.message_consume(queue="tx-queue", destination="results", no_ack=False) + msg = self.client.queue("results").get(timeout=1) + self.assertEqual("two", msg.message_id) + channel.message_cancel(destination="results") + #ack the message then close the channel + msg.ok() + channel.channel_close() + + channel = self.channel + #commit the transaction and check that the first message (and + #only the first message) is then delivered + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "tx-queue") + self.assertMessageId("one", "tx-queue") + + def test_invalid_commit_one_phase_true(self): + """ + Test that a commit with one_phase = True is rejected if the + transaction in question has already been prepared. + """ + other = self.connect() + tester = other.channel(1) + tester.channel_open() + tester.queue_declare(queue="dummy", exclusive=True) + tester.dtx_demarcation_select() + tx = self.xid("dummy") + tester.dtx_demarcation_start(xid=tx) + tester.message_transfer(routing_key="dummy", body="whatever") + tester.dtx_demarcation_end(xid=tx) + tester.dtx_coordination_prepare(xid=tx) + failed = False + try: + tester.dtx_coordination_commit(xid=tx, one_phase=True) + except Closed, e: + failed = True + error = e + + if failed: + self.channel.dtx_coordination_rollback(xid=tx) + self.assertConnectionException(503, e.args[0]) + else: + tester.channel_close() + other.close() + self.fail("Invalid use of one_phase=True, expected exception!") + + def test_invalid_commit_one_phase_false(self): + """ + Test that a commit with one_phase = False is rejected if the + transaction in question has not yet been prepared. + """ + """ + Test that a commit with one_phase = True is rejected if the + transaction in question has already been prepared. + """ + other = self.connect() + tester = other.channel(1) + tester.channel_open() + tester.queue_declare(queue="dummy", exclusive=True) + tester.dtx_demarcation_select() + tx = self.xid("dummy") + tester.dtx_demarcation_start(xid=tx) + tester.message_transfer(routing_key="dummy", body="whatever") + tester.dtx_demarcation_end(xid=tx) + failed = False + try: + tester.dtx_coordination_commit(xid=tx, one_phase=False) + except Closed, e: + failed = True + error = e + + if failed: + self.channel.dtx_coordination_rollback(xid=tx) + self.assertConnectionException(503, e.args[0]) + else: + tester.channel_close() + other.close() + self.fail("Invalid use of one_phase=False, expected exception!") + + def test_implicit_end(self): + """ + Test that an association is implicitly ended when the channel + is closed (whether by exception or explicit client request) + and the transaction in question is marked as rollback only. + """ + channel1 = self.channel + channel2 = self.client.channel(2) + channel2.channel_open() + + #setup: + channel2.queue_declare(queue="dummy", exclusive=True) + channel2.message_transfer(routing_key="dummy", body="whatever") + tx = self.xid("dummy") + + channel2.dtx_demarcation_select() + channel2.dtx_demarcation_start(xid=tx) + channel2.message_get(queue="dummy", destination="dummy") + self.client.queue("dummy").get(timeout=1).ok() + channel2.message_transfer(routing_key="dummy", body="whatever") + channel2.channel_close() + + self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).status) + channel1.dtx_coordination_rollback(xid=tx) + + def test_get_timeout(self): + """ + Check that get-timeout returns the correct value, (and that a + transaction with a timeout can complete normally) + """ + channel = self.channel + tx = self.xid("dummy") + + channel.dtx_demarcation_select() + channel.dtx_demarcation_start(xid=tx) + self.assertEqual(0, channel.dtx_coordination_get_timeout(xid=tx).timeout) + channel.dtx_coordination_set_timeout(xid=tx, timeout=60) + self.assertEqual(60, channel.dtx_coordination_get_timeout(xid=tx).timeout) + self.assertEqual(self.XA_OK, channel.dtx_demarcation_end(xid=tx).status) + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + + def test_set_timeout(self): + """ + Test the timeout of a transaction results in the expected + behaviour + """ + #open new channel to allow self.channel to be used in checking te queue + channel = self.client.channel(2) + channel.channel_open() + #setup: + tx = self.xid("dummy") + channel.queue_declare(queue="queue-a", exclusive=True) + channel.queue_declare(queue="queue-b", exclusive=True) + channel.message_transfer(routing_key="queue-a", message_id="timeout", body="DtxMessage") + + channel.dtx_demarcation_select() + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "queue-a", "queue-b") + channel.dtx_coordination_set_timeout(xid=tx, timeout=2) + sleep(3) + #check that the work has been rolled back already + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("timeout", "queue-a") + #check the correct codes are returned when we try to complete the txn + self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_demarcation_end(xid=tx).status) + self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_coordination_rollback(xid=tx).status) + + + + def test_recover(self): + """ + Test basic recover behaviour + """ + channel = self.channel + + channel.dtx_demarcation_select() + channel.queue_declare(queue="dummy", exclusive=True) + + prepared = [] + for i in range(1, 10): + tx = self.xid("tx%s" % (i)) + channel.dtx_demarcation_start(xid=tx) + channel.message_transfer(routing_key="dummy", body="message%s" % (i)) + channel.dtx_demarcation_end(xid=tx) + if i in [2, 5, 6, 8]: + channel.dtx_coordination_prepare(xid=tx) + prepared.append(tx) + else: + channel.dtx_coordination_rollback(xid=tx) + + indoubt = channel.dtx_coordination_recover().in_doubt + #convert indoubt table to a list of xids (note: this will change for 0-10) + data = indoubt["xids"] + xids = [] + pos = 0 + while pos < len(data): + size = unpack("!B", data[pos])[0] + start = pos + 1 + end = start + size + xid = data[start:end] + xids.append(xid) + pos = end + + #rollback the prepared transactions returned by recover + for x in xids: + channel.dtx_coordination_rollback(xid=x) + + #validate against the expected list of prepared transactions + actual = set(xids) + expected = set(prepared) + intersection = actual.intersection(expected) + + if intersection != expected: + missing = expected.difference(actual) + extra = actual.difference(expected) + for x in missing: + channel.dtx_coordination_rollback(xid=x) + self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) + + def xid(self, txid, branchqual = ''): + return pack('LBB', 0, len(txid), len(branchqual)) + txid + branchqual + + def txswap(self, tx, id): + channel = self.channel + #declare two queues: + channel.queue_declare(queue="queue-a", exclusive=True) + channel.queue_declare(queue="queue-b", exclusive=True) + #put message with specified id on one queue: + channel.message_transfer(routing_key="queue-a", message_id=id, body="DtxMessage") + + #start the transaction: + channel.dtx_demarcation_select() + self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_start(xid=tx).status) + + #'swap' the message from one queue to the other, under that transaction: + self.swap(self.channel, "queue-a", "queue-b") + + #mark the end of the transactional work: + self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_end(xid=tx).status) + + def swap(self, channel, src, dest): + #consume from src: + channel.message_get(destination="temp-swap", queue=src) + msg = self.client.queue("temp-swap").get(timeout=1) + msg.ok(); + + #re-publish to dest + channel.message_transfer(routing_key=dest, message_id=msg.message_id, body=msg.body) + + def assertMessageCount(self, expected, queue): + self.assertEqual(expected, self.channel.queue_declare(queue=queue, passive=True).message_count) + + def assertMessageId(self, expected, queue): + self.channel.message_consume(queue=queue, destination="results", no_ack=True) + self.assertEqual(expected, self.client.queue("results").get(timeout=1).message_id) + self.channel.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py new file mode 100644 index 0000000000..7ab4cc7d0a --- /dev/null +++ b/qpid/python/tests_0-10/example.py @@ -0,0 +1,94 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExampleTest (TestBase): + """ + An example Qpid test, illustrating the unittest frameowkr and the + python Qpid client. The test class must inherit TestCase. The + test code uses the Qpid client to interact with a qpid broker and + verify it behaves as expected. + """ + + def test_example(self): + """ + An example test. Note that test functions must start with 'test_' + to be recognized by the test framework. + """ + + # By inheriting TestBase, self.client is automatically connected + # and self.channel is automatically opened as channel(1) + # Other channel methods mimic the protocol. + channel = self.channel + + # Now we can send regular commands. If you want to see what the method + # arguments mean or what other commands are available, you can use the + # python builtin help() method. For example: + #help(chan) + #help(chan.exchange_declare) + + # If you want browse the available protocol methods without being + # connected to a live server you can use the amqp-doc utility: + # + # Usage amqp-doc [] [ ... ] + # + # Options: + # -e, --regexp use regex instead of glob when matching + + # Now that we know what commands are available we can use them to + # interact with the server. + + # Here we use ordinal arguments. + self.exchange_declare(channel, 0, "test", "direct") + + # Here we use keyword arguments. + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") + + # Call Channel.basic_consume to register as a consumer. + # All the protocol methods return a message object. The message object + # has fields corresponding to the reply method fields, plus a content + # field that is filled if the reply includes content. In this case the + # interesting field is the consumer_tag. + channel.message_consume(queue="test-queue", destination="consumer_tag") + + # We can use the Client.queue(...) method to access the queue + # corresponding to our consumer_tag. + queue = self.client.queue("consumer_tag") + + # Now lets publish a message and see if our consumer gets it. To do + # this we need to import the Content class. + body = "Hello World!" + channel.message_transfer(destination="test", + routing_key="key", + body = body) + + # Now we'll wait for the message to arrive. We can use the timeout + # argument in case the server hangs. By default queue.get() will wait + # until a message arrives or the connection to the server dies. + msg = queue.get(timeout=10) + + # And check that we got the right response with assertEqual + self.assertEqual(body, msg.body) + + # Now acknowledge the message. + msg.ok() + diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py new file mode 100644 index 0000000000..3a47ffff8c --- /dev/null +++ b/qpid/python/tests_0-10/exchange.py @@ -0,0 +1,327 @@ +# +# 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. +# + +""" +Tests for exchange behaviour. + +Test classes ending in 'RuleTests' are derived from rules in amqp.xml. +""" + +import Queue, logging +from qpid.testlib import TestBase +from qpid.content import Content +from qpid.client import Closed + + +class StandardExchangeVerifier: + """Verifies standard exchange behavior. + + Used as base class for classes that test standard exchanges.""" + + def verifyDirectExchange(self, ex): + """Verify that ex behaves like a direct exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") + self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") + try: + self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") + self.fail("Expected Empty exception") + except Queue.Empty: None # Expected + + def verifyFanOutExchange(self, ex): + """Verify that ex behaves like a fanout exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex) + self.queue_declare(queue="p") + self.channel.queue_bind(queue="p", exchange=ex) + for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) + + def verifyTopicExchange(self, ex): + """Verify that ex behaves like a topic exchange""" + self.queue_declare(queue="a") + self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + q = self.consume("a") + self.assertPublishGet(q, ex, "a.b.x") + self.assertPublishGet(q, ex, "a.x.b.x") + self.assertPublishGet(q, ex, "a.x.x.b.x") + # Shouldn't match + self.channel.message_transfer(destination=ex, routing_key="a.b", body="") + self.channel.message_transfer(destination=ex, routing_key="a.b.x.y", body="") + self.channel.message_transfer(destination=ex, routing_key="x.a.b.x", body="") + self.channel.message_transfer(destination=ex, routing_key="a.b", body="") + self.assert_(q.empty()) + + def verifyHeadersExchange(self, ex): + """Verify that ex is a headers exchange""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) + q = self.consume("q") + headers = {"name":"fred", "age":3} + self.assertPublishGet(q, exchange=ex, properties=headers) + self.channel.message_transfer(destination=ex, body="") # No headers, won't deliver + self.assertEmpty(q); + + +class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server SHOULD implement these standard exchange types: topic, headers. + + Client attempts to declare an exchange with each of these standard types. + """ + + def testDirect(self): + """Declare and test a direct exchange""" + self.exchange_declare(0, exchange="d", type="direct") + self.verifyDirectExchange("d") + + def testFanout(self): + """Declare and test a fanout exchange""" + self.exchange_declare(0, exchange="f", type="fanout") + self.verifyFanOutExchange("f") + + def testTopic(self): + """Declare and test a topic exchange""" + self.exchange_declare(0, exchange="t", type="topic") + self.verifyTopicExchange("t") + + def testHeaders(self): + """Declare and test a headers exchange""" + self.exchange_declare(0, exchange="h", type="headers") + self.verifyHeadersExchange("h") + + +class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST, in each virtual host, pre-declare an exchange instance + for each standard exchange type that it implements, where the name of the + exchange instance is amq. followed by the exchange type name. + + Client creates a temporary queue and attempts to bind to each required + exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if + those types are defined). + """ + def testAmqDirect(self): self.verifyDirectExchange("amq.direct") + + def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") + + def testAmqTopic(self): self.verifyTopicExchange("amq.topic") + + def testAmqMatch(self): self.verifyHeadersExchange("amq.match") + +class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST predeclare a direct exchange to act as the default exchange + for content Publish methods and for default queue bindings. + + Client checks that the default exchange is active by specifying a queue + binding with no exchange name, and publishing a message with a suitable + routing key but without specifying the exchange name, then ensuring that + the message arrives in the queue correctly. + """ + def testDefaultExchange(self): + # Test automatic binding by queue name. + self.queue_declare(queue="d") + self.assertPublishConsume(queue="d", routing_key="d") + # Test explicit bind to default queue + self.verifyDirectExchange("") + + +# TODO aconway 2006-09-27: Fill in empty tests: + +class DefaultAccessRuleTests(TestBase): + """ + The server MUST NOT allow clients to access the default exchange except + by specifying an empty exchange name in the Queue.Bind and content Publish + methods. + """ + +class ExtensionsRuleTests(TestBase): + """ + The server MAY implement other exchange types as wanted. + """ + + +class DeclareMethodMinimumRuleTests(TestBase): + """ + The server SHOULD support a minimum of 16 exchanges per virtual host and + ideally, impose no limit except as defined by available resources. + + The client creates as many exchanges as it can until the server reports + an error; the number of exchanges successfuly created must be at least + sixteen. + """ + + +class DeclareMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access to + the realm in which the exchange exists or will be created, or "passive" + access if the if-exists flag is set. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeclareMethodExchangeFieldReservedRuleTests(TestBase): + """ + Exchange names starting with "amq." are reserved for predeclared and + standardised exchanges. The client MUST NOT attempt to create an exchange + starting with "amq.". + + + """ + + +class DeclareMethodTypeFieldTypedRuleTests(TestBase): + """ + Exchanges cannot be redeclared with different types. The client MUST not + attempt to redeclare an existing exchange with a different type than used + in the original Exchange.Declare method. + + + """ + + +class DeclareMethodTypeFieldSupportRuleTests(TestBase): + """ + The client MUST NOT attempt to create an exchange with a type that the + server does not support. + + + """ + + +class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): + """ + If set, and the exchange does not already exist, the server MUST raise a + channel exception with reply code 404 (not found). + """ + def test(self): + try: + self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) + self.fail("Expected 404 for passive declaration of unknown exchange.") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + +class DeclareMethodDurableFieldSupportRuleTests(TestBase): + """ + The server MUST support both durable and transient exchanges. + + + """ + + +class DeclareMethodDurableFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the durable field if the exchange already exists. + + + """ + + +class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the auto-delete field if the exchange already + exists. + + + """ + + +class DeleteMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access + rights to the exchange's access realm. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeleteMethodExchangeFieldExistsRuleTests(TestBase): + """ + The client MUST NOT attempt to delete an exchange that does not exist. + """ + + +class HeadersExchangeTests(TestBase): + """ + Tests for headers exchange functionality. + """ + def setUp(self): + TestBase.setUp(self) + self.queue_declare(queue="q") + self.q = self.consume("q") + + def myAssertPublishGet(self, headers): + self.assertPublishGet(self.q, exchange="amq.match", properties=headers) + + def myBasicPublish(self, headers): + self.channel.message_transfer(destination="amq.match", body="foobar", application_headers=headers) + + def testMatchAll(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) + + # None of these should match + self.myBasicPublish({}) + self.myBasicPublish({"name":"barney"}) + self.myBasicPublish({"name":10}) + self.myBasicPublish({"name":"fred", "age":2}) + self.assertEmpty(self.q) + + def testMatchAny(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred"}) + self.myAssertPublishGet({"name":"fred", "ignoreme":10}) + self.myAssertPublishGet({"ignoreme":10, "age":3}) + + # Wont match + self.myBasicPublish({}) + self.myBasicPublish({"irrelevant":0}) + self.assertEmpty(self.q) + + +class MiscellaneousErrorsTests(TestBase): + """ + Test some miscellaneous error conditions + """ + def testTypeNotKnown(self): + try: + self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") + self.fail("Expected 503 for declaration of unknown exchange type.") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def testDifferentDeclaredType(self): + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + try: + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + self.fail("Expected 530 for redeclaration of exchange with different type.") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + #cleanup + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + c2.exchange_delete(exchange="test_different_declared_type_exchange") + diff --git a/qpid/python/tests_0-10/execution.py b/qpid/python/tests_0-10/execution.py new file mode 100644 index 0000000000..f2facfe42b --- /dev/null +++ b/qpid/python/tests_0-10/execution.py @@ -0,0 +1,29 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExecutionTests (TestBase): + def test_flush(self): + channel = self.channel + for i in [1, 2, 3]: + channel.basic_publish() + channel.execution_flush() + assert(channel.completion.wait(channel.completion.command_id, timeout=1)) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py new file mode 100644 index 0000000000..b25016e680 --- /dev/null +++ b/qpid/python/tests_0-10/message.py @@ -0,0 +1,657 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from qpid.reference import Reference, ReferenceId + +class MessageTests(TestBase): + """Tests for 'methods' on the amqp message 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True) + #establish two consumers one of which excludes delivery of locally sent messages + channel.message_consume(destination="local_included", queue="test-queue-1a") + channel.message_consume(destination="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local") + channel.message_transfer(routing_key="test-queue-1b", body="consume_no_local") + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True) + + #check that an exclusive consumer prevents other consumer being created: + channel.message_consume(destination="first", queue="test-queue-2", exclusive=True) + try: + channel.message_consume(destination="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.channel_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + channel.message_consume(destination="first", queue="test-queue-2") + try: + channel.message_consume(destination="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + channel.message_consume(queue="invalid-queue") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.message_consume(queue="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True) + + #check that attempts to use duplicate tags are detected and prevented: + channel.message_consume(destination="first", queue="test-queue-3") + try: + channel.message_consume(destination="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.message_consume(destination="my-consumer", queue="test-queue-4") + channel.message_transfer(routing_key="test-queue-4", body="One") + + #cancel should stop messages being delivered + channel.message_cancel(destination="my-consumer") + channel.message_transfer(routing_key="test-queue-4", body="Two") + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.message_cancel(destination="my-consumer") + channel.message_cancel(destination="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True) + + channel.message_consume(queue="test-ack-queue", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(routing_key="test-ack-queue", body="One") + channel.message_transfer(routing_key="test-ack-queue", body="Two") + channel.message_transfer(routing_key="test-ack-queue", body="Three") + channel.message_transfer(routing_key="test-ack-queue", body="Four") + channel.message_transfer(routing_key="test-ack-queue", body="Five") + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + msg1.ok(batchoffset=1)#One and Two + msg4.ok() + + channel.message_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True) + + channel.message_consume(queue="test-requeue", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(routing_key="test-requeue", body="One") + channel.message_transfer(routing_key="test-requeue", body="Two") + channel.message_transfer(routing_key="test-requeue", body="Three") + channel.message_transfer(routing_key="test-requeue", body="Four") + channel.message_transfer(routing_key="test-requeue", body="Five") + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + msg1.ok(batchoffset=1) #One and Two + msg4.ok() #Four + + channel.message_cancel(destination="consumer_tag") + + #publish a new message + channel.message_transfer(routing_key="test-requeue", body="Six") + #requeue unacked messages (Three and Five) + channel.message_recover(requeue=True) + + channel.message_consume(queue="test-requeue", destination="consumer_tag") + queue2 = self.client.queue("consumer_tag") + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) + + self.assertEqual(True, msg3b.redelivered) + self.assertEqual(True, msg5b.redelivered) + + self.assertEqual("Six", queue2.get(timeout=1).body) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True) + subscription = channel.message_consume(queue="test-prefetch-count", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + #set prefetch to 5: + channel.message_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + #todo: once batching is implmented, send a single response for all messages + msg.ok(batchoffset=-4)#1-5 + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + msg.ok(batchoffset=-4)#6-10 + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True) + subscription = channel.message_consume(queue="test-prefetch-size", destination="consumer_tag", no_ack=False) + queue = self.client.queue("consumer_tag") + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.message_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + msg.ok(batchoffset=-4)#1-5 + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + msg.ok(batchoffset=-4)#6-10 + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.message_transfer(routing_key="test-prefetch-size", body=large) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.body) + + def test_get(self): + """ + Test message_get method + """ + channel = self.channel + channel.queue_declare(queue="test-get", exclusive=True) + + #publish some messages (no_ack=True) + for i in range(1, 11): + channel.message_transfer(routing_key="test-get", body="Message %d" % i) + + #use message_get to read back the messages, and check that we get an empty at the end + for i in range(1, 11): + tag = "queue %d" % i + reply = channel.message_get(no_ack=True, queue="test-get", destination=tag) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "ok") + self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + #repeat for no_ack=False + for i in range(11, 21): + channel.message_transfer(routing_key="test-get", body="Message %d" % i) + + for i in range(11, 21): + tag = "queue %d" % i + reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "ok") + msg = self.client.queue(tag).get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + if (i==13): + msg.ok(batchoffset=-2)#11, 12 & 13 + if(i in [15, 17, 19]): + msg.ok() + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + #recover(requeue=True) + channel.message_recover(requeue=True) + + #get the unacked messages again (14, 16, 18, 20) + for i in [14, 16, 18, 20]: + tag = "queue %d" % i + reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "ok") + msg = self.client.queue(tag).get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() + #channel.message_ack(delivery_tag=reply.delivery_tag) + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + channel.message_recover(requeue=True) + + reply = channel.message_get(no_ack=True, queue="test-get") + self.assertEqual(reply.method.klass.name, "message") + self.assertEqual(reply.method.name, "empty") + + def test_reference_simple(self): + """ + Test basic ability to handle references + """ + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.message_append(reference=refId, bytes="abcd") + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) + channel.synchronous = True + + channel.message_append(reference=refId, bytes="efgh") + channel.message_append(reference=refId, bytes="ijkl") + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + self.assertDataEquals(channel, queue.get(timeout=1), "abcdefghijkl") + + + def test_reference_large(self): + """ + Test basic ability to handle references whose content exceeds max frame size + """ + channel = self.channel + self.queue_declare(queue="ref_queue") + + #generate a big data string (> max frame size of consumer): + data = "0123456789" + for i in range(0, 10): + data += data + #send it inline + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=data) + channel.synchronous = True + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + #create a new connection for consumer, with specific max frame size (< data) + other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0}) + ch2 = other.channel(1) + ch2.channel_open() + ch2.message_consume(queue="ref_queue", destination="c1") + queue = other.queue("c1") + + msg = queue.get(timeout=1) + self.assertTrue(isinstance(msg.body, ReferenceId)) + self.assertTrue(msg.reference) + self.assertEquals(data, msg.reference.get_complete()) + + def test_reference_completion(self): + """ + Test that reference transfer are not deemed complete until + closed (therefore are not acked or routed until that point) + """ + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.message_append(reference=refId, bytes="abcd") + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) + channel.synchronous = True + + try: + msg = queue.get(timeout=1) + self.fail("Got unexpected message on queue: " + msg) + except Empty: None + + self.assertTrue(not ack.is_complete()) + + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + self.assertDataEquals(channel, queue.get(timeout=1), "abcd") + + def test_reference_multi_transfer(self): + """ + Test that multiple transfer requests for the same reference are + correctly handled. + """ + channel = self.channel + #declare and consume from two queues + channel.queue_declare(queue="q-one", exclusive=True) + channel.queue_declare(queue="q-two", exclusive=True) + channel.message_consume(queue="q-one", destination="q-one") + channel.message_consume(queue="q-two", destination="q-two") + queue1 = self.client.queue("q-one") + queue2 = self.client.queue("q-two") + + #transfer a single ref to both queues (in separate commands) + channel.message_open(reference="my-ref") + channel.synchronous = False + ack1 = channel.message_transfer(routing_key="q-one", body=ReferenceId("my-ref")) + channel.message_append(reference="my-ref", bytes="my data") + ack2 = channel.message_transfer(routing_key="q-two", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + #check that both queues have the message + self.assertDataEquals(channel, queue1.get(timeout=1), "my data") + self.assertDataEquals(channel, queue2.get(timeout=1), "my data") + self.assertEmpty(queue1) + self.assertEmpty(queue2) + + #transfer a single ref to the same queue twice (in separate commands) + channel.message_open(reference="my-ref") + channel.synchronous = False + ack1 = channel.message_transfer(routing_key="q-one", message_id="abc", body=ReferenceId("my-ref")) + channel.message_append(reference="my-ref", bytes="second message") + ack2 = channel.message_transfer(routing_key="q-one", message_id="xyz", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + msg1 = queue1.get(timeout=1) + msg2 = queue1.get(timeout=1) + #order is undefined + if msg1.message_id == "abc": + self.assertEquals(msg2.message_id, "xyz") + else: + self.assertEquals(msg1.message_id, "xyz") + self.assertEquals(msg2.message_id, "abc") + + #would be legal for the incoming messages to be transfered + #inline or by reference in any combination + + if isinstance(msg1.body, ReferenceId): + self.assertEquals("second message", msg1.reference.get_complete()) + if isinstance(msg2.body, ReferenceId): + if msg1.body != msg2.body: + self.assertEquals("second message", msg2.reference.get_complete()) + #else ok, as same ref as msg1 + else: + self.assertEquals("second message", msg1.body) + if isinstance(msg2.body, ReferenceId): + self.assertEquals("second message", msg2.reference.get_complete()) + else: + self.assertEquals("second message", msg2.body) + + self.assertEmpty(queue1) + + def test_reference_unopened_on_append_error(self): + channel = self.channel + try: + channel.message_append(reference="unopened") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_unopened_on_close_error(self): + channel = self.channel + try: + channel.message_close(reference="unopened") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_unopened_on_transfer_error(self): + channel = self.channel + try: + channel.message_transfer(body=ReferenceId("unopened")) + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_reference_already_opened_error(self): + channel = self.channel + channel.message_open(reference="a") + try: + channel.message_open(reference="a") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_empty_reference(self): + channel = self.channel + channel.queue_declare(queue="ref_queue", exclusive=True) + channel.message_consume(queue="ref_queue", destination="c1") + queue = self.client.queue("c1") + + refId = "myref" + channel.message_open(reference=refId) + channel.synchronous = False + ack = channel.message_transfer(routing_key="ref_queue", message_id="empty-msg", body=ReferenceId(refId)) + channel.synchronous = True + channel.message_close(reference=refId) + + #first, wait for the ok for the transfer + ack.get_response(timeout=1) + + msg = queue.get(timeout=1) + self.assertEquals(msg.message_id, "empty-msg") + self.assertDataEquals(channel, msg, "") + + def test_reject(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + + channel.message_consume(queue = "q", destination = "consumer") + channel.message_transfer(routing_key = "q", body="blah, blah") + msg = self.client.queue("consumer").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + channel.message_cancel(destination = "consumer") + msg.reject() + + channel.message_consume(queue = "q", destination = "checker") + msg = self.client.queue("checker").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + + def test_checkpoint(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + + channel.message_open(reference="my-ref") + channel.message_append(reference="my-ref", bytes="abcdefgh") + channel.message_append(reference="my-ref", bytes="ijklmnop") + channel.message_checkpoint(reference="my-ref", identifier="my-checkpoint") + channel.channel_close() + + channel = self.client.channel(2) + channel.channel_open() + channel.message_consume(queue = "q", destination = "consumer") + offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value + self.assertTrue(offset<=16) + channel.message_append(reference="my-ref", bytes="qrstuvwxyz") + channel.synchronous = False + channel.message_transfer(routing_key="q-one", message_id="abcd", body=ReferenceId("my-ref")) + channel.synchronous = True + channel.message_close(reference="my-ref") + + self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz") + self.assertEmpty(self.client.queue("consumer")) + + + def assertDataEquals(self, channel, msg, expected): + if isinstance(msg.body, ReferenceId): + data = msg.reference.get_complete() + else: + data = msg.body + self.assertEquals(expected, data) diff --git a/qpid/python/tests_0-10/query.py b/qpid/python/tests_0-10/query.py new file mode 100644 index 0000000000..c2e08c003c --- /dev/null +++ b/qpid/python/tests_0-10/query.py @@ -0,0 +1,224 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueryTests(TestBase): + """Tests for various query methods introduced in 0-10 and available in 0-9 for preview""" + + def test_exchange_query(self): + """ + Test that the exchange_query method works as expected + """ + channel = self.channel + #check returned type for the standard exchanges + self.assertEqual("direct", channel.exchange_query(name="amq.direct").type) + self.assertEqual("topic", channel.exchange_query(name="amq.topic").type) + self.assertEqual("fanout", channel.exchange_query(name="amq.fanout").type) + self.assertEqual("headers", channel.exchange_query(name="amq.match").type) + self.assertEqual("direct", channel.exchange_query(name="").type) + #declare an exchange + channel.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) + #check that the result of a query is as expected + response = channel.exchange_query(name="my-test-exchange") + self.assertEqual("direct", response.type) + self.assertEqual(False, response.durable) + self.assertEqual(False, response.not_found) + #delete the exchange + channel.exchange_delete(exchange="my-test-exchange") + #check that the query now reports not-found + self.assertEqual(True, channel.exchange_query(name="my-test-exchange").not_found) + + def test_binding_query_direct(self): + """ + Test that the binding_query method works as expected with the direct exchange + """ + self.binding_query_with_key("amq.direct") + + def test_binding_query_topic(self): + """ + Test that the binding_query method works as expected with the direct exchange + """ + self.binding_query_with_key("amq.topic") + + def binding_query_with_key(self, exchange_name): + channel = self.channel + #setup: create two queues + channel.queue_declare(queue="used-queue", exclusive=True) + channel.queue_declare(queue="unused-queue", exclusive=True) + + channel.queue_bind(exchange=exchange_name, queue="used-queue", routing_key="used-key") + + # test detection of any binding to specific queue + response = channel.binding_query(exchange=exchange_name, queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test detection of specific binding to any queue + response = channel.binding_query(exchange=exchange_name, routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.key_not_matched) + + # test detection of specific binding to specific queue + response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(False, response.key_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + # test unspecified queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.key_not_matched) + + # test matched queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(True, response.key_not_matched) + + # test unmatched queue, matched binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(False, response.key_not_matched) + + # test unmatched queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(True, response.key_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange=exchange_name, queue="unknown-queue").queue_not_found) + + + def test_binding_query_fanout(self): + """ + Test that the binding_query method works as expected with fanout exchange + """ + channel = self.channel + #setup + channel.queue_declare(queue="used-queue", exclusive=True) + channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_bind(exchange="amq.fanout", queue="used-queue") + + # test detection of any binding to specific queue + response = channel.binding_query(exchange="amq.fanout", queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange="amq.fanout", queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange="amq.fanout", queue="unknown-queue").queue_not_found) + + def test_binding_query_header(self): + """ + Test that the binding_query method works as expected with headers exchanges + """ + channel = self.channel + #setup + channel.queue_declare(queue="used-queue", exclusive=True) + channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) + + # test detection of any binding to specific queue + response = channel.binding_query(exchange="amq.match", queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test detection of specific binding to any queue + response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.args_not_matched) + + # test detection of specific binding to specific queue + response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(False, response.args_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + # test unspecified queue, unmatched binding + response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.args_not_matched) + + # test matched queue, unmatched binding + response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(True, response.args_not_matched) + + # test unmatched queue, matched binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(False, response.args_not_matched) + + # test unmatched queue, unmatched binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(True, response.args_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange="amq.match", queue="unknown-queue").queue_not_found) + diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py new file mode 100644 index 0000000000..e7fe0b3ed4 --- /dev/null +++ b/qpid/python/tests_0-10/queue.py @@ -0,0 +1,340 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueueTests(TestBase): + """Tests for 'methods' on the amqp queue 'class'""" + + def test_purge(self): + """ + Test that the purge method removes messages from the queue + """ + channel = self.channel + #setup, declare a queue and add some messages to it: + channel.exchange_declare(exchange="test-exchange", type="direct") + channel.queue_declare(queue="test-queue", exclusive=True) + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + channel.message_transfer(destination="test-exchange", routing_key="key", body="one") + channel.message_transfer(destination="test-exchange", routing_key="key", body="two") + channel.message_transfer(destination="test-exchange", routing_key="key", body="three") + + #check that the queue now reports 3 messages: + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(3, reply.message_count) + + #now do the purge, then test that three messages are purged and the count drops to 0 + reply = channel.queue_purge(queue="test-queue"); + self.assertEqual(3, reply.message_count) + reply = channel.queue_declare(queue="test-queue") + self.assertEqual(0, reply.message_count) + + #send a further message and consume it, ensuring that the other messages are really gone + channel.message_transfer(destination="test-exchange", routing_key="key", body="four") + channel.message_consume(queue="test-queue", destination="tag", no_ack=True) + queue = self.client.queue("tag") + msg = queue.get(timeout=1) + self.assertEqual("four", msg.body) + + #check error conditions (use new channels): + channel = self.client.channel(2) + channel.channel_open() + try: + #queue specified but doesn't exist: + channel.queue_purge(queue="invalid-queue") + self.fail("Expected failure when purging non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(3) + channel.channel_open() + try: + #queue not specified and none previously declared for channel: + channel.queue_purge() + self.fail("Expected failure when purging unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.channel_open() + channel.exchange_delete(exchange="test-exchange") + + def test_declare_exclusive(self): + """ + Test that the exclusive field is honoured in queue.declare + """ + # TestBase.setUp has already opened channel(1) + c1 = self.channel + # Here we open a second separate connection: + other = self.connect() + c2 = other.channel(1) + c2.channel_open() + + #declare an exclusive queue: + c1.queue_declare(queue="exclusive-queue", exclusive="True") + try: + #other connection should not be allowed to declare this: + c2.queue_declare(queue="exclusive-queue", exclusive="True") + self.fail("Expected second exclusive queue_declare to raise a channel exception") + except Closed, e: + self.assertChannelException(405, e.args[0]) + + + def test_declare_passive(self): + """ + Test that the passive field is honoured in queue.declare + """ + channel = self.channel + #declare an exclusive queue: + channel.queue_declare(queue="passive-queue-1", exclusive="True") + channel.queue_declare(queue="passive-queue-1", passive="True") + try: + #other connection should not be allowed to declare this: + channel.queue_declare(queue="passive-queue-2", passive="True") + self.fail("Expected passive declaration of non-existant queue to raise a channel exception") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_bind(self): + """ + Test various permutations of the queue.bind method + """ + channel = self.channel + channel.queue_declare(queue="queue-1", exclusive="True") + + #straightforward case, both exchange & queue exist so no errors expected: + channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + + #bind the default queue for the channel (i.e. last one declared): + channel.queue_bind(exchange="amq.direct", routing_key="key2") + + #use the queue name where neither routing key nor queue are specified: + channel.queue_bind(exchange="amq.direct") + + #try and bind to non-existant exchange + try: + channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + self.fail("Expected bind to non-existant exchange to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #need to reopen a channel: + channel = self.client.channel(2) + channel.channel_open() + + #try and bind non-existant queue: + try: + channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + self.fail("Expected bind of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_unbind_direct(self): + self.unbind_test(exchange="amq.direct", routing_key="key") + + def test_unbind_topic(self): + self.unbind_test(exchange="amq.topic", routing_key="key") + + def test_unbind_fanout(self): + self.unbind_test(exchange="amq.fanout") + + def test_unbind_headers(self): + self.unbind_test(exchange="amq.match", args={ "x-match":"all", "a":"b"}, headers={"a":"b"}) + + def unbind_test(self, exchange, routing_key="", args=None, headers={}): + #bind two queues and consume from them + channel = self.channel + + channel.queue_declare(queue="queue-1", exclusive="True") + channel.queue_declare(queue="queue-2", exclusive="True") + + channel.message_consume(queue="queue-1", destination="queue-1", no_ack=True) + channel.message_consume(queue="queue-2", destination="queue-2", no_ack=True) + + queue1 = self.client.queue("queue-1") + queue2 = self.client.queue("queue-2") + + channel.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) + + #send a message that will match both bindings + channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="one") + + #unbind first queue + channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + + #send another message + channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="two") + + #check one queue has both messages and the other has only one + self.assertEquals("one", queue1.get(timeout=1).body) + try: + msg = queue1.get(timeout=1) + self.fail("Got extra message: %s" % msg.body) + except Empty: pass + + self.assertEquals("one", queue2.get(timeout=1).body) + self.assertEquals("two", queue2.get(timeout=1).body) + try: + msg = queue2.get(timeout=1) + self.fail("Got extra message: " + msg) + except Empty: pass + + + def test_delete_simple(self): + """ + Test core queue deletion behaviour + """ + channel = self.channel + + #straight-forward case: + channel.queue_declare(queue="delete-me") + channel.message_transfer(routing_key="delete-me", body="a") + channel.message_transfer(routing_key="delete-me", body="b") + channel.message_transfer(routing_key="delete-me", body="c") + reply = channel.queue_delete(queue="delete-me") + self.assertEqual(3, reply.message_count) + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="delete-me", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #check attempted deletion of non-existant queue is handled correctly: + channel = self.client.channel(2) + channel.channel_open() + try: + channel.queue_delete(queue="i-dont-exist", if_empty="True") + self.fail("Expected delete of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + + def test_delete_ifempty(self): + """ + Test that if_empty field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and add a message to it (use default binding): + channel.queue_declare(queue="delete-me-2") + channel.queue_declare(queue="delete-me-2", passive="True") + channel.message_transfer(routing_key="delete-me-2", body="message") + + #try to delete, but only if empty: + try: + channel.queue_delete(queue="delete-me-2", if_empty="True") + self.fail("Expected delete if_empty to fail for non-empty queue") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + #need new channel now: + channel = self.client.channel(2) + channel.channel_open() + + #empty queue: + channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True) + queue = self.client.queue("consumer_tag") + msg = queue.get(timeout=1) + self.assertEqual("message", msg.body) + channel.message_cancel(destination="consumer_tag") + + #retry deletion on empty queue: + channel.queue_delete(queue="delete-me-2", if_empty="True") + + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-2", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_delete_ifunused(self): + """ + Test that if_unused field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and register a consumer: + channel.queue_declare(queue="delete-me-3") + channel.queue_declare(queue="delete-me-3", passive="True") + channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True) + + #need new channel now: + channel2 = self.client.channel(2) + channel2.channel_open() + #try to delete, but only if empty: + try: + channel2.queue_delete(queue="delete-me-3", if_unused="True") + self.fail("Expected delete if_unused to fail for queue with existing consumer") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + + channel.message_cancel(destination="consumer_tag") + channel.queue_delete(queue="delete-me-3", if_unused="True") + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-3", passive="True") + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_autodelete_shared(self): + """ + Test auto-deletion (of non-exclusive queues) + """ + channel = self.channel + other = self.connect() + channel2 = other.channel(1) + channel2.channel_open() + + channel.queue_declare(queue="auto-delete-me", auto_delete=True) + + #consume from both channels + reply = channel.basic_consume(queue="auto-delete-me", no_ack=True) + channel2.basic_consume(queue="auto-delete-me", no_ack=True) + + #implicit cancel + channel2.channel_close() + + #check it is still there + channel.queue_declare(queue="auto-delete-me", passive=True) + + #explicit cancel => queue is now unused again: + channel.basic_cancel(consumer_tag=reply.consumer_tag) + + #NOTE: this assumes there is no timeout in use + + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="auto-delete-me", passive=True) + self.fail("Expected queue to have been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + diff --git a/qpid/python/tests_0-10/testlib.py b/qpid/python/tests_0-10/testlib.py new file mode 100644 index 0000000000..f345fbbd80 --- /dev/null +++ b/qpid/python/tests_0-10/testlib.py @@ -0,0 +1,66 @@ +# +# 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. +# + +# +# Tests for the testlib itself. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from Queue import Empty + +import sys +from traceback import * + +def mytrace(frame, event, arg): + print_stack(frame); + print "====" + return mytrace + +class TestBaseTest(TestBase): + """Verify TestBase functions work as expected""" + + def testAssertEmptyPass(self): + """Test assert empty works""" + self.queue_declare(queue="empty") + q = self.consume("empty") + self.assertEmpty(q) + try: + q.get(timeout=1) + self.fail("Queue is not empty.") + except Empty: None # Ignore + + def testAssertEmptyFail(self): + self.queue_declare(queue="full") + q = self.consume("full") + self.channel.message_transfer(routing_key="full", body="") + try: + self.assertEmpty(q); + self.fail("assertEmpty did not assert on non-empty queue") + except AssertionError: None # Ignore + + def testMessageProperties(self): + """Verify properties are passed with message""" + props={"x":1, "y":2} + self.queue_declare(queue="q") + q = self.consume("q") + self.assertPublishGet(q, routing_key="q", properties=props) + + + diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py new file mode 100644 index 0000000000..0f6b4f5cd1 --- /dev/null +++ b/qpid/python/tests_0-10/tx.py @@ -0,0 +1,188 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class TxTests(TestBase): + """ + Tests for 'methods' on the amqp tx 'class' + """ + + def test_commit(self): + """ + Test that commited publishes are delivered and commited acks are not re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c") + channel.tx_commit() + + #check results + for i in range(1, 5): + msg = queue_c.get(timeout=1) + self.assertEqual("TxMessage %d" % i, msg.body) + msg.ok() + + msg = queue_b.get(timeout=1) + self.assertEqual("TxMessage 6", msg.body) + msg.ok() + + msg = queue_a.get(timeout=1) + self.assertEqual("TxMessage 7", msg.body) + msg.ok() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def test_auto_rollback(self): + """ + Test that a channel closed with an open transaction is effectively rolled back + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.body) + msg.ok() + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.body) + msg.ok() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def test_rollback(self): + """ + Test that rolled back publishes are not delivered and rolled back acks are re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + channel.tx_rollback() + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + msg.ok() + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.body) + msg.ok() + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.body) + msg.ok() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def perform_txn_work(self, channel, name_a, name_b, name_c): + """ + Utility method that does some setup and some work under a transaction. Used for testing both + commit and rollback + """ + #setup: + channel.queue_declare(queue=name_a, exclusive=True) + channel.queue_declare(queue=name_b, exclusive=True) + channel.queue_declare(queue=name_c, exclusive=True) + + key = "my_key_" + name_b + topic = "my_topic_" + name_c + + channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) + channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) + + for i in range(1, 5): + channel.message_transfer(routing_key=name_a, body="Message %d" % i) + + channel.message_transfer(routing_key=key, destination="amq.direct", body="Message 6") + channel.message_transfer(routing_key=topic, destination="amq.topic", body="Message 7") + + channel.tx_select() + + #consume and ack messages + channel.message_consume(queue=name_a, destination="sub_a", no_ack=False) + queue_a = self.client.queue("sub_a") + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.body) + + msg.ok(batchoffset=-3) + + channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) + queue_b = self.client.queue("sub_b") + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.body) + msg.ok() + + sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False) + queue_c = self.client.queue("sub_c") + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.body) + msg.ok() + + #publish messages + for i in range(1, 5): + channel.message_transfer(routing_key=topic, destination="amq.topic", body="TxMessage %d" % i) + + channel.message_transfer(routing_key=key, destination="amq.direct", body="TxMessage 6") + channel.message_transfer(routing_key=name_a, body="TxMessage 7") + + return queue_a, queue_b, queue_c diff --git a/qpid/python/tests_0-9/broker.py b/qpid/python/tests_0-9/broker.py index 30eda03d48..03b4132d3e 100644 --- a/qpid/python/tests_0-9/broker.py +++ b/qpid/python/tests_0-9/broker.py @@ -27,7 +27,7 @@ class BrokerTests(TestBase): def test_ack_and_no_ack(self): """ First, this test tries to receive a message with a no-ack - consumer. Second, this test tries to explicitely receive and + consumer. Second, this test tries to explicitly receive and acknowledge a message with an acknowledging consumer. """ ch = self.channel -- cgit v1.2.1 From 9b3a523d044be6ae5e88a96a4513b05a48d4feb6 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 24 Jul 2007 20:05:52 +0000 Subject: Removed unused regex. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@559184 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 02b924b6b8..a5228e8003 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -248,8 +248,6 @@ class Codec: """ return self.dec_str("!L") - KEY_CHECK = re.compile(r"[\$#A-Za-z][\$#A-Za-z0-9_]*") - def encode_table(self, tbl): """ encodes a table data structure in network byte order @@ -260,7 +258,6 @@ class Codec: for key, value in tbl.items(): if len(key) > 128: raise ValueError("field table key too long: '%s'" % key) - codec.encode_shortstr(key) if isinstance(value, basestring): codec.write("S") -- cgit v1.2.1 From b1a136bc3613c685c278ef9940a562ee75388fc3 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 24 Jul 2007 22:16:45 +0000 Subject: Made attributes queryable. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@559241 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/dom.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py index 9b1740055b..80f1f2a409 100644 --- a/qpid/python/mllib/dom.py +++ b/qpid/python/mllib/dom.py @@ -145,14 +145,6 @@ class Tag(Node): if name == k: return v - def __getitem__(self, name): - if name and name[0] == "@": - return self.get_attr(name[1:]) - else: - for nd in self.query[name]: - return nd - return self.get_attr(name) - def dispatch(self, f): try: method = getattr(f, "do_" + self.name) @@ -203,6 +195,9 @@ class Filter(View): elif predicate[0] == "#": type = predicate[1:] self.predicate = lambda x: x.is_type(type) + elif predicate[0] == "@": + name = predicate[1:] + self.predicate = lambda x: x[0] == name else: self.predicate = lambda x: isinstance(x, Tag) and x.name == predicate @@ -231,6 +226,19 @@ class Children(View): for child in nd.children: yield child +class Attributes(View): + + def __iter__(self): + for nd in self.source: + for a in nd.attrs: + yield a + +class Values(View): + + def __iter__(self): + for name, value in self.source: + yield value + class Query(View): def __iter__(self): @@ -245,6 +253,9 @@ class Query(View): query = self.source for p in path: - query = Query(Filter(p, Flatten(Children(query)))) + if isinstance(p, basestring) and p[0] == "@": + query = Values(Filter(p, Attributes(query))) + else: + query = Query(Filter(p, Flatten(Children(query)))) return query -- cgit v1.2.1 From ecc0eba144f108b318830eb75c21d1330f6ff19d Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 25 Jul 2007 03:23:07 +0000 Subject: made query[...] support both navigation and filtering git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@559299 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/dom.py | 53 ++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 20 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py index 80f1f2a409..5a5d017ff9 100644 --- a/qpid/python/mllib/dom.py +++ b/qpid/python/mllib/dom.py @@ -190,16 +190,7 @@ class Filter(View): def __init__(self, predicate, source): View.__init__(self, source) - if callable(predicate): - self.predicate = predicate - elif predicate[0] == "#": - type = predicate[1:] - self.predicate = lambda x: x.is_type(type) - elif predicate[0] == "@": - name = predicate[1:] - self.predicate = lambda x: x[0] == name - else: - self.predicate = lambda x: isinstance(x, Tag) and x.name == predicate + self.predicate = predicate def __iter__(self): for nd in self.source: @@ -239,23 +230,45 @@ class Values(View): for name, value in self.source: yield value +def flatten_path(path): + if isinstance(path, basestring): + for part in path.split("/"): + yield part + elif callable(path): + yield path + else: + for p in path: + for fp in flatten_path(p): + yield fp + class Query(View): def __iter__(self): for nd in self.source: yield nd - def __getitem__(self, predicate): - if isinstance(predicate, basestring): - path = predicate.split("/") - else: - path = [predicate] - + def __getitem__(self, path): query = self.source - for p in path: - if isinstance(p, basestring) and p[0] == "@": - query = Values(Filter(p, Attributes(query))) + for p in flatten_path(path): + if callable(p): + select = Query + pred = p + source = query + elif isinstance(p, basestring): + if p[0] == "@": + select = Values + pred = lambda x, n=p[1:]: x[0] == n + source = Attributes(query) + elif p[0] == "#": + select = Query + pred = lambda x, t=p[1:]: x.is_type(t) + source = Children(query) + else: + select = Query + pred = lambda x, n=p: isinstance(x, Tag) and x.name == n + source = Flatten(Children(query)) else: - query = Query(Filter(p, Flatten(Children(query)))) + raise ValueError(p) + query = select(Filter(pred, source)) return query -- cgit v1.2.1 From 259f9ace78988e069a21476a3a464056e8fa7a87 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 26 Jul 2007 16:08:40 +0000 Subject: Changed use of Event to directly use Condition to workaround problems on other platforms (where Event.wait() returned immediately after a set()/clear()). git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@559869 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index d7ae85e345..3927f20667 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -351,8 +351,8 @@ class Future: class ExecutionCompletion: def __init__(self): - self.completed = threading.Event() - self.sequence = Sequence(0) + self.condition = threading.Condition() + self.sequence = Sequence(1) self.command_id = 0 self.mark = 0 @@ -362,19 +362,27 @@ class ExecutionCompletion: self.command_id = self.sequence.next() def close(self): - self.completed.set() + self.condition.acquire() + try: + self.condition.notifyAll() + finally: + self.condition.release() def complete(self, mark): - self.mark = mark - self.completed.set() - self.completed.clear() + self.condition.acquire() + try: + self.mark = mark + self.condition.notifyAll() + finally: + self.condition.release() def wait(self, point_of_interest=-1, timeout=None): - """ - todo: really want to allow different threads to call this with - different points of interest on the same channel, this is a quick - hack for now - """ if point_of_interest == -1: point_of_interest = self.command_id - self.completed.wait(timeout) + self.condition.acquire() + try: + if point_of_interest > self.mark: + self.condition.wait(timeout) + finally: + self.condition.release() + #todo: retry until timed out or closed return point_of_interest <= self.mark -- cgit v1.2.1 From a2f1ddbe4175c6136b1188faeccaf1f8e561e3b2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 27 Jul 2007 15:44:52 +0000 Subject: Use execution layer to acknowledge messages. Turn off 0-9 framing of requests and responses. Some refactoring around message delivery. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@560285 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 13 ++++++- qpid/python/qpid/codec.py | 25 +++++++----- qpid/python/qpid/message.py | 8 +++- qpid/python/qpid/peer.py | 82 ++++++++++++++++++++++++++++++++++----- qpid/python/qpid/spec.py | 2 +- qpid/python/tests_0-10/broker.py | 2 +- qpid/python/tests_0-10/dtx.py | 26 +++++++++++-- qpid/python/tests_0-10/example.py | 2 +- qpid/python/tests_0-10/message.py | 24 ++++++------ qpid/python/tests_0-10/tx.py | 56 ++++++++++++++++---------- 10 files changed, 178 insertions(+), 62 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 798d1769eb..e68f942d67 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,4 +1,15 @@ -tests_0-10.message.MessageTests.test_checkpoint tests_0-10.message.MessageTests.test_reject tests_0-10.basic.BasicTests.test_get +tests_0-10.message.MessageTests.test_get +tests_0-10.message.MessageTests.test_checkpoint +tests_0-10.message.MessageTests.test_empty_reference +tests_0-10.message.MessageTests.test_reference_already_opened_error +tests_0-10.message.MessageTests.test_reference_completion +tests_0-10.message.MessageTests.test_reference_large +tests_0-10.message.MessageTests.test_reference_multi_transfer +tests_0-10.message.MessageTests.test_reference_simple +tests_0-10.message.MessageTests.test_reference_unopened_on_append_error +tests_0-10.message.MessageTests.test_reference_unopened_on_close_error +tests_0-10.message.MessageTests.test_reference_unopened_on_transfer_error + diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index a5228e8003..a0d9696c8b 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -329,12 +329,6 @@ class Codec: return ReferenceId(self.decode_longstr()) # new domains for 0-10: - - def encode_uuid(self, s): - self.encode_longstr(s) - - def decode_uuid(self): - return self.decode_longstr() def encode_rfc1982_long(self, s): self.encode_long(s) @@ -342,10 +336,21 @@ class Codec: def decode_rfc1982_long(self): return self.decode_long() - #Not done yet def encode_rfc1982_long_set(self, s): - self.encode_short(0) + self.encode_short(len(s)) + for i in s: + self.encode_long(i) def decode_rfc1982_long_set(self): - self.decode_short() - return 0; + count = self.decode_short() + set = [] + for i in range(0, count): + set.append(self.decode_long()) + return set; + + #not correct for 0-10 yet + def encode_uuid(self, s): + self.encode_longstr(s) + + def decode_uuid(self): + return self.decode_longstr() diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index f80293180e..970ab9d974 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -26,7 +26,10 @@ class Message: self.frame = frame self.method = frame.method_type self.content = content - + if self.method.klass.name != "execution": + self.command_id = self.channel.incoming_completion.sequence.next() + #print "allocated: ", self.command_id, "to ", self.method.klass.name, "_", self.method.name + def __len__(self): return len(self.frame.args) @@ -66,3 +69,6 @@ class Message: def __repr__(self): return Message.REPR % (self.method, self.frame.args, self.content) + + def complete(self, cumulative=True): + self.channel.incoming_completion.complete(mark=self.command_id, cumulative=cumulative) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 3927f20667..bedc96895b 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -30,6 +30,7 @@ from message import Message from queue import Queue, Closed as QueueClosed from content import Content from cStringIO import StringIO +from time import time class Sequence: @@ -186,11 +187,11 @@ class Channel: self.requester = Requester(self.write) self.responder = Responder(self.write) - self.completion = ExecutionCompletion() + self.completion = OutgoingCompletion() + self.incoming_completion = IncomingCompletion(self) # Use reliable framing if version == 0-9. - # (also for 0-10 while transitioning...) - self.reliable = (spec.major == 0 and (spec.minor == 9 or spec.minor == 10)) + self.reliable = (spec.major == 0 and spec.minor == 9) self.use_execution_layer = (spec.major == 0 and spec.minor == 10) self.synchronous = True @@ -202,6 +203,7 @@ class Channel: self.incoming.close() self.responses.close() self.completion.close() + self.incoming_completion.reset() def write(self, frame, content = None): if self.closed: @@ -252,6 +254,9 @@ class Channel: self.responder.respond(method, batch, request) def invoke(self, type, args, kwargs): + if type.klass.name == "channel" and (type.name == "close" or type.name == "open"): + self.completion.reset() + self.incoming_completion.reset() self.completion.next_command(type) content = kwargs.pop("content", None) frame = Method(type, type.arguments(*args, **kwargs)) @@ -306,6 +311,13 @@ class Channel: return Message(self, resp, content) else: raise ValueError(resp) + elif self.synchronous and not frame.method.response \ + and self.use_execution_layer and frame.method.klass.name != "execution": + self.execution_flush() + self.completion.wait() + if self.closed: + raise Closed(self.reason) + except QueueClosed, e: if self.closed: raise Closed(self.reason) @@ -349,21 +361,32 @@ class Future: def is_complete(self): return self.completed.isSet() -class ExecutionCompletion: +class OutgoingCompletion: + """ + Manages completion of outgoing commands i.e. command sent by this peer + """ + def __init__(self): self.condition = threading.Condition() - self.sequence = Sequence(1) - self.command_id = 0 - self.mark = 0 + + self.sequence = Sequence(1) #issues ids for outgoing commands + self.command_id = 0 #last issued id + self.mark = 0 #commands up to this mark are known to be complete + self.closed = False def next_command(self, method): #the following test is a hack until the track/sub-channel is available if method.klass.name != "execution": self.command_id = self.sequence.next() + def reset(self): + self.sequence = Sequence(1) #reset counter + def close(self): + self.reset() self.condition.acquire() try: + self.closed = True self.condition.notifyAll() finally: self.condition.release() @@ -378,11 +401,50 @@ class ExecutionCompletion: def wait(self, point_of_interest=-1, timeout=None): if point_of_interest == -1: point_of_interest = self.command_id + start_time = time() + remaining = timeout self.condition.acquire() try: - if point_of_interest > self.mark: - self.condition.wait(timeout) + while not self.closed and point_of_interest > self.mark: + #print "waiting for ", point_of_interest, " mark is currently at ", self.mark + self.condition.wait(remaining) + if timeout: + if start_time + timeout > time(): break + else: remaining = timeout - (time() - start_time) finally: self.condition.release() - #todo: retry until timed out or closed return point_of_interest <= self.mark + +class IncomingCompletion: + """ + Manages completion of incoming commands i.e. command received by this peer + """ + + def __init__(self, channel): + self.sequence = Sequence(1) #issues ids for incoming commands + self.mark = 0 #id of last command of whose completion notification was sent to the other peer + self.channel = channel + + def next_id(self, method): + #the following test is a hack until the track/sub-channel is available + if method.klass.name != "execution": + return self.sequence.next() + else: + return 0 + + def reset(self): + self.sequence = Sequence(1) #reset counter + + def complete(self, mark, cumulative=True): + if cumulative: + if mark > self.mark: + self.mark = mark + self.channel.execution_complete(cumulative_execution_mark=self.mark) + else: + #TODO: record and manage the ranges properly + range = [mark, mark] + self.channel.execution_complete(cumulative_execution_mark=self.mark, ranged_execution_set=range) + + + + diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index c537401164..09e7dc9d0b 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -240,7 +240,7 @@ class Method(Metadata): "content": None, "uuid": "", "rfc1982_long": 0, - "rfc1982_long_set": 0 + "rfc1982_long_set": [] } def define_method(self, name): diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 684b36597e..6bc2f7ceb8 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -48,7 +48,7 @@ class BrokerTests(TestBase): body = "test ack" ch.message_transfer(routing_key = "otherqueue", body = body) msg = self.client.queue(ctag).get(timeout = 5) - msg.ok() + msg.complete() self.assert_(msg.body == body) def test_simple_delivery_immediate(self): diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index c0d1bd2b74..2835d703ae 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -40,6 +40,11 @@ class DtxTests(TestBase): XA_RBROLLBACK = 1 XA_RBTIMEOUT = 2 XA_OK = 8 + tx_counter = 0 + + def reset_channel(self): + self.channel.channel_close() + self.channel.channel_open() def test_simple_commit(self): """ @@ -56,6 +61,9 @@ class DtxTests(TestBase): #commit self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).status) + #should close and reopen channel to ensure no unacked messages are held + self.reset_channel() + #check result self.assertMessageCount(0, "queue-a") self.assertMessageCount(1, "queue-b") @@ -79,6 +87,8 @@ class DtxTests(TestBase): #commit self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).status) + self.reset_channel() + #check result self.assertMessageCount(0, "queue-a") self.assertMessageCount(1, "queue-b") @@ -100,6 +110,8 @@ class DtxTests(TestBase): #rollback self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + self.reset_channel() + #check result self.assertMessageCount(1, "queue-a") self.assertMessageCount(0, "queue-b") @@ -123,6 +135,8 @@ class DtxTests(TestBase): #rollback self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + self.reset_channel() + #check result self.assertMessageCount(1, "queue-a") self.assertMessageCount(0, "queue-b") @@ -191,6 +205,8 @@ class DtxTests(TestBase): channel = self.channel #do some transactional work & complete the transaction self.test_simple_commit() + # channel has been reset, so reselect for use with dtx + channel.dtx_demarcation_select() #start association for the same xid as the previously completed txn tx = self.xid("my-xid") @@ -355,7 +371,7 @@ class DtxTests(TestBase): self.assertEqual("two", msg.message_id) channel.message_cancel(destination="results") #ack the message then close the channel - msg.ok() + msg.complete() channel.channel_close() channel = self.channel @@ -446,7 +462,7 @@ class DtxTests(TestBase): channel2.dtx_demarcation_select() channel2.dtx_demarcation_start(xid=tx) channel2.message_get(queue="dummy", destination="dummy") - self.client.queue("dummy").get(timeout=1).ok() + self.client.queue("dummy").get(timeout=1).complete() channel2.message_transfer(routing_key="dummy", body="whatever") channel2.channel_close() @@ -548,7 +564,9 @@ class DtxTests(TestBase): channel.dtx_coordination_rollback(xid=x) self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) - def xid(self, txid, branchqual = ''): + def xid(self, txid): + DtxTests.tx_counter += 1 + branchqual = "v%s" % DtxTests.tx_counter return pack('LBB', 0, len(txid), len(branchqual)) + txid + branchqual def txswap(self, tx, id): @@ -573,7 +591,7 @@ class DtxTests(TestBase): #consume from src: channel.message_get(destination="temp-swap", queue=src) msg = self.client.queue("temp-swap").get(timeout=1) - msg.ok(); + msg.complete(); #re-publish to dest channel.message_transfer(routing_key=dest, message_id=msg.message_id, body=msg.body) diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index 7ab4cc7d0a..dc71b0590b 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -90,5 +90,5 @@ class ExampleTest (TestBase): self.assertEqual(body, msg.body) # Now acknowledge the message. - msg.ok() + msg.complete() diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index b25016e680..74e2b6416f 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -171,8 +171,8 @@ class MessageTests(TestBase): self.assertEqual("Four", msg4.body) self.assertEqual("Five", msg5.body) - msg1.ok(batchoffset=1)#One and Two - msg4.ok() + msg2.complete(cumulative=True)#One and Two + msg4.complete(cumulative=False) channel.message_recover(requeue=False) @@ -215,8 +215,8 @@ class MessageTests(TestBase): self.assertEqual("Four", msg4.body) self.assertEqual("Five", msg5.body) - msg1.ok(batchoffset=1) #One and Two - msg4.ok() #Four + msg2.complete(cumulative=True) #One and Two + msg4.complete(cumulative=False) #Four channel.message_cancel(destination="consumer_tag") @@ -276,14 +276,13 @@ class MessageTests(TestBase): except Empty: None #ack messages and check that the next set arrive ok: - #todo: once batching is implmented, send a single response for all messages - msg.ok(batchoffset=-4)#1-5 + msg.complete() for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok(batchoffset=-4)#6-10 + msg.complete() try: extra = queue.get(timeout=1) @@ -320,13 +319,13 @@ class MessageTests(TestBase): except Empty: None #ack messages and check that the next set arrive ok: - msg.ok(batchoffset=-4)#1-5 + msg.complete() for i in range(6, 11): msg = queue.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok(batchoffset=-4)#6-10 + msg.complete() try: extra = queue.get(timeout=1) @@ -376,9 +375,9 @@ class MessageTests(TestBase): self.assertEqual("Message %d" % i, msg.body) if (i==13): - msg.ok(batchoffset=-2)#11, 12 & 13 + msg.complete()#11, 12 & 13 if(i in [15, 17, 19]): - msg.ok() + msg.complete(cumulative=False) reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") @@ -395,8 +394,7 @@ class MessageTests(TestBase): self.assertEqual(reply.method.name, "ok") msg = self.client.queue(tag).get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok() - #channel.message_ack(delivery_tag=reply.delivery_tag) + msg.complete() reply = channel.message_get(no_ack=True, queue="test-get") self.assertEqual(reply.method.klass.name, "message") diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 0f6b4f5cd1..b499c2d1f9 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -30,23 +30,39 @@ class TxTests(TestBase): """ Test that commited publishes are delivered and commited acks are not re-delivered """ + channel2 = self.client.channel(2) + channel2.channel_open() + self.perform_txn_work(channel2, "tx-commit-a", "tx-commit-b", "tx-commit-c") + channel2.tx_commit() + channel2.channel_close() + + #use a different channel with new subscriptions to ensure + #there is no redelivery of acked messages: channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c") - channel.tx_commit() + channel.tx_select() + + channel.message_consume(queue="tx-commit-a", destination="qa", no_ack=False) + queue_a = self.client.queue("qa") + + channel.message_consume(queue="tx-commit-b", destination="qb", no_ack=False) + queue_b = self.client.queue("qb") + + channel.message_consume(queue="tx-commit-c", destination="qc", no_ack=False) + queue_c = self.client.queue("qc") #check results for i in range(1, 5): msg = queue_c.get(timeout=1) self.assertEqual("TxMessage %d" % i, msg.body) - msg.ok() + msg.complete() msg = queue_b.get(timeout=1) self.assertEqual("TxMessage 6", msg.body) - msg.ok() + msg.complete() msg = queue_a.get(timeout=1) self.assertEqual("TxMessage 7", msg.body) - msg.ok() + msg.complete() for q in [queue_a, queue_b, queue_c]: try: @@ -76,15 +92,15 @@ class TxTests(TestBase): for i in range(1, 5): msg = queue_a.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok() + msg.complete() msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) - msg.ok() + msg.complete() msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) - msg.ok() + msg.complete() for q in [queue_a, queue_b, queue_c]: try: @@ -114,15 +130,15 @@ class TxTests(TestBase): for i in range(1, 5): msg = queue_a.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok() + msg.complete() msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) - msg.ok() + msg.complete() msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) - msg.ok() + msg.complete() for q in [queue_a, queue_b, queue_c]: try: @@ -150,10 +166,10 @@ class TxTests(TestBase): channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) for i in range(1, 5): - channel.message_transfer(routing_key=name_a, body="Message %d" % i) + channel.message_transfer(routing_key=name_a, message_id="msg%d" % i, body="Message %d" % i) - channel.message_transfer(routing_key=key, destination="amq.direct", body="Message 6") - channel.message_transfer(routing_key=topic, destination="amq.topic", body="Message 7") + channel.message_transfer(routing_key=key, destination="amq.direct", message_id="msg6", body="Message 6") + channel.message_transfer(routing_key=topic, destination="amq.topic", message_id="msg7", body="Message 7") channel.tx_select() @@ -164,25 +180,25 @@ class TxTests(TestBase): msg = queue_a.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) - msg.ok(batchoffset=-3) + msg.complete() channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) queue_b = self.client.queue("sub_b") msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) - msg.ok() + msg.complete() sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False) queue_c = self.client.queue("sub_c") msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) - msg.ok() + msg.complete() #publish messages for i in range(1, 5): - channel.message_transfer(routing_key=topic, destination="amq.topic", body="TxMessage %d" % i) + channel.message_transfer(routing_key=topic, destination="amq.topic", message_id="tx-msg%d" % i, body="TxMessage %d" % i) - channel.message_transfer(routing_key=key, destination="amq.direct", body="TxMessage 6") - channel.message_transfer(routing_key=name_a, body="TxMessage 7") + channel.message_transfer(routing_key=key, destination="amq.direct", message_id="tx-msg6", body="TxMessage 6") + channel.message_transfer(routing_key=name_a, message_id="tx-msg7", body="TxMessage 7") return queue_a, queue_b, queue_c -- cgit v1.2.1 From 5d7add0f735d963bb69f0a57a37ff9c090baf909 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 27 Jul 2007 23:53:43 +0000 Subject: added an explicit upper bound check for longs git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@560425 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index a0d9696c8b..a11486376d 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -178,8 +178,10 @@ class Codec: encodes long (32 bits) data 'o' in network byte order """ - if (o < 0): - raise ValueError('unsinged long int cannot be less than 0') + # we need to check both bounds because on 64 bit platforms + # struct.pack won't raise an error if o is too large + if (o < 0 or o > 4294967295): + raise ValueError('Valid range of long int is [0,4294967295]') self.pack("!L", o) -- cgit v1.2.1 From 4fbd28b6078d6b3fbfe528a99d6e27963c68f99b Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Tue, 31 Jul 2007 15:53:37 +0000 Subject: Merged revisions 1-447993,447995-448007,448009-448141,448143-448157,448161-448194,448196-448210,448212-448218,448220-448223,448225-448233,448235,448237-448241,448243-448596,448598-448623,448625-448850,448852-448880,448882-448982,448984-449635,449637-449639,449641-449642,449644-449645,449647-449674,449676-449719,449721-449749,449751-449762,449764-449933,449935-449941,449943-450383,450385,450387-450400,450402-450433,450435-450503,450505-450555,450557-450860,450862-451024,451026-451149,451151-451316,451318-451931,451933-452139,452141-452162,452164-452320,452322,452324-452325,452327-452333,452335-452429,452431-452528,452530-452545,452547-453192,453194-453195,453197-453536,453538,453540-453656,453658-454676,454678-454735,454737,454739-454781,454783-462728,462730-462819,462821-462833,462835-462839,462841-463071,463073-463178,463180-463308,463310-463362,463364-463375,463377-463396,463398-463402,463404-463409,463411-463661,463663-463670,463672-463673,463675-464493,464495-464502,464504-464576,464578-464613,464615-464628,464630,464632-464866,464868-464899,464901-464942,464944-464949,464951-465004,465006-465016,465018-465053,465055-465165,465167-465321,465323-465406,465408-465427,465429-465431,465433-465548,465550-466044,466047-466075,466077,466079-466081,466083-466099,466101-466112,466114-466126,466128-466240,466242-466971,466973-466978,466980-467309,467311-467312,467316-467328,467330-467485,467487-467588,467590-467604,467606-467699,467701-467706,467708-467749,467751-468069,468071-468537,468539-469241,469244-469246,469248-469318,469320-469421,469423,469425-469429,469431-469435,469437-469462,469464-469469,469472-469477,469479-469490,469492-469503,469505-469529,469531-469598,469600-469624,469626-469737,469739-469752,469754-469806,469808-469928,469930-469953,469955-470011,470013-470109,470111-470335,470338-470339,470341-470379,470381,470383-470399,470401-470446,470448-470741,470743-470758,470760-470809,470811-470817,470819-470993,470995-471001,471003-471788,471790-471792,471794-472028,472030-472032,472034-472036,472038,472040,472043,472045-472059,472061,472063,472065-472066,472068,472070-472072,472074-472080,472082,472084-472092,472094-472107,472109-472123,472125-472158,472160-472165,472167-472172,472174-472457,472459-472460,472462-472464,472466-472470,472472-472483,472486-472491,472493-472494,472496-472497,472499,472501-472503,472505-472512,472514-472544,472546-472556,472558-472560,472562-472572,472574-472587,472589-472591,472593-472605,472607,472609-472731,472733-472786,472788-472843,472845-472849,472851-472859,472861-472878,472880-472903,472905,472907-472988,472990-472991,472993-473071,473073-473086,473088-473090,473093,473095-473096,473098-473106,473108-473110,473112-473185,473187-473260,473262,473268-473270,473275-473279,473281,473284-473287,473289-473295,473297-473306,473308-473330,473332-473335,473337,473339-473344,473346-473351,473353-473355,473357-473358,473361-473471,473473-473497,473499-473535,473537-473567,473569-473888,473890-474451,474454-474492,474494-474563,474565-474843,474845-474865,474867-474932,474934-475035,475037-475144,475146-475180,475182-475265,475267-475285,475287,475289-475293,475295-475296,475298-475302,475304-475631,475633-475649,475651-475748,475750-475752,475754-476107,476109-476302,476304-476413,476415-476430,476432-476700,476702-476868,476870-477147,477149-477213,477215-477263,477265-477340,477342-477635,477637-477789,477791-477825,477827-477841,477843,477846-477852,477854,477856,477858-477865,477867-477894,477896-478022,478024-478182,478184-478211,478213-478233,478235-478236,478238-478241,478243-478252,478254-478259,478261-478263,478265,478267-478269,478271-478286,478288-478342,478344-478379,478381-478412,478414-478443,478445-478636,478639-478658,478660-478821,478823-478853,478855-478922,478924-478962,478965-478974,478976-479029,479031-479049,479051-479210,479212-479214,479216-479407,479409-479415,479417-479425,479427-479559,479561-479639,479641-479676,479678-479685,479687-480030,480033-480086,480091-480093,480095-480118,480120-480139,480141,480143-480148,480150-480156,480158-480163,480165-480177,480179-480189,480191-480193,480195-480198,480200-480220,480222-480282,480284-480292,480294-480308,480310-480317,480320-480422,480424,480426-480581,480583-480656,480658-480692,480695-480702,480704,480706-480710,480712-480910,480913-480933,480935-480945,480947-480972,480974-480993,480995-481034,481036-481158,481161-481174,481176-481220,481222-481234,481236-481260,481263-481264,481266-481296,481298-481304,481306-481311,481313-481332,481334,481336-481380,481382-481441,481443-482144,482146-482180,482182-482193,482195-482232,482234-482236,482239,482241-482242,482244-482247,482250-482251,482253,482256-482261,482264-482288,482290-482364,482366,482368,482370-482554,482556,482558-482569,482572-482636,482638,482640-482696,482698-482722,482724-482732,482734-482771,482774-482957,482959-483045,483047-483105,483108,483110-483115,483117,483119-483127,483130-483134,483136-483148,483150-483158,483160-483164,483166-483178,483180-483391,483393-483400,483402-483403,483405-483418,483420-483421,483425-483436,483438-483470,483472-483502,483504-483558,483560-483599,483601-483637,483639-483644,483646-483659,483661-483670,483672-483878,483880-483910,483912-483915,483917-483940,483942,483944-483968,483970-483972,483974-483976,483978,483980-484612,484614-484657,484659-484693,484695-484718,484720-484842,484844-484847,484849-484986,484988-485019,485021-485489,485491-485544,485546-485591,485593,485595-485697,485699-485729,485731-485734,485736-485779,485781-485787,485789-485851,485853,485855-486007,486009,486011-486020,486022-486083,486085-486097,486099-486117,486120-486131,486133-486148,486150-486161,486163-486164,486166-486197,486199-486205,486208-486247,486249-486253,486256-486427,486429-486431,486433-486554,486556-486573,486575-486593,486595,486597-486609,486611-486619,486622,486625,486627-486641,486643-486645,486649-486687,486689-486721,486723-486730,486732-486746,486748-486759,486761,486763-486777,486779-486782,486784-486788,486790,486792,486794-486796,486798-487175,487178,487180-487213,487215,487217-487267,487269-487284,487286-487298,487300-487358,487360-487367,487369-487382,487384-487434,487436-487480,487482-487547,487549-487561,487563-487565,487567-487578,487580-487615,487617-487622,487624,487626,487628,487630-487635,487637-487703,487705-487777,487780-487781,487783-487800,487802-487803,487805-487820,487822-487848,487850-487902,487904-488103,488105-488133,488135-488158,488160-488163,488165-488187,488189-488216,488218-488248,488250-488278,488280,488282-488303,488305-488313,488315-488342,488344-488351,488353-488376,488378-488449,488451-488593,488595,488597-488623,488625-488700,488702-488704,488706-488710,488714,488716-488725,488727-488744,488746-488770,488772-488798,488800,488802-488807,488809,488811-488829,488831-488843,488845-488851,488853-489069,489071-489077,489079-489081,489084-489102,489104-489105,489107-489109,489111-489112,489114-489139,489141-489178,489181-489203,489205-489211,489213,489216-489329,489332-489402,489404-489417,489419-489421,489423-489643,489645-489690,489692-489703,489705-489714,489716-489747,489749-489753,489755-489803,489805-489904,489906-490372,490374-490504,490506-490604,490606-490707,490710-490733,490735-490871,490873-490984,490986-491028,491030,491032-491071,491073-491119,491121-491576,491578-491672,491674-491800,491802-491838,491840-491878,491880-492183,492185-492279,492281-492317,492319-492513,492515-492584,492586-492587,492589-492601,492603-492635,492637-492640,492642-492717,492719-492723,492725-492729,492731-492755,492757-492901,492903-492955,492957-492962,492964-492997,492999-493002,493004-493041,493043-493059,493062-493063,493065-493086,493088-493125,493127-493139,493141-493150,493152-493871,493873-494017,494019-494030,494032-494041,494043-494091,494093-494120,494122-494354,494356-494436,494438-494539,494541-494552,494554-494586,494588-494649,494651,494653-494654,494656-494657,494659-494764,494766-494768,494770-494796,494798-494799,494802,494804-494860,494862-494903,494905-494906,494908-495019,495021-495160,495162-495168,495171-495188,495190-495229,495231-495254,495256-495303,495305-495313,495315-495336,495338-495372,495374-495379,495381-495454,495457-495459,495462-495516,495518-495524,495526-495531,495533-495548,495551-495553,495555,495557-495558,495560,495562-495573,495575-495583,495585-495594,495596-495628,495630-495638,495640-495651,495653-495660,495662-495753,495755-496259,496261-496262,496264-496269,496271-496275,496277-496301,496303-496316,496318-496383,496385-496413,496415-496495,496497-496625,496627-496636,496638-496640,496642-496647,496650-496657,496659-496660,496663-496664,496666-496677,496679-496681,496683-496730,496732-496750,496752,496754-496784,496786-496832,496834-496840,496842-496990,496992-496995,496997-497340,497343-497351,497353-497403,497405-497424,497426-497438,497440-497481,497483-497497,497499-497765,497767-497769,497771-497775,497777-497778,497780,497782-497783,497785,497787-497812,497814-497871,497873-497877,497879-498573,498575-498588,498590,498592,498594-498636,498638-498669,498671-498686,498688-498689,498691-498719,498721-498964,498966-498969,498971-498973,498975-498982,498985-499035,499037-499040,499042,499044-499048,499050-499082,499084-499086,499088-499164,499167-499169,499171-499355,499357-499370,499372-499373,499375-499391,499393,499395-499425,499428,499430-499445,499447-499455,499457-499460,499462-499465,499467,499469-499489,499491-499492,499494-499531,499533-499562,499566-499627,499629-499715,499717-499732,499734-499755,499758-499763,499765-499780,499782-499795,499797-499802,499804-499844,499846,499848-499850,499852-499863,499865-499873,499875-499974,499976-499978,499980-500263,500265-500283,500285-500309,500311-501000,501002,501012-501057,501059-501095,501097-501390,501392-501410,501413-501447,501449-501454,501456,501458-501464,501466-501471,501473-501803,501805-501913,501915-501916,501918-501919,501921-501944,501946-502171,502173-502177,502181,502183-502247,502250-502252,502254-502260,502262-502267,502270,502272,502274-502575,502577-502609,502611-502619,502621-502626,502628-502654,502656-503592,503594-503603,503605-503608,503610-503636,503638-503645,503647-503705,503707-503789,503791-504024,504026-504111,504113-504506,504508-504735,504737-504863,504865-504867,504869-504914,504916-505241,505243-505254,505257-505267,505269-505354,505356-505891,505893-505971,505973-506400,506402-506404,506407-506438,506440-506516,506518-506541,506543-506966,506968-506971,506973-507095,507097-507108,507111-507454,507456,507459-507471,507473-507556,507558,507560-507581,507585-507594,507597,507599-507608,507610-507728,507730-507893,507895-507937,507940-508234,508236-508350,508352-508365,508367-508380,508383,508386-508415,508417-508648,508650-508941,508943-509146,509148-509171,509173-509175,509179-509201,509203-509207,509209-509215,509217-509222,509224-509477,509480-509627,509629-509634,509636-509641,509643-509736,509738-509931,509933-510059,510061-510075,510077-510158,510161-510896,510898-510938,510940-511388,511390-511922,511924-512287,512289-512698,512702-512813,512815-512817,512819-513359,513361-513370,513372-514702,514704-514886,514888-514902,514904-515126,515129-515141,515143-515516,515518-515534,515536-515538,515540-515648,515650-515651,515653-516070,516072-516411,516413-516448,516450,516452-517637,517639-517647,517649-517659,517661-517663,517665-517677,517679-517682,517684-517744,517746-518085,518087-518175,518177-518558,518560-518568,518571-518666,518668,518670-518699,518701-518987,518990-518992,518994-519908,519910-519932,519934-520414,520416-520842,520844-520937,520939-521362,521364-521681,521683-521704,521706-521709,521711-521714,521716-521781,521783-521792,521794-522462,522464-522527,522529-522534,522536-522566,522568-522958,522960,522962-522966,522968-522976,522978-522980,522982-522988,522992-522993,522995-523244,523246-523746,523748-524049,524051-524738,524741-524742,524744-524762,524764,524766,524768-525486,525488-525530,525532,525534,525537-525552,525554-525765,525767-525776,525778-525784,525789-525803,525805-525816,525818-525828,525830-525861,525863-525866,525868-526090,526092-526112,526114-526116,526119-526121,526123-526149,526151-526153,526155-526156,526160-526165,526167-526186,526188-526193,526196-526197,526200-526665,526667-526682,526686-526690,526693,526695-526708,526710-526713,526715-526775,526777-526802,526804-526806,526808-527048,527051-527052,527054-527181,527183-527486,527488-527492,527494-527498,527500-527508,527510-527517,527519-527536,527538-527555,527559-527802,527804-527842,527844-527847,527849-527875,527877-527940,527942-527958,527960-527971,527973-528002,528004,528006-528423,528425-529232,529234-529245,529247-529296,529298-529634,529636-529658,529660-529665,529667-529668,529670-530033,530035-530036,530038-530040,530045-530046,530050-530051,530053-530431,530433-530436,530439-530440,530443,530445-530446,530448,530450-530682,530684,530687-530696,530698-530733,530735-530776,530778-530795,530799,530801-530811,530813-530818,530820-530837,530839-531436,531438-531455,531457,531459-531511,531514,531516,531519-531523,531525,531528-531858,531860-531864,531866-531907,531909-531916,531918-531936,531938-531988,531990-532001,532003-532371,532373-532465,532467-532727,532729-532765,532767-532785,532788-532790,532792-532793,532795-533064,533066-533074,533076,533080-533130,533132-533139,533142-533703,533705-533720,533722-533763,533766-533818,533820-533839,533841-533859,533862-534035,534037-534112,534114-534116,534118-534472,534474-534477,534479-534762,534764-534896,534898-534902,534904-535253,535255-535308,535310-535808,535810-535873,535875-536007,536009-536140,536142-536162,536165-536242,536244-536252,536254-536278,536280-536338,536340-536448,536450-536479,536481-536482,536484-536485,536487-536495,536497,536500-536505,536507-536561,536563-536570,536572,536574-536583,536586-536823,536825-537014,537016-537018,537020-537025,537027-537028,537030-537160,537162-537170,537172-537672,537674-537781,537783-537833,537836-537840,537842-537844,537846-537953,537955-538034,538036-538078,538080-538083,538085-538097,538099-538108,538110-538239,538241-538881,538883-538906,538908-538911,538913-538921,538923-539177,539179-539190,539192-539469,539471-539475,539477-539480,539482-539483,539485-539500,539502-539593,539595-539782,539784-539787,539789-540106,540108-540168,540170-540510,540512-541246,541248-542483,542485-542788,542790-543495,543497-544108,544110-544421,544423-544507,544509-544865,544867-545145,545147-546095,546097-546189,546191-546440,546442-546457,546459-547177,547179-547626,547628-548275,548277-548278,548280-548301,548303-548307,548309-548311,548313-548314,548316,548318,548320-548380,548382-549010,549012-549529,549531-549848,549850-550508,550510-550747,550749-550772,550774-550848,550850-551116,551122-553446,553448-561282 via svnmerge from https://svn.apache.org/repos/asf/incubator/qpid/branches/M2 ........ r541920 | tomasr | 2007-05-26 18:35:51 +0100 (Sat, 26 May 2007) | 1 line QPID-136 Initial Prefetch Implementation ........ r549112 | arnaudsimon | 2007-06-20 15:11:03 +0100 (Wed, 20 Jun 2007) | 1 line changed setText to use UTF8 as default encoder ........ r551167 | arnaudsimon | 2007-06-27 15:08:50 +0100 (Wed, 27 Jun 2007) | 1 line added public void declareAndBind(AMQDestination amqd) ........ r551174 | ritchiem | 2007-06-27 15:23:21 +0100 (Wed, 27 Jun 2007) | 3 lines Caused each of these tests to run 10 times to help identify any race conditions that were occuring. Updated the CommitRollbackTest to be more robust in the detection of failure. ........ r551175 | ritchiem | 2007-06-27 15:23:52 +0100 (Wed, 27 Jun 2007) | 1 line Allowed more of the constants to be set via system properties. ........ r551176 | ritchiem | 2007-06-27 15:25:13 +0100 (Wed, 27 Jun 2007) | 1 line renamed the passwd programme qpid-passwd to match the change in bin directory. ........ r552441 | rupertlssmith | 2007-07-02 10:23:54 +0100 (Mon, 02 Jul 2007) | 1 line Added log4j as slfj logger for perftests. ........ r552499 | rupertlssmith | 2007-07-02 15:17:45 +0100 (Mon, 02 Jul 2007) | 1 line Added some documentation. ........ r553172 | rupertlssmith | 2007-07-04 12:11:04 +0100 (Wed, 04 Jul 2007) | 1 line Messages moved by management console now commited on the message store. ........ r553248 | ritchiem | 2007-07-04 17:05:55 +0100 (Wed, 04 Jul 2007) | 6 lines Addition of the MessageStore Tool. Small changes to the Exchanges to allow the extraction of currently listed items. Extracted initial broker configuration mechanism to a reusable class. Have modified broker to use it. Move the Passwd.java to new tools package structure on the broker. ........ r553265 | ritchiem | 2007-07-04 17:42:59 +0100 (Wed, 04 Jul 2007) | 1 line Tidied up some extranious logging. ........ r553432 | rupertlssmith | 2007-07-05 10:28:33 +0100 (Thu, 05 Jul 2007) | 1 line Fixed test state carrying over to mandatory message test from immediate. Also added in-vm clean up to other tests. ........ r553480 | ritchiem | 2007-07-05 13:40:50 +0100 (Thu, 05 Jul 2007) | 2 lines Minor changes and tidy up when running via command line. Added Copy command. ........ r553482 | ritchiem | 2007-07-05 13:44:42 +0100 (Thu, 05 Jul 2007) | 2 lines Forgot to compile before committing. Missed a method change in the Select command. ........ r554964 | rupertlssmith | 2007-07-10 15:40:04 +0100 (Tue, 10 Jul 2007) | 1 line Added message copy method. ........ r555249 | rupertlssmith | 2007-07-11 12:52:39 +0100 (Wed, 11 Jul 2007) | 1 line Update perftests to center better around current performance. ........ r556011 | rupertlssmith | 2007-07-13 15:24:03 +0100 (Fri, 13 Jul 2007) | 1 line Moved test framework into its own package and cleaned it up. ........ r556024 | rupertlssmith | 2007-07-13 16:02:06 +0100 (Fri, 13 Jul 2007) | 1 line Completed javadoc for test framework. ........ r556628 | rgodfrey | 2007-07-16 14:50:57 +0100 (Mon, 16 Jul 2007) | 1 line QPID-537 : Make AMQMessage.incrementReference public ........ r556675 | cctrieloff | 2007-07-16 18:36:21 +0100 (Mon, 16 Jul 2007) | 2 lines added notice entries ........ r556680 | cctrieloff | 2007-07-16 18:56:40 +0100 (Mon, 16 Jul 2007) | 2 lines clean up ........ r556682 | cctrieloff | 2007-07-16 18:58:37 +0100 (Mon, 16 Jul 2007) | 2 lines removed optional cppunit as not in distributed packages ........ r556845 | ritchiem | 2007-07-17 09:26:33 +0100 (Tue, 17 Jul 2007) | 3 lines Additional logging in case of broker failure at startup. Use broker logger at error level as well as System.out ........ r556847 | ritchiem | 2007-07-17 09:35:35 +0100 (Tue, 17 Jul 2007) | 3 lines Update to the MessageStore Tool to provide Move and Purge functionality. Updated to remove the AMQExceptions that will be removed from the Exchange class. ........ r556861 | ritchiem | 2007-07-17 10:26:47 +0100 (Tue, 17 Jul 2007) | 2 lines QPID-538 Check to ensure a duplicate binding is not created. ........ r556868 | ritchiem | 2007-07-17 10:55:56 +0100 (Tue, 17 Jul 2007) | 1 line Addition of simple pub/sub examples. ........ r556869 | ritchiem | 2007-07-17 10:56:17 +0100 (Tue, 17 Jul 2007) | 1 line QPID-540 Prevent NPE when purging message from the main _message queue in the ConcurrentSelectorDeliveryManager that have been delivered via a Subscribers _messageQueue. Ensuring that any expired messages are still correctly handled. i.e. the Queue size/depth is reduced and the message correctly dequeued from the underlying store. ........ r556871 | ritchiem | 2007-07-17 10:57:35 +0100 (Tue, 17 Jul 2007) | 1 line White space & code formatting change ........ r556872 | ritchiem | 2007-07-17 10:58:35 +0100 (Tue, 17 Jul 2007) | 3 lines Added additional information to hard-error logging in exceptionReceived. Fully expanded imports ........ r556888 | ritchiem | 2007-07-17 12:33:08 +0100 (Tue, 17 Jul 2007) | 1 line Change to allow the management port to be specified on the command line, via -m or --mport ........ r556890 | ritchiem | 2007-07-17 12:38:10 +0100 (Tue, 17 Jul 2007) | 4 lines QPID-541 A large portion of memory was being wasted in 32k ByteBuffers being held by the AMQShortStrings. Patch submitted by Robert Godfrey to intern() the AMQSSs to reduce memory usage. Current implementation *will* impact performance due to the usage of a static Map for storage. However, a thread local implementation is in the works. ........ r556898 | rgodfrey | 2007-07-17 13:00:57 +0100 (Tue, 17 Jul 2007) | 1 line QPID-541 : Change to use threadlocal maps for intern for the common case to avoid excessive synchronization. In the uncommon case will require more lookup. ........ r556958 | rupertlssmith | 2007-07-17 17:22:16 +0100 (Tue, 17 Jul 2007) | 1 line Refactored the distributed test clients and coordinator to support different distribution and sequencing engines. ........ r556967 | rupertlssmith | 2007-07-17 17:40:14 +0100 (Tue, 17 Jul 2007) | 1 line Removed unused package. ........ r556968 | rupertlssmith | 2007-07-17 17:42:00 +0100 (Tue, 17 Jul 2007) | 1 line Retired old interop tests. ........ r556969 | rupertlssmith | 2007-07-17 17:43:49 +0100 (Tue, 17 Jul 2007) | 1 line Properties file not needed any more. Test properties all driven from MessagingTestConfigProperties. ........ r557276 | ritchiem | 2007-07-18 15:36:11 +0100 (Wed, 18 Jul 2007) | 1 line Updates to pom files and Licensing/Notice files for M2 release. ........ r557279 | ritchiem | 2007-07-18 15:51:42 +0100 (Wed, 18 Jul 2007) | 1 line This is left over from ANT ........ r557281 | ritchiem | 2007-07-18 15:54:06 +0100 (Wed, 18 Jul 2007) | 1 line updated comment to refelect property values ........ r557286 | ritchiem | 2007-07-18 16:02:22 +0100 (Wed, 18 Jul 2007) | 1 line Set default mvn build to assembly:assembly ........ r557288 | ritchiem | 2007-07-18 16:09:07 +0100 (Wed, 18 Jul 2007) | 1 line Ensure the top level release-docs directory is included in the builds ........ r557306 | ritchiem | 2007-07-18 17:01:58 +0100 (Wed, 18 Jul 2007) | 1 line Update fix incorrect license headers. ........ r557312 | ritchiem | 2007-07-18 17:07:01 +0100 (Wed, 18 Jul 2007) | 1 line added license ........ r557314 | ritchiem | 2007-07-18 17:11:17 +0100 (Wed, 18 Jul 2007) | 1 line added license ........ r557452 | aconway | 2007-07-19 03:03:02 +0100 (Thu, 19 Jul 2007) | 14 lines * lib/broker/Daemon.cpp, .h - Rewrote to remove libdaemon dependency. - PID file stored in /var/run if root, /tmp otherwise. * src/qpidd.cpp: Use new Daemon.cpp. - lock files stored in /var/run (for root) or /tmp. - updated to trunk daemon flag behavior. * lib/broker/Makefile.am (libqpidbroker_la_LIBADD): - Daemon.cpp now needs -lboost_iostreams * NOTICE, README: Removed mention of libdaemon. ........ r558027 | ritchiem | 2007-07-20 17:08:05 +0100 (Fri, 20 Jul 2007) | 1 line Added a logger but only used to control the toString inclusion of password. If in debug mode it will include password otherwise the password is "********". ........ r558072 | astitcher | 2007-07-20 18:49:41 +0100 (Fri, 20 Jul 2007) | 2 lines Fixed the license from the "old" apache copyright notice ........ r558083 | aconway | 2007-07-20 19:29:08 +0100 (Fri, 20 Jul 2007) | 2 lines Remove -ldaemon, we no longer require libdaemon. ........ r558099 | aconway | 2007-07-20 20:20:01 +0100 (Fri, 20 Jul 2007) | 2 lines Ignore QPID_ env variables that don't correspond to known options. ........ r558108 | cctrieloff | 2007-07-20 20:55:40 +0100 (Fri, 20 Jul 2007) | 2 lines typo fix ........ r558114 | rajith | 2007-07-20 21:11:03 +0100 (Fri, 20 Jul 2007) | 1 line added release notes ........ r558115 | rajith | 2007-07-20 21:12:20 +0100 (Fri, 20 Jul 2007) | 1 line Checking in the release notes ........ r558116 | aconway | 2007-07-20 21:16:20 +0100 (Fri, 20 Jul 2007) | 3 lines Removed .txt from RELEASE_NOTES Added RELEASE_NOTES to EXTRA_DIST in Makefile.am ........ r558168 | rajith | 2007-07-20 23:03:42 +0100 (Fri, 20 Jul 2007) | 1 line added release notes ........ r558170 | rajith | 2007-07-20 23:04:11 +0100 (Fri, 20 Jul 2007) | 1 line added release notes ........ r558630 | gsim | 2007-07-23 08:21:49 +0100 (Mon, 23 Jul 2007) | 3 lines Revised release notes: removed bug fixed on this branch, removed outstanding feature lists as it is not terribly accurate or helpful. ........ r559419 | rupertlssmith | 2007-07-25 13:17:59 +0100 (Wed, 25 Jul 2007) | 1 line Refactored interop tests into general distributed test framework. Moved framework under systests from integrationtests. ........ r559427 | ritchiem | 2007-07-25 13:40:24 +0100 (Wed, 25 Jul 2007) | 2 lines AMQMessage - added //todo-s and removed unused parameter StoreContext from expired() method call. ConcurrentSelectorDeliveryManager - Update to reflect expired() call change. Created new _reaperContextStore to be used when performing reaper operations such as message dequeue due to expiration. Removed old commented code. ........ r559455 | rupertlssmith | 2007-07-25 14:40:16 +0100 (Wed, 25 Jul 2007) | 1 line Added to comments. ........ r559456 | rupertlssmith | 2007-07-25 14:41:21 +0100 (Wed, 25 Jul 2007) | 1 line Removed redundant method. ........ r559458 | rupertlssmith | 2007-07-25 14:57:21 +0100 (Wed, 25 Jul 2007) | 1 line Refactored packaging of test framework. ........ r559461 | rupertlssmith | 2007-07-25 15:00:16 +0100 (Wed, 25 Jul 2007) | 1 line Removed redundant packages. ........ r559943 | rhs | 2007-07-26 20:15:17 +0100 (Thu, 26 Jul 2007) | 1 line adding missing ack ........ r559944 | rhs | 2007-07-26 20:15:44 +0100 (Thu, 26 Jul 2007) | 1 line removed old script ........ r560198 | ritchiem | 2007-07-27 12:30:34 +0100 (Fri, 27 Jul 2007) | 1 line Added files to ignore list ........ r560225 | ritchiem | 2007-07-27 14:33:50 +0100 (Fri, 27 Jul 2007) | 1 line Converted namespaces from Qpid.* to Apache.Qpid.* ........ r560471 | tomasr | 2007-07-28 03:35:41 +0100 (Sat, 28 Jul 2007) | 1 line Removed using directives causing compilation failure in .NET 1.1 ........ r561278 | ritchiem | 2007-07-31 10:07:57 +0100 (Tue, 31 Jul 2007) | 8 lines Changes to POMs. Client pom now builds a single jar with all dependancies included in the single bundle. systests/pom.xml adjusted to include only *Test.class items. This will fix the current Error on OptOutTestCase management/eclipse-plugin/pom.xml - editied to include there required MANIFEST.MF to identify plugin to eclipse distribution/src/main/assembly/management-eclipse-plugin.xml editied to include there required MANIFEST.MF to identify the plugin distribution/pom.xml - white space Also updated log4j.xml default to create an alert.log file from the AMQQueue alerting. Added a debug.log4j.xml that gives example of debugging the broker via log4j. ........ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@561365 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/RELEASE_NOTES | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 qpid/python/RELEASE_NOTES (limited to 'qpid/python') diff --git a/qpid/python/RELEASE_NOTES b/qpid/python/RELEASE_NOTES new file mode 100644 index 0000000000..7005aa83cb --- /dev/null +++ b/qpid/python/RELEASE_NOTES @@ -0,0 +1,25 @@ +Apache Incubator Qpid Python M2 Release Notes +------------------------------------------- + +The Qpid M2 release contains support the for AMQP 0-8 specification. +You can access the 0-8 specification using the following link. +http://www.amqp.org/tikiwiki/tiki-index.php?page=Download + +For full details of Qpid capabilities, as they currently stand, see our +detailed project documentation at: + +http://cwiki.apache.org/confluence/pages/viewpage.action?pageId=28284 + +Please take time to go through the README file provided with the distro. + + +Known Issues/Outstanding Work +----------------------------- + +There are no known issues for the Phyton client. + + +M2 Tasks Completed +------------------- + +Bug QPID-467 Complete Interop Testing -- cgit v1.2.1 From faf8adec808ff9dfdf50c7f0a2d13030cc2b4286 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 1 Aug 2007 04:45:06 +0000 Subject: added future imports for jython git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@561654 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/dom.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py index 5a5d017ff9..d95326d1a1 100644 --- a/qpid/python/mllib/dom.py +++ b/qpid/python/mllib/dom.py @@ -21,6 +21,10 @@ Simple DOM for both SGML and XML documents. """ +from __future__ import division +from __future__ import generators +from __future__ import nested_scopes + from transforms import Text class Container: -- cgit v1.2.1 From e512cef4ddb7e2e73bff17fe87915ce79f7da72d Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 2 Aug 2007 18:09:48 +0000 Subject: Some restructuring of the client code: * Introduced three separate 'handlers' for the connection, channel and execution 'layers'. * Support for asynchronous retrieval of response or completion status. * Channel methods no longer included in execution layers command id count. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@562212 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/message.py | 2 +- qpid/python/qpid/peer.py | 9 +-------- qpid/python/qpid/spec.py | 3 +++ 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index 970ab9d974..7e28b63037 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -26,7 +26,7 @@ class Message: self.frame = frame self.method = frame.method_type self.content = content - if self.method.klass.name != "execution": + if self.method.is_l4_command(): self.command_id = self.channel.incoming_completion.sequence.next() #print "allocated: ", self.command_id, "to ", self.method.klass.name, "_", self.method.name diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index bedc96895b..2861d51165 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -376,7 +376,7 @@ class OutgoingCompletion: def next_command(self, method): #the following test is a hack until the track/sub-channel is available - if method.klass.name != "execution": + if method.is_l4_command(): self.command_id = self.sequence.next() def reset(self): @@ -425,13 +425,6 @@ class IncomingCompletion: self.mark = 0 #id of last command of whose completion notification was sent to the other peer self.channel = channel - def next_id(self, method): - #the following test is a hack until the track/sub-channel is available - if method.klass.name != "execution": - return self.sequence.next() - else: - return 0 - def reset(self): self.sequence = Sequence(1) #reset counter diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 09e7dc9d0b..9bbe709ed9 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -191,6 +191,9 @@ class Method(Metadata): self.docs = docs self.response = False + def is_l4_command(self): + return self.klass.name not in ["execution", "channel", "connection"] + def arguments(self, *args, **kwargs): nargs = len(args) + len(kwargs) maxargs = len(self.fields) -- cgit v1.2.1 From 5838439eb64e0d812744405a5a2b172e52ab9f61 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 8 Aug 2007 17:11:12 +0000 Subject: Timeout after waiting for completion that doesn't arrive. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@563945 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 2861d51165..4a560fca5a 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -269,7 +269,7 @@ class Channel: self.request(frame, self.queue_response, content) if not frame.method.responses: - if self.use_execution_layer and type.klass.name != "execution": + if self.use_execution_layer and type.is_l4_command(): self.execution_flush() self.completion.wait() if self.closed: @@ -312,11 +312,13 @@ class Channel: else: raise ValueError(resp) elif self.synchronous and not frame.method.response \ - and self.use_execution_layer and frame.method.klass.name != "execution": + and self.use_execution_layer and frame.method.is_l4_command(): self.execution_flush() - self.completion.wait() + completed = self.completion.wait(timeout=10) if self.closed: raise Closed(self.reason) + if not completed: + raise close("Timed-out waiting for completion") except QueueClosed, e: if self.closed: -- cgit v1.2.1 From 61bc423099806cdfb952dfd730989e11a8ce106e Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 9 Aug 2007 09:03:42 +0000 Subject: Fixed syntax error in handling of timeouts. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564154 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 4a560fca5a..bbba94623d 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -318,7 +318,7 @@ class Channel: if self.closed: raise Closed(self.reason) if not completed: - raise close("Timed-out waiting for completion") + self.close("Timed-out waiting for completion") except QueueClosed, e: if self.closed: -- cgit v1.2.1 From 6d57872baf835acfd17a9e6a185299a5bf4f6fd5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 9 Aug 2007 19:57:10 +0000 Subject: added support for parsing structs and results into the spec metadata git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564362 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 60 ++++++++++++++++++++++++++++++++++++++--------- qpid/python/tests/spec.py | 56 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 qpid/python/tests/spec.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 9bbe709ed9..f9d305c133 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -42,7 +42,9 @@ class SpecContainer: def add(self, item): if self.byname.has_key(item.name): raise ValueError("duplicate name: %s" % item) - if self.byid.has_key(item.id): + if item.id == None: + item.id = len(self) + elif self.byid.has_key(item.id): raise ValueError("duplicate id: %s" % item) self.indexes[item] = len(self.items) self.items.append(item) @@ -144,15 +146,26 @@ class Domain(Metadata): PRINT=["name", "type"] - def __init__(self, spec, id, name, type, description, docs): + def __init__(self, spec, name, type, description, docs): Metadata.__init__(self) self.spec = spec - self.id = id + self.id = None self.name = name self.type = type self.description = description self.docs = docs +class Struct(Metadata): + + PRINT=["size", "type", "pack"] + + def __init__(self, size, type, pack): + Metadata.__init__(self) + self.size = size + self.type = type + self.pack = pack + self.fields = SpecContainer() + class Class(Metadata): PRINT=["name", "id"] @@ -177,7 +190,7 @@ class Method(Metadata): PRINT=["name", "id"] - def __init__(self, klass, name, id, content, responses, synchronous, + def __init__(self, klass, name, id, content, responses, result, synchronous, description, docs): Metadata.__init__(self) self.klass = klass @@ -185,6 +198,7 @@ class Method(Metadata): self.id = id self.content = content self.responses = responses + self.result = result self.synchronous = synchronous self.fields = SpecContainer() self.description = description @@ -224,8 +238,8 @@ class Method(Metadata): if f.docs: s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, f.name)] + [fill(d, 4) for d in f.docs[1:]]) - if self.responses: - s += "\n\nValid responses: " + if self.responses: + s += "\n\nValid responses: " for r in self.responses: s += r.name + " " return s @@ -243,8 +257,8 @@ class Method(Metadata): "content": None, "uuid": "", "rfc1982_long": 0, - "rfc1982_long_set": [] - } + "rfc1982_long_set": [], + "long_struct": None} def define_method(self, name): g = {Method.METHOD: self} @@ -281,6 +295,16 @@ class Field(Metadata): self.description = description self.docs = docs +def get_result(nd, spec): + result = nd["result"] + if not result: return None + name = result["@domain"] + if name != None: return spec.domains.byname[name] + st_nd = result["struct"] + st = Struct(st_nd["@size"], st_nd["@type"], st_nd["@pack"]) + load_fields(st_nd, st.fields, spec.domains.byname) + return st + def get_desc(nd): label = nd["@label"] if not label: @@ -321,11 +345,24 @@ def load(specfile, *errata): spec.constants.add(const) except ValueError, e: print "Warning:", e + # domains are typedefs + structs = [] for nd in root.query["domain"]: - spec.domains.add(Domain(spec, nd.index(), pythonize(nd["@name"]), - pythonize(nd["@type"]), get_desc(nd), - get_docs(nd))) + type = nd["@type"] + if type == None: + st_nd = nd["struct"] + type = Struct(st_nd["@size"], st_nd["@type"], st_nd["@pack"]) + structs.append((type, st_nd)) + else: + type = pythonize(type) + domain = Domain(spec, pythonize(nd["@name"]), type, get_desc(nd), + get_docs(nd)) + spec.domains.add(domain) + + # structs + for st, st_nd in structs: + load_fields(st_nd, st.fields, spec.domains.byname) # classes for c_nd in root.query["class"]: @@ -348,6 +385,7 @@ def load(specfile, *errata): int(m_nd["@index"]), m_nd["@content"] == "1", [pythonize(nd["@name"]) for nd in m_nd.query["response"]], + get_result(m_nd, spec), m_nd["@synchronous"] == "1", get_desc(m_nd), get_docs(m_nd)) diff --git a/qpid/python/tests/spec.py b/qpid/python/tests/spec.py new file mode 100644 index 0000000000..c70e4f9c4b --- /dev/null +++ b/qpid/python/tests/spec.py @@ -0,0 +1,56 @@ +from unittest import TestCase +from qpid.spec import load + + +class SpecTest(TestCase): + + def check_load(self, *urls): + spec = load(*urls) + qdecl = spec.method("queue_declare") + assert qdecl != None + assert not qdecl.content + + queue = qdecl.fields.byname["queue"] + assert queue != None + assert queue.domain.name == "queue_name" + assert queue.type == "shortstr" + + qdecl_ok = spec.method("queue_declare_ok") + + # 0-8 is actually 8-0 + if (spec.major == 8 and spec.minor == 0 or + spec.major == 0 and spec.minor == 9): + assert qdecl_ok != None + + assert len(qdecl.responses) == 1 + assert qdecl_ok in qdecl.responses + + publish = spec.method("basic_publish") + assert publish != None + assert publish.content + + if (spec.major == 0 and spec.minor == 10): + assert qdecl_ok == None + reply_to = spec.domains.byname["reply_to"] + assert reply_to.type.size == "short" + assert reply_to.type.pack == "short" + assert len(reply_to.type.fields) == 2 + + qq = spec.method("queue_query") + assert qq != None + assert qq.result.size == "long" + assert qq.result.type != None + args = qq.result.fields.byname["arguments"] + assert args.type == "table" + + def test_load_0_8(self): + self.check_load("../specs/amqp.0-8.xml") + + def test_load_0_9(self): + self.check_load("../specs/amqp.0-9.xml") + + def test_load_0_9_errata(self): + self.check_load("../specs/amqp.0-9.xml", "../specs/amqp-errata.0-9.xml") + + def test_load_0_10(self): + self.check_load("../specs/amqp.0-10-preview.xml") -- cgit v1.2.1 From dde71dbbb0a359403f7d97f1ee07d86a684509d2 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 9 Aug 2007 20:19:01 +0000 Subject: removed extraneous whitespace git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564369 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 2c22c04199..2cc7e75f95 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -6,9 +6,9 @@ # 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 @@ -90,7 +90,7 @@ Options: "True if we are running with the old 0-8 spec." # NB: AMQP 0-8 identifies itself as 8-0 for historical reasons. return self.spec.major==8 and self.spec.minor==0 - + def _parseargs(self, args): # Defaults self.setBroker("localhost") @@ -125,7 +125,7 @@ Options: self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") - elif self.spec.major == 0 and self.spec.minor == 10: + elif self.spec.major == 0 and self.spec.minor == 10: self.tests+=findmodules("tests_0-10") else: self.tests+=findmodules("tests_0-9") @@ -140,13 +140,13 @@ Options: # Use our IgnoringTestSuite in the test loader. unittest.TestLoader.suiteClass = IgnoringTestSuite return unittest.defaultTestLoader.loadTestsFromNames(self.tests) - + def run(self, args=sys.argv[1:]): self._parseargs(args) runner = unittest.TextTestRunner(descriptions=False, verbosity=self.verbose) result = runner.run(self.testSuite()) - + if (self.ignore): print "=======================================" print "NOTE: the following tests were ignored:" @@ -180,7 +180,7 @@ class TestBase(unittest.TestCase): Deletes queues and exchanges after. Tests call self.queue_declare(channel, ...) and self.exchange_declare(chanel, ...) which are wrappers for the Channel functions that note - resources to clean up later. + resources to clean up later. """ def setUp(self): @@ -199,9 +199,9 @@ class TestBase(unittest.TestCase): except: print "Error on tearDown:", sys.exc_info() - if not self.client.closed: + if not self.client.closed: self.client.channel(0).connection_close(reply_code=200) - else: + else: self.client.close() def connect(self, *args, **keys): @@ -213,7 +213,7 @@ class TestBase(unittest.TestCase): reply = channel.queue_declare(*args, **keys) self.queues.append((channel, reply.queue)) return reply - + def exchange_declare(self, channel=None, ticket=0, exchange='', type='', passive=False, durable=False, auto_delete=False, @@ -227,7 +227,7 @@ class TestBase(unittest.TestCase): """Generate a unique string, unique for this TestBase instance""" if not "uniqueCounter" in dir(self): self.uniqueCounter = 1; return "Test Message " + str(self.uniqueCounter) - + def consume(self, queueName): """Consume from named queue returns the Queue object.""" if testrunner.use08spec(): @@ -271,7 +271,7 @@ class TestBase(unittest.TestCase): self.assertEqual(body, msg.body) if (properties): self.assertEqual(properties, msg.application_headers) - + def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): """ Publish a message and consume it, assert it comes back intact. @@ -286,7 +286,7 @@ class TestBase(unittest.TestCase): self.assertEqual(expectedCode, message.reply_code) - def assertConnectionException(self, expectedCode, message): + def assertConnectionException(self, expectedCode, message): if not isinstance(message, Message): self.fail("expected connection_close method, got %s" % (message)) self.assertEqual("connection", message.method.klass.name) self.assertEqual("close", message.method.name) -- cgit v1.2.1 From 198416d32bfedb55e653e27d6cdb9d05fc874aae Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 9 Aug 2007 23:02:00 +0000 Subject: removed extraneous whitespace git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564414 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index bbba94623d..11246588d9 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -6,9 +6,9 @@ # 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 @@ -30,7 +30,7 @@ from message import Message from queue import Queue, Closed as QueueClosed from content import Content from cStringIO import StringIO -from time import time +from time import time class Sequence: @@ -266,12 +266,12 @@ class Channel: self.request(frame, future.put_response, content) if not frame.method.responses: return None else: return future - + self.request(frame, self.queue_response, content) if not frame.method.responses: if self.use_execution_layer and type.is_l4_command(): self.execution_flush() - self.completion.wait() + self.completion.wait() if self.closed: raise Closed(self.reason) return None @@ -314,7 +314,7 @@ class Channel: elif self.synchronous and not frame.method.response \ and self.use_execution_layer and frame.method.is_l4_command(): self.execution_flush() - completed = self.completion.wait(timeout=10) + completed = self.completion.wait(timeout=10) if self.closed: raise Closed(self.reason) if not completed: @@ -367,7 +367,7 @@ class OutgoingCompletion: """ Manages completion of outgoing commands i.e. command sent by this peer """ - + def __init__(self): self.condition = threading.Condition() @@ -439,7 +439,3 @@ class IncomingCompletion: #TODO: record and manage the ranges properly range = [mark, mark] self.channel.execution_complete(cumulative_execution_mark=self.mark, ranged_execution_set=range) - - - - -- cgit v1.2.1 From ad0f4097dc9602c657389994bf49ed097cbd802d Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 9 Aug 2007 23:58:41 +0000 Subject: split invoke out into two distinct version specific impls git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564434 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 61 ++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 28 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 11246588d9..03c48bef90 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -191,7 +191,10 @@ class Channel: self.incoming_completion = IncomingCompletion(self) # Use reliable framing if version == 0-9. - self.reliable = (spec.major == 0 and spec.minor == 9) + if spec.major == 0 and spec.minor == 9: + self.invoker = self.invoke_reliable + else: + self.invoker = self.invoke_method self.use_execution_layer = (spec.major == 0 and spec.minor == 10) self.synchronous = True @@ -260,36 +263,38 @@ class Channel: self.completion.next_command(type) content = kwargs.pop("content", None) frame = Method(type, type.arguments(*args, **kwargs)) - if self.reliable: - if not self.synchronous: - future = Future() - self.request(frame, future.put_response, content) - if not frame.method.responses: return None - else: return future - - self.request(frame, self.queue_response, content) - if not frame.method.responses: - if self.use_execution_layer and type.is_l4_command(): - self.execution_flush() - self.completion.wait() - if self.closed: - raise Closed(self.reason) - return None - try: - resp = self.responses.get() - if resp.method_type.content: - return Message(self, resp, read_content(self.responses)) - else: - return Message(self, resp) - - except QueueClosed, e: + return self.invoker(frame, content) + + # used for 0-9 + def invoke_reliable(self, frame, content = None): + if not self.synchronous: + future = Future() + self.request(frame, future.put_response, content) + if not frame.method.responses: return None + else: return future + + self.request(frame, self.queue_response, content) + if not frame.method.responses: + if self.use_execution_layer and type.is_l4_command(): + self.execution_flush() + self.completion.wait() if self.closed: raise Closed(self.reason) - else: - raise e - else: - return self.invoke_method(frame, content) + return None + try: + resp = self.responses.get() + if resp.method_type.content: + return Message(self, resp, read_content(self.responses)) + else: + return Message(self, resp) + + except QueueClosed, e: + if self.closed: + raise Closed(self.reason) + else: + raise e + # used for 0-8 and 0-10 def invoke_method(self, frame, content = None): self.write(frame, content) -- cgit v1.2.1 From 0ced84fe4947e2d2e37c0422aec7f6a2ac4ebd69 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 10 Aug 2007 14:51:08 +0000 Subject: Broker management of message acknowledgements now runs entirely off execution layer. Flow control support. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564611 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 138 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 74e2b6416f..b882cd5438 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -645,7 +645,145 @@ class MessageTests(TestBase): self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz") self.assertEmpty(self.client.queue("consumer")) + + def test_credit_flow_messages(self): + """ + Test basic credit based flow control with unit = message + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + #create consumer (for now that defaults to infinite credit) + channel.message_consume(queue = "q", destination = "c") + channel.message_flow_mode(mode = 0, destination = "c") + #set credit to zero (can remove this once move to proper default for subscribe method) + channel.message_stop(destination = "c") + #send batch of messages to queue + for i in range(1, 11): + channel.message_transfer(routing_key = "q", body = "Message %d" % i) + #set message credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 0, value = 5, destination = "c") + #set infinite byte credit + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + for i in range(1, 6): + self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertEmpty(q) + + #increase credit again and check more are received + for i in range(6, 11): + channel.message_flow(unit = 0, value = 1, destination = "c") + self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertEmpty(q) + + def test_credit_flow_bytes(self): + """ + Test basic credit based flow control with unit = bytes + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + #create consumer (for now that defaults to infinite credit) + channel.message_consume(queue = "q", destination = "c") + channel.message_flow_mode(mode = 0, destination = "c") + #set credit to zero (can remove this once move to proper default for subscribe method) + channel.message_stop(destination = "c") + #send batch of messages to queue + for i in range(1, 11): + channel.message_transfer(routing_key = "q", body = "abcdefgh") + + #each message is currently interpreted as requiring 75 bytes of credit + #set byte credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 1, value = 75*5, destination = "c") + #set infinite message credit + channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + for i in range(1, 6): + self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertEmpty(q) + + #increase credit again and check more are received + for i in range(6, 11): + channel.message_flow(unit = 1, value = 75, destination = "c") + self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertEmpty(q) + + + def test_window_flow_messages(self): + """ + Test basic window based flow control with unit = message + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + #create consumer (for now that defaults to infinite credit) + channel.message_consume(queue = "q", destination = "c") + channel.message_flow_mode(mode = 1, destination = "c") + #set credit to zero (can remove this once move to proper default for subscribe method) + channel.message_stop(destination = "c") + #send batch of messages to queue + for i in range(1, 11): + channel.message_transfer(routing_key = "q", body = "Message %d" % i) + + #set message credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 0, value = 5, destination = "c") + #set infinite byte credit + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + for i in range(1, 6): + msg = q.get(timeout = 1) + self.assertDataEquals(channel, msg, "Message %d" % i) + self.assertEmpty(q) + + #acknowledge messages and check more are received + msg.complete(cumulative=True) + for i in range(6, 11): + self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertEmpty(q) + + + def test_window_flow_bytes(self): + """ + Test basic window based flow control with unit = bytes + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + #create consumer (for now that defaults to infinite credit) + channel.message_consume(queue = "q", destination = "c") + channel.message_flow_mode(mode = 1, destination = "c") + #set credit to zero (can remove this once move to proper default for subscribe method) + channel.message_stop(destination = "c") + #send batch of messages to queue + for i in range(1, 11): + channel.message_transfer(routing_key = "q", body = "abcdefgh") + + #each message is currently interpreted as requiring 75 bytes of credit + #set byte credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 1, value = 75*5, destination = "c") + #set infinite message credit + channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + msgs = [] + for i in range(1, 6): + msg = q.get(timeout = 1) + msgs.append(msg) + self.assertDataEquals(channel, msg, "abcdefgh") + self.assertEmpty(q) + + #ack each message individually and check more are received + for i in range(6, 11): + msg = msgs.pop() + msg.complete(cumulative=False) + self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertEmpty(q) + + def assertDataEquals(self, channel, msg, expected): if isinstance(msg.body, ReferenceId): -- cgit v1.2.1 From d1753304ae2248afbe7fad712a76ae87b4c0140a Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 10 Aug 2007 15:55:16 +0000 Subject: added support for unpacked structs and execution.result git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@564637 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 12 ++++++------ qpid/python/qpid/__init__.py | 23 ++++++++++++++++++++++ qpid/python/qpid/client.py | 6 +++++- qpid/python/qpid/codec.py | 27 ++++++++++++++++++++++---- qpid/python/qpid/connection.py | 6 +++--- qpid/python/qpid/content.py | 8 ++++++++ qpid/python/qpid/message.py | 2 +- qpid/python/qpid/peer.py | 16 ++++++++++++--- qpid/python/qpid/spec.py | 16 +++++++++++++-- qpid/python/server | 44 ++++++++++++++++++++++++++++++++++++------ qpid/python/tests/codec.py | 7 +++++-- 11 files changed, 139 insertions(+), 28 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index 8b7a2752c5..4fb065ae53 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -3,13 +3,13 @@ import qpid from qpid.client import Client from qpid.content import Content -client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-9.xml", - "../specs/amqp-errata.0-9.xml")) +client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-10-preview.xml")) client.start({"LOGIN": "guest", "PASSWORD": "guest"}) ch = client.channel(1) -ch.channel_open() +ch.session_open() ch.queue_declare(queue="test") ch.queue_bind(exchange="amq.direct", queue="test", routing_key="test") -ch.message_consume(queue="test", destination="test") -ch.message_transfer(destination="amq.direct", routing_key="test", - body="hello world") +print ch.queue_query(queue="test") +ch.message_subscribe(queue="test", destination="test") +ch.message_transfer(destination="amq.direct", + content=Content("hello world")) diff --git a/qpid/python/qpid/__init__.py b/qpid/python/qpid/__init__.py index 4363f175fb..4aeccae38e 100644 --- a/qpid/python/qpid/__init__.py +++ b/qpid/python/qpid/__init__.py @@ -18,3 +18,26 @@ # import spec, codec, connection, content, peer, delegate, client + +class Struct: + + def __init__(self, type): + self.__dict__["type"] = type + self.__dict__["_values"] = {} + + def _check(self, attr): + field = self.type.fields.byname.get(attr) + if field == None: + raise AttributeError(attr) + return field + + def __setattr__(self, attr, value): + self._check(attr) + self._values[attr] = value + + def __getattr__(self, attr): + field = self._check(attr) + return self._values.get(attr, field.default()) + + def __str__(self): + return "%s %s" % (self.type.type, self._values) diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index f1800204db..3efd79c389 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -110,7 +110,7 @@ class ClientDelegate(Delegate): #todo: just override the params, i.e. don't require them # all to be included in tune_params msg.tune_ok(**self.client.tune_params) - else: + else: msg.tune_ok(*msg.frame.args) self.client.started.set() @@ -143,6 +143,10 @@ class ClientDelegate(Delegate): def execution_complete(self, ch, msg): ch.completion.complete(msg.cumulative_execution_mark) + def execution_result(self, ch, msg): + future = ch.futures[msg.command_id] + future.put_response(ch, msg.data) + def close(self, reason): self.client.closed = True self.client.reason = reason diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index a11486376d..3920f2c8d9 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -26,7 +26,7 @@ fields. The unit test for this module is located in tests/codec.py """ -import re +import re, qpid from cStringIO import StringIO from struct import * from reference import ReferenceId @@ -40,11 +40,12 @@ class Codec: class that handles encoding/decoding of AMQP primitives """ - def __init__(self, stream): + def __init__(self, stream, spec): """ initializing the stream/fields used """ self.stream = stream + self.spec = spec self.nwrote = 0 self.nread = 0 self.incoming_bits = [] @@ -163,7 +164,7 @@ class Codec: # short int's valid range is [0,65535] if (o < 0 or o > 65535): - raise ValueError('Valid range of short int is [0,65535]') + raise ValueError('Valid range of short int is [0,65535]: %s' % o) self.pack("!H", o) @@ -255,7 +256,7 @@ class Codec: encodes a table data structure in network byte order """ enc = StringIO() - codec = Codec(enc) + codec = Codec(enc, self.spec) if tbl: for key, value in tbl.items(): if len(key) > 128: @@ -356,3 +357,21 @@ class Codec: def decode_uuid(self): return self.decode_longstr() + + def encode_long_struct(self, s): + enc = StringIO() + codec = Codec(enc, self.spec) + type = s.type + codec.encode_short(type.type) + for f in type.fields: + codec.encode(f.type, getattr(s, f.name)) + codec.flush() + self.encode_longstr(enc.getvalue()) + + def decode_long_struct(self): + codec = Codec(StringIO(self.decode_longstr()), self.spec) + type = self.spec.structs[codec.decode_short()] + s = qpid.Struct(type) + for f in type.fields: + setattr(s, f.name, codec.decode(f.type)) + return s diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index cdfa2c2dc0..58235117ef 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -74,7 +74,7 @@ def listen(host, port, predicate = lambda: True): class Connection: def __init__(self, io, spec): - self.codec = codec.Codec(io) + self.codec = codec.Codec(io, spec) self.spec = spec self.FRAME_END = self.spec.constants.byname["frame_end"].id @@ -95,7 +95,7 @@ class Connection: c.encode_octet(self.spec.constants.byname[frame.type].id) c.encode_short(frame.channel) body = StringIO() - enc = codec.Codec(body) + enc = codec.Codec(body, self.spec) frame.encode(enc) enc.flush() c.encode_longstr(body.getvalue()) @@ -106,7 +106,7 @@ class Connection: type = self.spec.constants.byid[c.decode_octet()].name channel = c.decode_short() body = c.decode_longstr() - dec = codec.Codec(StringIO(body)) + dec = codec.Codec(StringIO(body), self.spec) frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) frame.channel = channel end = c.decode_octet() diff --git a/qpid/python/qpid/content.py b/qpid/python/qpid/content.py index bcbea1697c..9391f4f1a8 100644 --- a/qpid/python/qpid/content.py +++ b/qpid/python/qpid/content.py @@ -48,3 +48,11 @@ class Content: def __delitem__(self, name): del self.properties[name] + + def __str__(self): + if self.children: + return "%s [%s] %s" % (self.properties, + ", ".join(map(str, self.children)), + self.body) + else: + return "%s %s" % (self.properties, self.body) diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index 7e28b63037..c9ea5a8a0c 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -29,7 +29,7 @@ class Message: if self.method.is_l4_command(): self.command_id = self.channel.incoming_completion.sequence.next() #print "allocated: ", self.command_id, "to ", self.method.klass.name, "_", self.method.name - + def __len__(self): return len(self.frame.args) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 03c48bef90..6762f774f4 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -189,6 +189,7 @@ class Channel: self.completion = OutgoingCompletion() self.incoming_completion = IncomingCompletion(self) + self.futures = {} # Use reliable framing if version == 0-9. if spec.major == 0 and spec.minor == 9: @@ -261,6 +262,7 @@ class Channel: self.completion.reset() self.incoming_completion.reset() self.completion.next_command(type) + content = kwargs.pop("content", None) frame = Method(type, type.arguments(*args, **kwargs)) return self.invoker(frame, content) @@ -275,7 +277,7 @@ class Channel: self.request(frame, self.queue_response, content) if not frame.method.responses: - if self.use_execution_layer and type.is_l4_command(): + if self.use_execution_layer and frame.method_type.is_l4_command(): self.execution_flush() self.completion.wait() if self.closed: @@ -287,7 +289,6 @@ class Channel: return Message(self, resp, read_content(self.responses)) else: return Message(self, resp) - except QueueClosed, e: if self.closed: raise Closed(self.reason) @@ -296,6 +297,11 @@ class Channel: # used for 0-8 and 0-10 def invoke_method(self, frame, content = None): + if frame.method.result: + cmd_id = self.completion.command_id + future = Future() + self.futures[cmd_id] = future + self.write(frame, content) try: @@ -316,6 +322,11 @@ class Channel: return Message(self, resp, content) else: raise ValueError(resp) + elif frame.method.result: + if self.synchronous: + return future.get_response(timeout=10) + else: + return future elif self.synchronous and not frame.method.response \ and self.use_execution_layer and frame.method.is_l4_command(): self.execution_flush() @@ -324,7 +335,6 @@ class Channel: raise Closed(self.reason) if not completed: self.close("Timed-out waiting for completion") - except QueueClosed, e: if self.closed: raise Closed(self.reason) diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index f9d305c133..8a511bcb3d 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -91,6 +91,8 @@ class Spec(Metadata): self.classes = SpecContainer() # methods indexed by classname_methname self.methods = {} + # structs by type code + self.structs = {} def post_load(self): self.module = self.define_module("amqp%s%s" % (self.major, self.minor)) @@ -295,13 +297,18 @@ class Field(Metadata): self.description = description self.docs = docs + def default(self): + return Method.DEFAULTS[self.type] + def get_result(nd, spec): result = nd["result"] if not result: return None name = result["@domain"] if name != None: return spec.domains.byname[name] st_nd = result["struct"] - st = Struct(st_nd["@size"], st_nd["@type"], st_nd["@pack"]) + st = Struct(st_nd["@size"], int(result.parent.parent["@index"])*256 + + int(st_nd["@type"]), st_nd["@pack"]) + spec.structs[st.type] = st load_fields(st_nd, st.fields, spec.domains.byname) return st @@ -352,7 +359,12 @@ def load(specfile, *errata): type = nd["@type"] if type == None: st_nd = nd["struct"] - type = Struct(st_nd["@size"], st_nd["@type"], st_nd["@pack"]) + code = st_nd["@type"] + if code not in (None, "", "none"): + code = int(code) + type = Struct(st_nd["@size"], code, st_nd["@pack"]) + if type.type != None: + spec.structs[type.type] = type structs.append((type, st_nd)) else: type = pythonize(type) diff --git a/qpid/python/server b/qpid/python/server index 56f0f32081..37416314e2 100755 --- a/qpid/python/server +++ b/qpid/python/server @@ -3,22 +3,54 @@ import qpid from qpid.connection import Connection, listen from qpid.delegate import Delegate from qpid.peer import Peer +from qpid import Struct class Server(Delegate): + def __init__(self): + Delegate.__init__(self) + self.queues = {} + self.bindings = {} + def connection_open(self, ch, msg): msg.open_ok() - def channel_open(self, ch, msg): - print "channel %s open" % ch.id - msg.open_ok() + def session_open(self, ch, msg): + print "session open on channel %s" % ch.id + msg.attached() + + def execution_flush(self, ch, msg): + pass + + def queue_declare(self, ch, msg): + self.queues[msg.queue] = [] + print "queue declared: %s" % msg.queue + msg.complete() + + def queue_bind(self, ch, msg): + if self.bindings.has_key(msg.exchange): + queues = self.bindings[msg.exchange] + else: + queues = set() + self.bindings[msg.exchange] = queues + queues.add((msg.routing_key, msg.queue)) + msg.complete() + + def queue_query(self, ch, msg): + st = Struct(msg.method.result) + ch.execution_result(msg.command_id, st) + msg.complete() + + def message_subscribe(self, ch, msg): + print msg + msg.complete() def message_transfer(self, ch, msg): - print msg.body - msg.ok() + print msg.content + msg.complete() -spec = qpid.spec.load("../specs/amqp.0-9.xml") +spec = qpid.spec.load("../specs/amqp.0-10-preview.xml") for io in listen("0.0.0.0", 5672): c = Connection(io, spec) diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py index 785f1aba6b..689a2cf4c1 100644 --- a/qpid/python/tests/codec.py +++ b/qpid/python/tests/codec.py @@ -20,6 +20,7 @@ import unittest from qpid.codec import Codec +from qpid.spec import load from cStringIO import StringIO from qpid.reference import ReferenceId @@ -52,11 +53,13 @@ __doc__ = """ """ +SPEC = load("../specs/amqp.0-10-preview.xml") # -------------------------------------- # -------------------------------------- class BaseDataTypes(unittest.TestCase): + """ Base class containing common functions """ @@ -66,7 +69,7 @@ class BaseDataTypes(unittest.TestCase): """ standard setUp for unitetest (refer unittest documentation for details) """ - self.codec = Codec(StringIO()) + self.codec = Codec(StringIO(), SPEC) # ------------------ def tearDown(self): @@ -504,7 +507,7 @@ def test(type, value): else: values = [value] stream = StringIO() - codec = Codec(stream) + codec = Codec(stream, SPEC) for v in values: codec.encode(type, v) codec.flush() -- cgit v1.2.1 From c5920c37a2b68d47decbdd11f9c46e3a283aa7b5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 20 Aug 2007 17:31:57 +0000 Subject: Permit skipping of self tests and specify a folder for the spec file. Patch is mostly as supplied by eokyere@gmail.com with minor modifications. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@567764 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 29 +++++++++++++++++++++++------ qpid/python/tests/codec.py | 3 ++- qpid/python/tests/spec.py | 12 ++++++------ 3 files changed, 31 insertions(+), 13 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 2cc7e75f95..8a3abf840b 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -45,6 +45,9 @@ def default(value, default): else: return value class TestRunner: + + SPEC_FOLDER = "../specs" + """Runs unit tests. Parses command line arguments, provides utility functions for tests, @@ -67,6 +70,8 @@ Options: -d/--debug : enable debug logging. -i/--ignore : ignore the named test. -I/--ignore-file : file containing patterns to ignore. + -S/--skip-self-test : skips the client self tests in the 'tests folder' + -F/--spec-folder : folder that contains the specs to be loaded """ sys.exit(1) @@ -98,8 +103,13 @@ Options: self.ignore = [] self.specfile = "0-8" self.errata = [] + self.skip_self_test = False + try: - opts, self.tests = getopt(args, "s:e:b:h?dvi:I:", ["help", "spec", "errata=", "server", "verbose", "ignore", "ignore-file"]) + opts, self.tests = getopt(args, "s:e:b:h?dvSi:I:F:", + ["help", "spec", "errata=", "server", + "verbose", "skip-self-test", "ignore", + "ignore-file", "spec-folder"]) except GetoptError, e: self._die(str(e)) for opt, value in opts: @@ -111,18 +121,23 @@ Options: if opt in ("-d", "--debug"): logging.basicConfig(level=logging.DEBUG) if opt in ("-i", "--ignore"): self.ignore.append(value) if opt in ("-I", "--ignore-file"): self.ignoreFile(value) + if opt in ("-S", "--skip-self-test"): self.skip_self_test = True + if opt in ("-F", "--spec-folder"): TestRunner.SPEC_FOLDER = value # Abbreviations for default settings. if (self.specfile == "0-8"): - self.specfile = "../specs/amqp.0-8.xml" - if (self.specfile == "0-9"): - self.specfile = "../specs/amqp.0-9.xml" - self.errata.append("../specs/amqp-errata.0-9.xml") + self.specfile = self.get_spec_file("amqp.0-8.xml") + elif (self.specfile == "0-9"): + self.specfile = self.get_spec_file("amqp.0-9.xml") + self.errata.append(self.get_spec_file("amqp-errata.0-9.xml")) + if (self.specfile == None): self._die("No XML specification provided") print "Using specification from:", self.specfile self.spec = qpid.spec.load(self.specfile, *self.errata) + if len(self.tests) == 0: - self.tests=findmodules("tests") + if not self.skip_self_test: + self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") elif self.spec.major == 0 and self.spec.minor == 10: @@ -166,6 +181,8 @@ Options: client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params) return client + def get_spec_file(self, fname): + return TestRunner.SPEC_FOLDER + os.sep + fname # Global instance for tests to call connect. testrunner = TestRunner() diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py index 689a2cf4c1..f9f7aab918 100644 --- a/qpid/python/tests/codec.py +++ b/qpid/python/tests/codec.py @@ -23,6 +23,7 @@ from qpid.codec import Codec from qpid.spec import load from cStringIO import StringIO from qpid.reference import ReferenceId +from qpid.testlib import testrunner __doc__ = """ @@ -53,7 +54,7 @@ __doc__ = """ """ -SPEC = load("../specs/amqp.0-10-preview.xml") +SPEC = load(testrunner.get_spec_file("amqp.0-10-preview.xml")) # -------------------------------------- # -------------------------------------- diff --git a/qpid/python/tests/spec.py b/qpid/python/tests/spec.py index c70e4f9c4b..c00faad3ba 100644 --- a/qpid/python/tests/spec.py +++ b/qpid/python/tests/spec.py @@ -1,11 +1,11 @@ from unittest import TestCase from qpid.spec import load - +from qpid.testlib import testrunner class SpecTest(TestCase): def check_load(self, *urls): - spec = load(*urls) + spec = load(*map(testrunner.get_spec_file, urls)) qdecl = spec.method("queue_declare") assert qdecl != None assert not qdecl.content @@ -44,13 +44,13 @@ class SpecTest(TestCase): assert args.type == "table" def test_load_0_8(self): - self.check_load("../specs/amqp.0-8.xml") + self.check_load("amqp.0-8.xml") def test_load_0_9(self): - self.check_load("../specs/amqp.0-9.xml") + self.check_load("amqp.0-9.xml") def test_load_0_9_errata(self): - self.check_load("../specs/amqp.0-9.xml", "../specs/amqp-errata.0-9.xml") + self.check_load("amqp.0-9.xml", "amqp-errata.0-9.xml") def test_load_0_10(self): - self.check_load("../specs/amqp.0-10-preview.xml") + self.check_load("amqp.0-10-preview.xml") -- cgit v1.2.1 From 87731c6669fd153ba20e889f32da9283b945fa90 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 21 Aug 2007 11:36:10 +0000 Subject: made codec.py load the spec file on demand git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@568074 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/codec.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py index f9f7aab918..fbaf05fc20 100644 --- a/qpid/python/tests/codec.py +++ b/qpid/python/tests/codec.py @@ -54,7 +54,13 @@ __doc__ = """ """ -SPEC = load(testrunner.get_spec_file("amqp.0-10-preview.xml")) +SPEC = None + +def spec(): + global SPEC + if SPEC == None: + SPEC = load(testrunner.get_spec_file("amqp.0-10-preview.xml")) + return SPEC # -------------------------------------- # -------------------------------------- @@ -70,7 +76,7 @@ class BaseDataTypes(unittest.TestCase): """ standard setUp for unitetest (refer unittest documentation for details) """ - self.codec = Codec(StringIO(), SPEC) + self.codec = Codec(StringIO(), spec()) # ------------------ def tearDown(self): @@ -508,7 +514,7 @@ def test(type, value): else: values = [value] stream = StringIO() - codec = Codec(stream, SPEC) + codec = Codec(stream, spec()) for v in values: codec.encode(type, v) codec.flush() -- cgit v1.2.1 From 849e954106e5cd789c9d57ccbdc32349762fdc5a Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 21 Aug 2007 15:51:41 +0000 Subject: Refresh of transitional xml to more closely reflect latest specification Initial execution-result support (not yet handled on c++ client) Generation is now all done through the ruby code (it is a little slower at present I'm afraid, will try to speed it up over the next weeks) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@568174 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 12 +++++- qpid/python/qpid/testlib.py | 4 +- qpid/python/tests_0-10/alternate-exchange.py | 8 ++-- qpid/python/tests_0-10/broker.py | 10 ++--- qpid/python/tests_0-10/dtx.py | 6 +-- qpid/python/tests_0-10/example.py | 2 +- qpid/python/tests_0-10/message.py | 64 ++++++++++++++-------------- qpid/python/tests_0-10/query.py | 15 ++++--- qpid/python/tests_0-10/queue.py | 27 ++++++------ qpid/python/tests_0-10/tx.py | 12 +++--- 10 files changed, 85 insertions(+), 75 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 6762f774f4..6ad5482f09 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -208,6 +208,8 @@ class Channel: self.responses.close() self.completion.close() self.incoming_completion.reset() + for f in self.futures.values(): + f.put_response(self, reason) def write(self, frame, content = None): if self.closed: @@ -324,7 +326,10 @@ class Channel: raise ValueError(resp) elif frame.method.result: if self.synchronous: - return future.get_response(timeout=10) + fr = future.get_response(timeout=10) + if self.closed: + raise Closed(self.reason) + return fr else: return future elif self.synchronous and not frame.method.response \ @@ -373,7 +378,10 @@ class Future: def get_response(self, timeout=None): self.completed.wait(timeout) - return self.response + if self.completed.isSet(): + return self.response + else: + return None def is_complete(self): return self.completed.isSet() diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 8a3abf840b..6820ae3bae 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -228,7 +228,7 @@ class TestBase(unittest.TestCase): def queue_declare(self, channel=None, *args, **keys): channel = channel or self.channel reply = channel.queue_declare(*args, **keys) - self.queues.append((channel, reply.queue)) + self.queues.append((channel, keys["queue"])) return reply def exchange_declare(self, channel=None, ticket=0, exchange='', @@ -254,7 +254,7 @@ class TestBase(unittest.TestCase): if not "uniqueTag" in dir(self): self.uniqueTag = 1 else: self.uniqueTag += 1 consumer_tag = "tag" + str(self.uniqueTag) - self.channel.message_consume(queue=queueName, destination=consumer_tag, no_ack=True) + self.channel.message_subscribe(queue=queueName, destination=consumer_tag) return self.client.queue(consumer_tag) def assertEmpty(self, queue): diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py index 19405a1c9f..a1c6151fca 100644 --- a/qpid/python/tests_0-10/alternate-exchange.py +++ b/qpid/python/tests_0-10/alternate-exchange.py @@ -39,13 +39,13 @@ class AlternateExchangeTests(TestBase): #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages channel.queue_declare(queue="returns", exclusive=True) channel.queue_bind(queue="returns", exchange="secondary") - channel.message_consume(destination="a", queue="returns") + channel.message_subscribe(destination="a", queue="returns") returned = self.client.queue("a") #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages channel.queue_declare(queue="processed", exclusive=True) channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") - channel.message_consume(destination="b", queue="processed") + channel.message_subscribe(destination="b", queue="processed") processed = self.client.queue("b") #publish to the primary exchange @@ -73,7 +73,7 @@ class AlternateExchangeTests(TestBase): channel.exchange_declare(exchange="dlq", type="fanout") channel.queue_declare(queue="deleted", exclusive=True) channel.queue_bind(exchange="dlq", queue="deleted") - channel.message_consume(destination="dlq", queue="deleted") + channel.message_subscribe(destination="dlq", queue="deleted") dlq = self.client.queue("dlq") #create a queue using the dlq as its alternate exchange: @@ -103,7 +103,7 @@ class AlternateExchangeTests(TestBase): channel.exchange_declare(exchange="dlq", type="fanout") channel.queue_declare(queue="immediate", exclusive=True) channel.queue_bind(exchange="dlq", queue="immediate") - channel.message_consume(destination="dlq", queue="immediate") + channel.message_subscribe(destination="dlq", queue="immediate") dlq = self.client.queue("dlq") #create a queue using the dlq as its alternate exchange: diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 6bc2f7ceb8..647f5d4fa5 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -35,7 +35,7 @@ class BrokerTests(TestBase): # No ack consumer ctag = "tag1" - ch.message_consume(queue = "myqueue", destination = ctag, no_ack = True) + ch.message_subscribe(queue = "myqueue", destination = ctag, confirm_mode = 0) body = "test no-ack" ch.message_transfer(routing_key = "myqueue", body = body) msg = self.client.queue(ctag).get(timeout = 5) @@ -44,7 +44,7 @@ class BrokerTests(TestBase): # Acknowledging consumer self.queue_declare(ch, queue = "otherqueue") ctag = "tag2" - ch.message_consume(queue = "otherqueue", destination = ctag, no_ack = False) + ch.message_subscribe(queue = "otherqueue", destination = ctag, confirm_mode = 1) body = "test ack" ch.message_transfer(routing_key = "otherqueue", body = body) msg = self.client.queue(ctag).get(timeout = 5) @@ -60,7 +60,7 @@ class BrokerTests(TestBase): self.queue_declare(channel, queue="test-queue") channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") consumer_tag = "tag1" - channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + channel.message_subscribe(queue="test-queue", destination=consumer_tag, confirm_mode = 0) queue = self.client.queue(consumer_tag) body = "Immediate Delivery" @@ -84,7 +84,7 @@ class BrokerTests(TestBase): channel.message_transfer(destination="test-exchange", routing_key="key", body=body) consumer_tag = "tag1" - channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True) + channel.message_subscribe(queue="test-queue", destination=consumer_tag, confirm_mode = 0) queue = self.client.queue(consumer_tag) msg = queue.get(timeout=5) self.assert_(msg.body == body) @@ -111,7 +111,7 @@ class BrokerTests(TestBase): def test_channel_flow(self): channel = self.channel channel.queue_declare(queue="flow_test_queue", exclusive=True) - channel.message_consume(destination="my-tag", queue="flow_test_queue") + channel.message_subscribe(destination="my-tag", queue="flow_test_queue") incoming = self.client.queue("my-tag") channel.channel_flow(active=False) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 2835d703ae..a5b53ac65b 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -366,7 +366,7 @@ class DtxTests(TestBase): #check the second message is available, but not the first self.assertMessageCount(1, "tx-queue") - channel.message_consume(queue="tx-queue", destination="results", no_ack=False) + channel.message_subscribe(queue="tx-queue", destination="results", confirm_mode=1) msg = self.client.queue("results").get(timeout=1) self.assertEqual("two", msg.message_id) channel.message_cancel(destination="results") @@ -597,9 +597,9 @@ class DtxTests(TestBase): channel.message_transfer(routing_key=dest, message_id=msg.message_id, body=msg.body) def assertMessageCount(self, expected, queue): - self.assertEqual(expected, self.channel.queue_declare(queue=queue, passive=True).message_count) + self.assertEqual(expected, self.channel.queue_query(queue=queue).message_count) def assertMessageId(self, expected, queue): - self.channel.message_consume(queue=queue, destination="results", no_ack=True) + self.channel.message_subscribe(queue=queue, destination="results") self.assertEqual(expected, self.client.queue("results").get(timeout=1).message_id) self.channel.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index dc71b0590b..e4c80951ac 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -68,7 +68,7 @@ class ExampleTest (TestBase): # has fields corresponding to the reply method fields, plus a content # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. - channel.message_consume(queue="test-queue", destination="consumer_tag") + channel.message_subscribe(queue="test-queue", destination="consumer_tag") # We can use the Client.queue(...) method to access the queue # corresponding to our consumer_tag. diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index b882cd5438..6cf2f3ef89 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -34,8 +34,8 @@ class MessageTests(TestBase): channel.queue_declare(queue="test-queue-1a", exclusive=True) channel.queue_declare(queue="test-queue-1b", exclusive=True) #establish two consumers one of which excludes delivery of locally sent messages - channel.message_consume(destination="local_included", queue="test-queue-1a") - channel.message_consume(destination="local_excluded", queue="test-queue-1b", no_local=True) + channel.message_subscribe(destination="local_included", queue="test-queue-1a") + channel.message_subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) #send a message channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local") @@ -61,9 +61,9 @@ class MessageTests(TestBase): channel.queue_declare(queue="test-queue-2", exclusive=True) #check that an exclusive consumer prevents other consumer being created: - channel.message_consume(destination="first", queue="test-queue-2", exclusive=True) + channel.message_subscribe(destination="first", queue="test-queue-2", exclusive=True) try: - channel.message_consume(destination="second", queue="test-queue-2") + channel.message_subscribe(destination="second", queue="test-queue-2") self.fail("Expected consume request to fail due to previous exclusive consumer") except Closed, e: self.assertChannelException(403, e.args[0]) @@ -73,9 +73,9 @@ class MessageTests(TestBase): channel.channel_open() #check that an exclusive consumer cannot be created if a consumer already exists: - channel.message_consume(destination="first", queue="test-queue-2") + channel.message_subscribe(destination="first", queue="test-queue-2") try: - channel.message_consume(destination="second", queue="test-queue-2", exclusive=True) + channel.message_subscribe(destination="second", queue="test-queue-2", exclusive=True) self.fail("Expected exclusive consume request to fail due to previous consumer") except Closed, e: self.assertChannelException(403, e.args[0]) @@ -87,7 +87,7 @@ class MessageTests(TestBase): channel = self.channel try: #queue specified but doesn't exist: - channel.message_consume(queue="invalid-queue") + channel.message_subscribe(queue="invalid-queue") self.fail("Expected failure when consuming from non-existent queue") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -96,7 +96,7 @@ class MessageTests(TestBase): channel.channel_open() try: #queue not specified and none previously declared for channel: - channel.message_consume(queue="") + channel.message_subscribe(queue="") self.fail("Expected failure when consuming from unspecified queue") except Closed, e: self.assertConnectionException(530, e.args[0]) @@ -110,9 +110,9 @@ class MessageTests(TestBase): channel.queue_declare(queue="test-queue-3", exclusive=True) #check that attempts to use duplicate tags are detected and prevented: - channel.message_consume(destination="first", queue="test-queue-3") + channel.message_subscribe(destination="first", queue="test-queue-3") try: - channel.message_consume(destination="first", queue="test-queue-3") + channel.message_subscribe(destination="first", queue="test-queue-3") self.fail("Expected consume request to fail due to non-unique tag") except Closed, e: self.assertConnectionException(530, e.args[0]) @@ -124,7 +124,7 @@ class MessageTests(TestBase): channel = self.channel #setup, declare a queue: channel.queue_declare(queue="test-queue-4", exclusive=True) - channel.message_consume(destination="my-consumer", queue="test-queue-4") + channel.message_subscribe(destination="my-consumer", queue="test-queue-4") channel.message_transfer(routing_key="test-queue-4", body="One") #cancel should stop messages being delivered @@ -150,7 +150,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue="test-ack-queue", exclusive=True) - channel.message_consume(queue="test-ack-queue", destination="consumer_tag", no_ack=False) + channel.message_subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") channel.message_transfer(routing_key="test-ack-queue", body="One") @@ -194,7 +194,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue="test-requeue", exclusive=True) - channel.message_consume(queue="test-requeue", destination="consumer_tag", no_ack=False) + channel.message_subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") channel.message_transfer(routing_key="test-requeue", body="One") @@ -225,7 +225,7 @@ class MessageTests(TestBase): #requeue unacked messages (Three and Five) channel.message_recover(requeue=True) - channel.message_consume(queue="test-requeue", destination="consumer_tag") + channel.message_subscribe(queue="test-requeue", destination="consumer_tag") queue2 = self.client.queue("consumer_tag") msg3b = queue2.get(timeout=1) @@ -256,7 +256,7 @@ class MessageTests(TestBase): #setup: declare queue and subscribe channel = self.channel channel.queue_declare(queue="test-prefetch-count", exclusive=True) - subscription = channel.message_consume(queue="test-prefetch-count", destination="consumer_tag", no_ack=False) + subscription = channel.message_subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") #set prefetch to 5: @@ -298,7 +298,7 @@ class MessageTests(TestBase): #setup: declare queue and subscribe channel = self.channel channel.queue_declare(queue="test-prefetch-size", exclusive=True) - subscription = channel.message_consume(queue="test-prefetch-size", destination="consumer_tag", no_ack=False) + subscription = channel.message_subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") #set prefetch to 50 bytes (each message is 9 or 10 bytes): @@ -362,13 +362,13 @@ class MessageTests(TestBase): self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "empty") - #repeat for no_ack=False + #repeat for confirm_mode=1 for i in range(11, 21): channel.message_transfer(routing_key="test-get", body="Message %d" % i) for i in range(11, 21): tag = "queue %d" % i - reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) + reply = channel.message_get(confirm_mode=1, queue="test-get", destination=tag) self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "ok") msg = self.client.queue(tag).get(timeout=1) @@ -389,7 +389,7 @@ class MessageTests(TestBase): #get the unacked messages again (14, 16, 18, 20) for i in [14, 16, 18, 20]: tag = "queue %d" % i - reply = channel.message_get(no_ack=False, queue="test-get", destination=tag) + reply = channel.message_get(confirm_mode=1, queue="test-get", destination=tag) self.assertEqual(reply.method.klass.name, "message") self.assertEqual(reply.method.name, "ok") msg = self.client.queue(tag).get(timeout=1) @@ -412,7 +412,7 @@ class MessageTests(TestBase): """ channel = self.channel channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_consume(queue="ref_queue", destination="c1") + channel.message_subscribe(queue="ref_queue", destination="c1") queue = self.client.queue("c1") refId = "myref" @@ -454,7 +454,7 @@ class MessageTests(TestBase): other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0}) ch2 = other.channel(1) ch2.channel_open() - ch2.message_consume(queue="ref_queue", destination="c1") + ch2.message_subscribe(queue="ref_queue", destination="c1") queue = other.queue("c1") msg = queue.get(timeout=1) @@ -469,7 +469,7 @@ class MessageTests(TestBase): """ channel = self.channel channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_consume(queue="ref_queue", destination="c1") + channel.message_subscribe(queue="ref_queue", destination="c1") queue = self.client.queue("c1") refId = "myref" @@ -502,8 +502,8 @@ class MessageTests(TestBase): #declare and consume from two queues channel.queue_declare(queue="q-one", exclusive=True) channel.queue_declare(queue="q-two", exclusive=True) - channel.message_consume(queue="q-one", destination="q-one") - channel.message_consume(queue="q-two", destination="q-two") + channel.message_subscribe(queue="q-one", destination="q-one") + channel.message_subscribe(queue="q-two", destination="q-two") queue1 = self.client.queue("q-one") queue2 = self.client.queue("q-two") @@ -590,7 +590,7 @@ class MessageTests(TestBase): def test_empty_reference(self): channel = self.channel channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_consume(queue="ref_queue", destination="c1") + channel.message_subscribe(queue="ref_queue", destination="c1") queue = self.client.queue("c1") refId = "myref" @@ -611,14 +611,14 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue = "q", exclusive=True) - channel.message_consume(queue = "q", destination = "consumer") + channel.message_subscribe(queue = "q", destination = "consumer") channel.message_transfer(routing_key = "q", body="blah, blah") msg = self.client.queue("consumer").get(timeout = 1) self.assertEquals(msg.body, "blah, blah") channel.message_cancel(destination = "consumer") msg.reject() - channel.message_consume(queue = "q", destination = "checker") + channel.message_subscribe(queue = "q", destination = "checker") msg = self.client.queue("checker").get(timeout = 1) self.assertEquals(msg.body, "blah, blah") @@ -634,7 +634,7 @@ class MessageTests(TestBase): channel = self.client.channel(2) channel.channel_open() - channel.message_consume(queue = "q", destination = "consumer") + channel.message_subscribe(queue = "q", destination = "consumer") offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value self.assertTrue(offset<=16) channel.message_append(reference="my-ref", bytes="qrstuvwxyz") @@ -654,7 +654,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue = "q", exclusive=True) #create consumer (for now that defaults to infinite credit) - channel.message_consume(queue = "q", destination = "c") + channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") #set credit to zero (can remove this once move to proper default for subscribe method) channel.message_stop(destination = "c") @@ -686,7 +686,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue = "q", exclusive=True) #create consumer (for now that defaults to infinite credit) - channel.message_consume(queue = "q", destination = "c") + channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") #set credit to zero (can remove this once move to proper default for subscribe method) channel.message_stop(destination = "c") @@ -720,7 +720,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue = "q", exclusive=True) #create consumer (for now that defaults to infinite credit) - channel.message_consume(queue = "q", destination = "c") + channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") #set credit to zero (can remove this once move to proper default for subscribe method) channel.message_stop(destination = "c") @@ -754,7 +754,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue = "q", exclusive=True) #create consumer (for now that defaults to infinite credit) - channel.message_consume(queue = "q", destination = "c") + channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") #set credit to zero (can remove this once move to proper default for subscribe method) channel.message_stop(destination = "c") diff --git a/qpid/python/tests_0-10/query.py b/qpid/python/tests_0-10/query.py index c2e08c003c..06f33be85b 100644 --- a/qpid/python/tests_0-10/query.py +++ b/qpid/python/tests_0-10/query.py @@ -30,16 +30,16 @@ class QueryTests(TestBase): """ channel = self.channel #check returned type for the standard exchanges - self.assertEqual("direct", channel.exchange_query(name="amq.direct").type) - self.assertEqual("topic", channel.exchange_query(name="amq.topic").type) - self.assertEqual("fanout", channel.exchange_query(name="amq.fanout").type) - self.assertEqual("headers", channel.exchange_query(name="amq.match").type) - self.assertEqual("direct", channel.exchange_query(name="").type) + self.assert_type("direct", channel.exchange_query(name="amq.direct")) + self.assert_type("topic", channel.exchange_query(name="amq.topic")) + self.assert_type("fanout", channel.exchange_query(name="amq.fanout")) + self.assert_type("headers", channel.exchange_query(name="amq.match")) + self.assert_type("direct", channel.exchange_query(name="")) #declare an exchange channel.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) #check that the result of a query is as expected response = channel.exchange_query(name="my-test-exchange") - self.assertEqual("direct", response.type) + self.assert_type("direct", response) self.assertEqual(False, response.durable) self.assertEqual(False, response.not_found) #delete the exchange @@ -47,6 +47,9 @@ class QueryTests(TestBase): #check that the query now reports not-found self.assertEqual(True, channel.exchange_query(name="my-test-exchange").not_found) + def assert_type(self, expected_type, response): + self.assertEqual(expected_type, response.__getattr__("type")) + def test_binding_query_direct(self): """ Test that the binding_query method works as expected with the direct exchange diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index e7fe0b3ed4..8d99c50d32 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -38,18 +38,18 @@ class QueueTests(TestBase): channel.message_transfer(destination="test-exchange", routing_key="key", body="three") #check that the queue now reports 3 messages: - reply = channel.queue_declare(queue="test-queue") + channel.queue_declare(queue="test-queue") + reply = channel.queue_query(queue="test-queue") self.assertEqual(3, reply.message_count) #now do the purge, then test that three messages are purged and the count drops to 0 - reply = channel.queue_purge(queue="test-queue"); - self.assertEqual(3, reply.message_count) - reply = channel.queue_declare(queue="test-queue") - self.assertEqual(0, reply.message_count) + channel.queue_purge(queue="test-queue"); + reply = channel.queue_query(queue="test-queue") + self.assertEqual(0, reply.message_count) #send a further message and consume it, ensuring that the other messages are really gone channel.message_transfer(destination="test-exchange", routing_key="key", body="four") - channel.message_consume(queue="test-queue", destination="tag", no_ack=True) + channel.message_subscribe(queue="test-queue", destination="tag") queue = self.client.queue("tag") msg = queue.get(timeout=1) self.assertEqual("four", msg.body) @@ -169,8 +169,8 @@ class QueueTests(TestBase): channel.queue_declare(queue="queue-1", exclusive="True") channel.queue_declare(queue="queue-2", exclusive="True") - channel.message_consume(queue="queue-1", destination="queue-1", no_ack=True) - channel.message_consume(queue="queue-2", destination="queue-2", no_ack=True) + channel.message_subscribe(queue="queue-1", destination="queue-1") + channel.message_subscribe(queue="queue-2", destination="queue-2") queue1 = self.client.queue("queue-1") queue2 = self.client.queue("queue-2") @@ -213,8 +213,7 @@ class QueueTests(TestBase): channel.message_transfer(routing_key="delete-me", body="a") channel.message_transfer(routing_key="delete-me", body="b") channel.message_transfer(routing_key="delete-me", body="c") - reply = channel.queue_delete(queue="delete-me") - self.assertEqual(3, reply.message_count) + channel.queue_delete(queue="delete-me") #check that it has gone be declaring passively try: channel.queue_declare(queue="delete-me", passive="True") @@ -256,7 +255,7 @@ class QueueTests(TestBase): channel.channel_open() #empty queue: - channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True) + channel.message_subscribe(destination="consumer_tag", queue="delete-me-2") queue = self.client.queue("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.body) @@ -281,7 +280,7 @@ class QueueTests(TestBase): #create a queue and register a consumer: channel.queue_declare(queue="delete-me-3") channel.queue_declare(queue="delete-me-3", passive="True") - channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True) + channel.message_subscribe(destination="consumer_tag", queue="delete-me-3") #need new channel now: channel2 = self.client.channel(2) @@ -316,8 +315,8 @@ class QueueTests(TestBase): channel.queue_declare(queue="auto-delete-me", auto_delete=True) #consume from both channels - reply = channel.basic_consume(queue="auto-delete-me", no_ack=True) - channel2.basic_consume(queue="auto-delete-me", no_ack=True) + reply = channel.basic_consume(queue="auto-delete-me") + channel2.basic_consume(queue="auto-delete-me") #implicit cancel channel2.channel_close() diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index b499c2d1f9..4c2f75d35e 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -41,13 +41,13 @@ class TxTests(TestBase): channel = self.channel channel.tx_select() - channel.message_consume(queue="tx-commit-a", destination="qa", no_ack=False) + channel.message_subscribe(queue="tx-commit-a", destination="qa", confirm_mode=1) queue_a = self.client.queue("qa") - channel.message_consume(queue="tx-commit-b", destination="qb", no_ack=False) + channel.message_subscribe(queue="tx-commit-b", destination="qb", confirm_mode=1) queue_b = self.client.queue("qb") - channel.message_consume(queue="tx-commit-c", destination="qc", no_ack=False) + channel.message_subscribe(queue="tx-commit-c", destination="qc", confirm_mode=1) queue_c = self.client.queue("qc") #check results @@ -174,7 +174,7 @@ class TxTests(TestBase): channel.tx_select() #consume and ack messages - channel.message_consume(queue=name_a, destination="sub_a", no_ack=False) + channel.message_subscribe(queue=name_a, destination="sub_a", confirm_mode=1) queue_a = self.client.queue("sub_a") for i in range(1, 5): msg = queue_a.get(timeout=1) @@ -182,13 +182,13 @@ class TxTests(TestBase): msg.complete() - channel.message_consume(queue=name_b, destination="sub_b", no_ack=False) + channel.message_subscribe(queue=name_b, destination="sub_b", confirm_mode=1) queue_b = self.client.queue("sub_b") msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) msg.complete() - sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False) + sub_c = channel.message_subscribe(queue=name_c, destination="sub_c", confirm_mode=1) queue_c = self.client.queue("sub_c") msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) -- cgit v1.2.1 From 8e97302911b515502f0a85169da43d4a33b54cf9 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 21 Aug 2007 20:01:51 +0000 Subject: made -i and -I support pattern matches git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@568248 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 6820ae3bae..c06a252f16 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -24,6 +24,7 @@ import sys, re, unittest, os, random, logging import qpid.client, qpid.spec import Queue +from fnmatch import fnmatch from getopt import getopt, GetoptError from qpid.content import Content from qpid.message import Message @@ -148,8 +149,10 @@ Options: def testSuite(self): class IgnoringTestSuite(unittest.TestSuite): def addTest(self, test): - if isinstance(test, unittest.TestCase) and test.id() in testrunner.ignore: - return + if isinstance(test, unittest.TestCase): + for pattern in testrunner.ignore: + if fnmatch(test.id(), pattern): + return unittest.TestSuite.addTest(self, test) # Use our IgnoringTestSuite in the test loader. -- cgit v1.2.1 From 5b686d223970bc51d38440b03b901d97f9b93412 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 22 Aug 2007 13:39:04 +0000 Subject: added support for 0-10 style header encoding git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@568607 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 5 ++-- qpid/python/qpid/__init__.py | 19 ++++++++++++-- qpid/python/qpid/codec.py | 36 ++++++++++++++++++-------- qpid/python/qpid/connection.py | 58 +++++++++++++++++++++++++++++++++++++++++- qpid/python/qpid/spec.py | 5 +++- qpid/python/qpid/testlib.py | 5 ++-- 6 files changed, 110 insertions(+), 18 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index 4fb065ae53..b3170c8e0c 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -11,5 +11,6 @@ ch.queue_declare(queue="test") ch.queue_bind(exchange="amq.direct", queue="test", routing_key="test") print ch.queue_query(queue="test") ch.message_subscribe(queue="test", destination="test") -ch.message_transfer(destination="amq.direct", - content=Content("hello world")) +msg = Content("hello world") +msg["content_type"] = "text/plain" +ch.message_transfer(destination="amq.direct", content=msg) diff --git a/qpid/python/qpid/__init__.py b/qpid/python/qpid/__init__.py index 4aeccae38e..3f6d82b89e 100644 --- a/qpid/python/qpid/__init__.py +++ b/qpid/python/qpid/__init__.py @@ -31,13 +31,28 @@ class Struct: raise AttributeError(attr) return field - def __setattr__(self, attr, value): + def has(self, name): + return self.type.fields.byname.has_key(name) + + def set(self, attr, value): self._check(attr) self._values[attr] = value - def __getattr__(self, attr): + def get(self, attr): field = self._check(attr) return self._values.get(attr, field.default()) + def __setattr__(self, attr, value): + self.set(attr, value) + + def __getattr__(self, attr): + return self.get(attr) + + def __setitem__(self, attr, value): + self.set(attr, value) + + def __getitem__(self, attr): + return self.get(attr) + def __str__(self): return "%s %s" % (self.type.type, self._values) diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 3920f2c8d9..ae113619ae 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -26,7 +26,7 @@ fields. The unit test for this module is located in tests/codec.py """ -import re, qpid +import re, qpid, spec from cStringIO import StringIO from struct import * from reference import ReferenceId @@ -113,13 +113,19 @@ class Codec: """ calls the appropriate encode function e.g. encode_octet, encode_short etc. """ - getattr(self, "encode_" + type)(value) + if isinstance(type, spec.Struct): + self.encode_struct(type, value) + else: + getattr(self, "encode_" + type)(value) def decode(self, type): """ calls the appropriate decode function e.g. decode_octet, decode_short etc. """ - return getattr(self, "decode_" + type)() + if isinstance(type, spec.Struct): + return self.decode_struct(type) + else: + return getattr(self, "decode_" + type)() def encode_bit(self, o): """ @@ -358,20 +364,30 @@ class Codec: def decode_uuid(self): return self.decode_longstr() + def encode_struct(self, type, s): + for f in type.fields: + if s == None: + val = f.default() + else: + val = s.get(f.name) + self.encode(f.type, val) + self.flush() + + def decode_struct(self, type): + s = qpid.Struct(type) + for f in type.fields: + s.set(f.name, self.decode(f.type)) + return s + def encode_long_struct(self, s): enc = StringIO() codec = Codec(enc, self.spec) type = s.type codec.encode_short(type.type) - for f in type.fields: - codec.encode(f.type, getattr(s, f.name)) - codec.flush() + codec.encode_struct(type, s) self.encode_longstr(enc.getvalue()) def decode_long_struct(self): codec = Codec(StringIO(self.decode_longstr()), self.spec) type = self.spec.structs[codec.decode_short()] - s = qpid.Struct(type) - for f in type.fields: - setattr(s, f.name, codec.decode(f.type)) - return s + return codec.decode_struct(type) diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 58235117ef..46b58e83b7 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -23,7 +23,7 @@ to read and write Frame objects. This could be used by a client, server, or even a proxy implementation. """ -import socket, codec,logging +import socket, codec, logging, qpid from cStringIO import StringIO from spec import load from codec import EOF @@ -238,6 +238,11 @@ class Response(Frame): def __str__(self): return "[%s] Response(%s,%s,%s) %s" % (self.channel, self.id, self.request_id, self.batch_offset, self.method) +def uses_struct_encoding(spec): + return (spec.major == 0 and + spec.minor == 10 and + "transitional" not in spec.file) + class Header(Frame): type = "frame_header" @@ -258,6 +263,33 @@ class Header(Frame): del self.properties[name] def encode(self, c): + if uses_struct_encoding(c.spec): + self.encode_structs(c) + else: + self.encode_legacy(c) + + def encode_structs(self, c): + # XXX + structs = [qpid.Struct(c.spec.domains.byname["delivery_properties"].type), + qpid.Struct(c.spec.domains.byname["message_properties"].type)] + + # XXX + props = self.properties.copy() + for k in self.properties: + for s in structs: + if s.has(k): + s.set(k, props.pop(k)) + if props: + raise TypeError("no such property: %s" % (", ".join(props))) + + # message properties store the content-length now, and weight is + # deprecated + structs[1].content_length = self.size + + for s in structs: + c.encode_long_struct(s) + + def encode_legacy(self, c): c.encode_short(self.klass.id) c.encode_short(self.weight) c.encode_longlong(self.size) @@ -287,6 +319,30 @@ class Header(Frame): c.encode(f.type, v) def decode(spec, c, size): + if uses_struct_encoding(spec): + return Header.decode_structs(spec, c, size) + else: + return Header.decode_legacy(spec, c, size) + + @staticmethod + def decode_structs(spec, c, size): + structs = [] + start = c.nread + while c.nread - start < size: + structs.append(c.decode_long_struct()) + + # XXX + props = {} + length = None + for s in structs: + for f in s.type.fields: + props[f.name] = s.get(f.name) + if f.name == "content_length": + length = s.get(f.name) + return Header(None, 0, length, props) + + @staticmethod + def decode_legacy(spec, c, size): klass = spec.classes.byid[c.decode_short()] weight = c.decode_short() size = c.decode_longlong() diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 8a511bcb3d..3febab7e09 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -298,7 +298,10 @@ class Field(Metadata): self.docs = docs def default(self): - return Method.DEFAULTS[self.type] + if isinstance(self.type, Struct): + return None + else: + return Method.DEFAULTS[self.type] def get_result(nd, spec): result = nd["result"] diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index c06a252f16..0b2a1b78d6 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -21,7 +21,7 @@ # Support library for qpid python tests. # -import sys, re, unittest, os, random, logging +import sys, re, unittest, os, random, logging, traceback import qpid.client, qpid.spec import Queue from fnmatch import fnmatch @@ -217,7 +217,8 @@ class TestBase(unittest.TestCase): for ch, ex in self.exchanges: ch.exchange_delete(exchange=ex) except: - print "Error on tearDown:", sys.exc_info() + print "Error on tearDown:" + print traceback.print_exc() if not self.client.closed: self.client.channel(0).connection_close(reply_code=200) -- cgit v1.2.1 From 237f40cf63857bfd1a2a9d036f2609c21cd02b76 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 22 Aug 2007 18:48:49 +0000 Subject: removed circular from imports git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@568726 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/dom.py | 4 ++-- qpid/python/mllib/transforms.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py index d95326d1a1..10b19d6db1 100644 --- a/qpid/python/mllib/dom.py +++ b/qpid/python/mllib/dom.py @@ -25,7 +25,7 @@ from __future__ import division from __future__ import generators from __future__ import nested_scopes -from transforms import Text +import transforms class Container: @@ -110,7 +110,7 @@ class Node(Container, Component, Dispatcher): return nd def text(self): - return self.dispatch(Text()) + return self.dispatch(transforms.Text()) def tag(self, name, *attrs, **kwargs): t = Tag(name, *attrs, **kwargs) diff --git a/qpid/python/mllib/transforms.py b/qpid/python/mllib/transforms.py index bb79dcf192..69d99125e3 100644 --- a/qpid/python/mllib/transforms.py +++ b/qpid/python/mllib/transforms.py @@ -21,8 +21,8 @@ Useful transforms for dom objects. """ +import dom from cStringIO import StringIO -from dom import * class Visitor: @@ -45,12 +45,12 @@ class Identity: return result def default(self, tag): - result = Tag(tag.name, *tag.attrs) + result = dom.Tag(tag.name, *tag.attrs) result.extend(self.descend(tag)) return result def tree(self, tree): - result = Tree() + result = dom.Tree() result.extend(self.descend(tree)) return result -- cgit v1.2.1 From 3ac27a0a7c7720d3b84a214cae80d4000650dfdc Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 23 Aug 2007 20:48:18 +0000 Subject: removed extraneous whitespace git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@569134 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-9/basic.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py index e7d22e00da..607ba26343 100644 --- a/qpid/python/tests_0-9/basic.py +++ b/qpid/python/tests_0-9/basic.py @@ -6,9 +6,9 @@ # 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 @@ -46,7 +46,7 @@ class BasicTests(TestBase): msg = included.get(timeout=1) self.assertEqual("consume_no_local", msg.content.body) try: - excluded.get(timeout=1) + excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") except Empty: None @@ -67,7 +67,7 @@ class BasicTests(TestBase): except Closed, e: self.assertChannelException(403, e.args[0]) - #open new channel and cleanup last consumer: + #open new channel and cleanup last consumer: channel = self.client.channel(2) channel.channel_open() @@ -129,12 +129,12 @@ class BasicTests(TestBase): myqueue = self.client.queue("my-consumer") msg = myqueue.get(timeout=1) self.assertEqual("One", msg.content.body) - + #cancel should stop messages being delivered channel.basic_cancel(consumer_tag="my-consumer") channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) try: - msg = myqueue.get(timeout=1) + msg = myqueue.get(timeout=1) self.fail("Got message after cancellation: " + msg) except Empty: None @@ -149,7 +149,7 @@ class BasicTests(TestBase): """ channel = self.channel channel.queue_declare(queue="test-ack-queue", exclusive=True) - + reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) queue = self.client.queue(reply.consumer_tag) @@ -158,13 +158,13 @@ class BasicTests(TestBase): channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) - + msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) msg3 = queue.get(timeout=1) msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - + self.assertEqual("One", msg1.content.body) self.assertEqual("Two", msg2.content.body) self.assertEqual("Three", msg3.content.body) @@ -175,10 +175,10 @@ class BasicTests(TestBase): channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four channel.basic_recover(requeue=False) - + msg3b = queue.get(timeout=1) msg5b = queue.get(timeout=1) - + self.assertEqual("Three", msg3b.content.body) self.assertEqual("Five", msg5b.content.body) @@ -193,7 +193,7 @@ class BasicTests(TestBase): """ channel = self.channel channel.queue_declare(queue="test-requeue", exclusive=True) - + subscription = channel.basic_consume(queue="test-requeue", no_ack=False) queue = self.client.queue(subscription.consumer_tag) @@ -202,13 +202,13 @@ class BasicTests(TestBase): channel.basic_publish(routing_key="test-requeue", content=Content("Three")) channel.basic_publish(routing_key="test-requeue", content=Content("Four")) channel.basic_publish(routing_key="test-requeue", content=Content("Five")) - + msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) msg3 = queue.get(timeout=1) msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - + self.assertEqual("One", msg1.content.body) self.assertEqual("Two", msg2.content.body) self.assertEqual("Three", msg3.content.body) @@ -224,10 +224,10 @@ class BasicTests(TestBase): subscription2 = channel.basic_consume(queue="test-requeue") queue2 = self.client.queue(subscription2.consumer_tag) - + msg3b = queue2.get(timeout=1) msg5b = queue2.get(timeout=1) - + self.assertEqual("Three", msg3b.content.body) self.assertEqual("Five", msg5b.content.body) @@ -242,8 +242,8 @@ class BasicTests(TestBase): extra = queue.get(timeout=1) self.fail("Got unexpected message in original queue: " + extra.content.body) except Empty: None - - + + def test_qos_prefetch_count(self): """ Test that the prefetch count specified is honoured @@ -285,7 +285,7 @@ class BasicTests(TestBase): except Empty: None - + def test_qos_prefetch_size(self): """ Test that the prefetch size specified is honoured @@ -340,7 +340,7 @@ class BasicTests(TestBase): """ channel = self.channel channel.queue_declare(queue="test-get", exclusive=True) - + #publish some messages (no_ack=True) for i in range(1, 11): channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) @@ -376,7 +376,7 @@ class BasicTests(TestBase): #recover(requeue=True) channel.basic_recover(requeue=True) - + #get the unacked messages again (14, 16, 18, 20) for i in [14, 16, 18, 20]: reply = channel.basic_get(no_ack=False) -- cgit v1.2.1 From b7b4a5407af61099567b8f1b3765cfada6150312 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 28 Aug 2007 19:38:17 +0000 Subject: Updated message.transfer encoding to use header and content segments (including new structs). Unified more between the basic and message classes messages. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@570538 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 13 +- qpid/python/qpid/client.py | 2 - qpid/python/qpid/connection.py | 4 +- qpid/python/qpid/testlib.py | 9 +- qpid/python/tests_0-10/alternate-exchange.py | 25 +- qpid/python/tests_0-10/basic.py | 396 -------------------------- qpid/python/tests_0-10/broker.py | 23 +- qpid/python/tests_0-10/dtx.py | 33 +-- qpid/python/tests_0-10/example.py | 9 +- qpid/python/tests_0-10/exchange.py | 12 +- qpid/python/tests_0-10/message.py | 410 ++++----------------------- qpid/python/tests_0-10/queue.py | 34 +-- qpid/python/tests_0-10/testlib.py | 2 +- qpid/python/tests_0-10/tx.py | 56 ++-- 14 files changed, 165 insertions(+), 863 deletions(-) delete mode 100644 qpid/python/tests_0-10/basic.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index e68f942d67..97cf420717 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,15 +1,4 @@ +tests_0-10.alternate-exchange.AlternateExchangeTests.test_immediate tests_0-10.message.MessageTests.test_reject tests_0-10.basic.BasicTests.test_get -tests_0-10.message.MessageTests.test_get -tests_0-10.message.MessageTests.test_checkpoint -tests_0-10.message.MessageTests.test_empty_reference -tests_0-10.message.MessageTests.test_reference_already_opened_error -tests_0-10.message.MessageTests.test_reference_completion -tests_0-10.message.MessageTests.test_reference_large -tests_0-10.message.MessageTests.test_reference_multi_transfer -tests_0-10.message.MessageTests.test_reference_simple -tests_0-10.message.MessageTests.test_reference_unopened_on_append_error -tests_0-10.message.MessageTests.test_reference_unopened_on_close_error -tests_0-10.message.MessageTests.test_reference_unopened_on_transfer_error - diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 3efd79c389..edcd1b8ad2 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -115,8 +115,6 @@ class ClientDelegate(Delegate): self.client.started.set() def message_transfer(self, ch, msg): - if isinstance(msg.body, ReferenceId): - msg.reference = ch.references.get(msg.body.id) self.client.queue(msg.destination).put(msg) def message_open(self, ch, msg): diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 46b58e83b7..39bcde17df 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -239,9 +239,7 @@ class Response(Frame): return "[%s] Response(%s,%s,%s) %s" % (self.channel, self.id, self.request_id, self.batch_offset, self.method) def uses_struct_encoding(spec): - return (spec.major == 0 and - spec.minor == 10 and - "transitional" not in spec.file) + return (spec.major == 0 and spec.minor == 10) class Header(Frame): diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 0b2a1b78d6..28c07ba43a 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -280,18 +280,17 @@ class TestBase(unittest.TestCase): routing_key=routing_key) else: self.channel.message_transfer( - destination=exchange, body=body, - application_headers=properties, - routing_key=routing_key) + destination=exchange, + content=Content(body, properties={'application_headers':properties,'routing_key':routing_key})) msg = queue.get(timeout=1) if testrunner.use08spec(): self.assertEqual(body, msg.content.body) if (properties): self.assertEqual(properties, msg.content.properties) else: - self.assertEqual(body, msg.body) + self.assertEqual(body, msg.content.body) if (properties): - self.assertEqual(properties, msg.application_headers) + self.assertEqual(properties, msg.content['application_headers']) def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): """ diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py index a1c6151fca..d6ac62ccfe 100644 --- a/qpid/python/tests_0-10/alternate-exchange.py +++ b/qpid/python/tests_0-10/alternate-exchange.py @@ -50,17 +50,17 @@ class AlternateExchangeTests(TestBase): #publish to the primary exchange #...one message that makes it to the 'processed' queue: - channel.message_transfer(destination="primary", routing_key="my-key", body="Good") + channel.message_transfer(destination="primary", content=Content("Good", properties={'routing_key':"my-key"})) #...and one that does not: - channel.message_transfer(destination="primary", routing_key="unused-key", body="Bad") + channel.message_transfer(destination="primary", content=Content("Bad", properties={'routing_key':"unused-key"})) #delete the exchanges channel.exchange_delete(exchange="primary") channel.exchange_delete(exchange="secondary") #verify behaviour - self.assertEqual("Good", processed.get(timeout=1).body) - self.assertEqual("Bad", returned.get(timeout=1).body) + self.assertEqual("Good", processed.get(timeout=1).content.body) + self.assertEqual("Bad", returned.get(timeout=1).content.body) self.assertEmpty(processed) self.assertEmpty(returned) @@ -79,18 +79,18 @@ class AlternateExchangeTests(TestBase): #create a queue using the dlq as its alternate exchange: channel.queue_declare(queue="delete-me", alternate_exchange="dlq") #send it some messages: - channel.message_transfer(routing_key="delete-me", body="One") - channel.message_transfer(routing_key="delete-me", body="Two") - channel.message_transfer(routing_key="delete-me", body="Three") + channel.message_transfer(content=Content("One", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("Two", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("Three", properties={'routing_key':"delete-me"})) #delete it: channel.queue_delete(queue="delete-me") #delete the dlq exchange: channel.exchange_delete(exchange="dlq") #check the messages were delivered to the dlq: - self.assertEqual("One", dlq.get(timeout=1).body) - self.assertEqual("Two", dlq.get(timeout=1).body) - self.assertEqual("Three", dlq.get(timeout=1).body) + self.assertEqual("One", dlq.get(timeout=1).content.body) + self.assertEqual("Two", dlq.get(timeout=1).content.body) + self.assertEqual("Three", dlq.get(timeout=1).content.body) self.assertEmpty(dlq) @@ -109,10 +109,11 @@ class AlternateExchangeTests(TestBase): #create a queue using the dlq as its alternate exchange: channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True) #send it some messages: - channel.message_transfer(routing_key="no-consumers", body="no one wants me", immediate=True) + #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK + channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) #check the messages were delivered to the dlq: - self.assertEqual("no one wants me", dlq.get(timeout=1).body) + self.assertEqual("no one wants me", dlq.get(timeout=1).content.body) self.assertEmpty(dlq) #cleanup: diff --git a/qpid/python/tests_0-10/basic.py b/qpid/python/tests_0-10/basic.py deleted file mode 100644 index e7d22e00da..0000000000 --- a/qpid/python/tests_0-10/basic.py +++ /dev/null @@ -1,396 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class BasicTests(TestBase): - """Tests for 'methods' on the amqp basic 'class'""" - - def test_consume_no_local(self): - """ - Test that the no_local flag is honoured in the consume method - """ - channel = self.channel - #setup, declare two queues: - channel.queue_declare(queue="test-queue-1a", exclusive=True) - channel.queue_declare(queue="test-queue-1b", exclusive=True) - #establish two consumers one of which excludes delivery of locally sent messages - channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a") - channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True) - - #send a message - channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local")) - channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local")) - - #check the queues of the two consumers - excluded = self.client.queue("local_excluded") - included = self.client.queue("local_included") - msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.content.body) - try: - excluded.get(timeout=1) - self.fail("Received locally published message though no_local=true") - except Empty: None - - - def test_consume_exclusive(self): - """ - Test that the exclusive flag is honoured in the consume method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-2", exclusive=True) - - #check that an exclusive consumer prevents other consumer being created: - channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True) - try: - channel.basic_consume(consumer_tag="second", queue="test-queue-2") - self.fail("Expected consume request to fail due to previous exclusive consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - #open new channel and cleanup last consumer: - channel = self.client.channel(2) - channel.channel_open() - - #check that an exclusive consumer cannot be created if a consumer already exists: - channel.basic_consume(consumer_tag="first", queue="test-queue-2") - try: - channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True) - self.fail("Expected exclusive consume request to fail due to previous consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - def test_consume_queue_errors(self): - """ - Test error conditions associated with the queue field of the consume method: - """ - channel = self.channel - try: - #queue specified but doesn't exist: - channel.basic_consume(queue="invalid-queue") - self.fail("Expected failure when consuming from non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - channel = self.client.channel(2) - channel.channel_open() - try: - #queue not specified and none previously declared for channel: - channel.basic_consume(queue="") - self.fail("Expected failure when consuming from unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_consume_unique_consumers(self): - """ - Ensure unique consumer tags are enforced - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-3", exclusive=True) - - #check that attempts to use duplicate tags are detected and prevented: - channel.basic_consume(consumer_tag="first", queue="test-queue-3") - try: - channel.basic_consume(consumer_tag="first", queue="test-queue-3") - self.fail("Expected consume request to fail due to non-unique tag") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_cancel(self): - """ - Test compliance of the basic.cancel method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-4", exclusive=True) - channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4") - channel.basic_publish(routing_key="test-queue-4", content=Content("One")) - - myqueue = self.client.queue("my-consumer") - msg = myqueue.get(timeout=1) - self.assertEqual("One", msg.content.body) - - #cancel should stop messages being delivered - channel.basic_cancel(consumer_tag="my-consumer") - channel.basic_publish(routing_key="test-queue-4", content=Content("Two")) - try: - msg = myqueue.get(timeout=1) - self.fail("Got message after cancellation: " + msg) - except Empty: None - - #cancellation of non-existant consumers should be handled without error - channel.basic_cancel(consumer_tag="my-consumer") - channel.basic_cancel(consumer_tag="this-never-existed") - - - def test_ack(self): - """ - Test basic ack/recover behaviour - """ - channel = self.channel - channel.queue_declare(queue="test-ack-queue", exclusive=True) - - reply = channel.basic_consume(queue="test-ack-queue", no_ack=False) - queue = self.client.queue(reply.consumer_tag) - - channel.basic_publish(routing_key="test-ack-queue", content=Content("One")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Two")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Three")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Four")) - channel.basic_publish(routing_key="test-ack-queue", content=Content("Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four - - channel.basic_recover(requeue=False) - - msg3b = queue.get(timeout=1) - msg5b = queue.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - def test_recover_requeue(self): - """ - Test requeing on recovery - """ - channel = self.channel - channel.queue_declare(queue="test-requeue", exclusive=True) - - subscription = channel.basic_consume(queue="test-requeue", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) - - channel.basic_publish(routing_key="test-requeue", content=Content("One")) - channel.basic_publish(routing_key="test-requeue", content=Content("Two")) - channel.basic_publish(routing_key="test-requeue", content=Content("Three")) - channel.basic_publish(routing_key="test-requeue", content=Content("Four")) - channel.basic_publish(routing_key="test-requeue", content=Content("Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two - channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four - - channel.basic_cancel(consumer_tag=subscription.consumer_tag) - - channel.basic_recover(requeue=True) - - subscription2 = channel.basic_consume(queue="test-requeue") - queue2 = self.client.queue(subscription2.consumer_tag) - - msg3b = queue2.get(timeout=1) - msg5b = queue2.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - self.assertEqual(True, msg3b.redelivered) - self.assertEqual(True, msg5b.redelivered) - - try: - extra = queue2.get(timeout=1) - self.fail("Got unexpected message in second queue: " + extra.content.body) - except Empty: None - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message in original queue: " + extra.content.body) - except Empty: None - - - def test_qos_prefetch_count(self): - """ - Test that the prefetch count specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-count", exclusive=True) - subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) - - #set prefetch to 5: - channel.basic_qos(prefetch_count=5) - - #publish 10 messages: - for i in range(1, 11): - channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i)) - - #only 5 messages should have been delivered: - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - - - def test_qos_prefetch_size(self): - """ - Test that the prefetch size specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-size", exclusive=True) - subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False) - queue = self.client.queue(subscription.consumer_tag) - - #set prefetch to 50 bytes (each message is 9 or 10 bytes): - channel.basic_qos(prefetch_size=50) - - #publish 10 messages: - for i in range(1, 11): - channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i)) - - #only 5 messages should have been delivered (i.e. 45 bytes worth): - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - #make sure that a single oversized message still gets delivered - large = "abcdefghijklmnopqrstuvwxyz" - large = large + "-" + large; - channel.basic_publish(routing_key="test-prefetch-size", content=Content(large)) - msg = queue.get(timeout=1) - self.assertEqual(large, msg.content.body) - - def test_get(self): - """ - Test basic_get method - """ - channel = self.channel - channel.queue_declare(queue="test-get", exclusive=True) - - #publish some messages (no_ack=True) - for i in range(1, 11): - channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) - - #use basic_get to read back the messages, and check that we get an empty at the end - for i in range(1, 11): - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_ok") - self.assertEqual("Message %d" % i, reply.content.body) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") - - #repeat for no_ack=False - for i in range(11, 21): - channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i)) - - for i in range(11, 21): - reply = channel.basic_get(no_ack=False) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_ok") - self.assertEqual("Message %d" % i, reply.content.body) - if(i == 13): - channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True) - if(i in [15, 17, 19]): - channel.basic_ack(delivery_tag=reply.delivery_tag) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") - - #recover(requeue=True) - channel.basic_recover(requeue=True) - - #get the unacked messages again (14, 16, 18, 20) - for i in [14, 16, 18, 20]: - reply = channel.basic_get(no_ack=False) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_ok") - self.assertEqual("Message %d" % i, reply.content.body) - channel.basic_ack(delivery_tag=reply.delivery_tag) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") - - channel.basic_recover(requeue=True) - - reply = channel.basic_get(no_ack=True) - self.assertEqual(reply.method.klass.name, "basic") - self.assertEqual(reply.method.name, "get_empty") diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 647f5d4fa5..0eb71287ec 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -37,19 +37,19 @@ class BrokerTests(TestBase): ctag = "tag1" ch.message_subscribe(queue = "myqueue", destination = ctag, confirm_mode = 0) body = "test no-ack" - ch.message_transfer(routing_key = "myqueue", body = body) + ch.message_transfer(content = Content(body, properties = {"routing_key" : "myqueue"})) msg = self.client.queue(ctag).get(timeout = 5) - self.assert_(msg.body == body) + self.assert_(msg.content.body == body) # Acknowledging consumer self.queue_declare(ch, queue = "otherqueue") ctag = "tag2" ch.message_subscribe(queue = "otherqueue", destination = ctag, confirm_mode = 1) body = "test ack" - ch.message_transfer(routing_key = "otherqueue", body = body) + ch.message_transfer(content = Content(body, properties = {"routing_key" : "otherqueue"})) msg = self.client.queue(ctag).get(timeout = 5) msg.complete() - self.assert_(msg.body == body) + self.assert_(msg.content.body == body) def test_simple_delivery_immediate(self): """ @@ -64,9 +64,9 @@ class BrokerTests(TestBase): queue = self.client.queue(consumer_tag) body = "Immediate Delivery" - channel.message_transfer(destination="test-exchange", routing_key="key", body=body, immediate=True) + channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) msg = queue.get(timeout=5) - self.assert_(msg.body == body) + self.assert_(msg.content.body == body) # TODO: Ensure we fail if immediate=True and there's no consumer. @@ -81,13 +81,13 @@ class BrokerTests(TestBase): self.queue_declare(channel, queue="test-queue") channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") body = "Queued Delivery" - channel.message_transfer(destination="test-exchange", routing_key="key", body=body) + channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) consumer_tag = "tag1" channel.message_subscribe(queue="test-queue", destination=consumer_tag, confirm_mode = 0) queue = self.client.queue(consumer_tag) msg = queue.get(timeout=5) - self.assert_(msg.body == body) + self.assert_(msg.content.body == body) def test_invalid_channel(self): channel = self.client.channel(200) @@ -114,8 +114,9 @@ class BrokerTests(TestBase): channel.message_subscribe(destination="my-tag", queue="flow_test_queue") incoming = self.client.queue("my-tag") - channel.channel_flow(active=False) - channel.message_transfer(routing_key="flow_test_queue", body="abcdefghijklmnopqrstuvwxyz") + channel.channel_flow(active=False) + c = Content("abcdefghijklmnopqrstuvwxyz", properties = {"routing_key" : "flow_test_queue"}) + channel.message_transfer(content = c) try: incoming.get(timeout=1) self.fail("Received message when flow turned off.") @@ -123,4 +124,4 @@ class BrokerTests(TestBase): channel.channel_flow(active=True) msg = incoming.get(timeout=1) - self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.body) + self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.content.body) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index a5b53ac65b..ea587f5998 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -248,8 +248,8 @@ class DtxTests(TestBase): #setup channel1.queue_declare(queue="one", exclusive=True) channel1.queue_declare(queue="two", exclusive=True) - channel1.message_transfer(routing_key="one", message_id="a", body="DtxMessage") - channel1.message_transfer(routing_key="two", message_id="b", body="DtxMessage") + channel1.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) + channel1.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) #create a xid tx = self.xid("dummy") @@ -284,8 +284,8 @@ class DtxTests(TestBase): #setup channel.queue_declare(queue="one", exclusive=True) channel.queue_declare(queue="two", exclusive=True) - channel.message_transfer(routing_key="one", message_id="a", body="DtxMessage") - channel.message_transfer(routing_key="two", message_id="b", body="DtxMessage") + channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) + channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) tx = self.xid("dummy") @@ -358,17 +358,17 @@ class DtxTests(TestBase): channel.dtx_demarcation_select() tx = self.xid("dummy") channel.dtx_demarcation_start(xid=tx) - channel.message_transfer(routing_key="tx-queue", message_id="one", body="DtxMessage") + channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"one"}, body="DtxMessage")) channel.dtx_demarcation_end(xid=tx) #now that association with txn is ended, publish another message - channel.message_transfer(routing_key="tx-queue", message_id="two", body="DtxMessage") + channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"two"}, body="DtxMessage")) #check the second message is available, but not the first self.assertMessageCount(1, "tx-queue") channel.message_subscribe(queue="tx-queue", destination="results", confirm_mode=1) msg = self.client.queue("results").get(timeout=1) - self.assertEqual("two", msg.message_id) + self.assertEqual("two", msg.content['message_id']) channel.message_cancel(destination="results") #ack the message then close the channel msg.complete() @@ -393,7 +393,7 @@ class DtxTests(TestBase): tester.dtx_demarcation_select() tx = self.xid("dummy") tester.dtx_demarcation_start(xid=tx) - tester.message_transfer(routing_key="dummy", body="whatever") + tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) tester.dtx_demarcation_end(xid=tx) tester.dtx_coordination_prepare(xid=tx) failed = False @@ -427,7 +427,7 @@ class DtxTests(TestBase): tester.dtx_demarcation_select() tx = self.xid("dummy") tester.dtx_demarcation_start(xid=tx) - tester.message_transfer(routing_key="dummy", body="whatever") + tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) tester.dtx_demarcation_end(xid=tx) failed = False try: @@ -456,14 +456,14 @@ class DtxTests(TestBase): #setup: channel2.queue_declare(queue="dummy", exclusive=True) - channel2.message_transfer(routing_key="dummy", body="whatever") + channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) tx = self.xid("dummy") channel2.dtx_demarcation_select() channel2.dtx_demarcation_start(xid=tx) channel2.message_get(queue="dummy", destination="dummy") self.client.queue("dummy").get(timeout=1).complete() - channel2.message_transfer(routing_key="dummy", body="whatever") + channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) channel2.channel_close() self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).status) @@ -497,7 +497,7 @@ class DtxTests(TestBase): tx = self.xid("dummy") channel.queue_declare(queue="queue-a", exclusive=True) channel.queue_declare(queue="queue-b", exclusive=True) - channel.message_transfer(routing_key="queue-a", message_id="timeout", body="DtxMessage") + channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':"timeout"}, body="DtxMessage")) channel.dtx_demarcation_select() channel.dtx_demarcation_start(xid=tx) @@ -527,7 +527,7 @@ class DtxTests(TestBase): for i in range(1, 10): tx = self.xid("tx%s" % (i)) channel.dtx_demarcation_start(xid=tx) - channel.message_transfer(routing_key="dummy", body="message%s" % (i)) + channel.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="message%s" % (i))) channel.dtx_demarcation_end(xid=tx) if i in [2, 5, 6, 8]: channel.dtx_coordination_prepare(xid=tx) @@ -575,7 +575,7 @@ class DtxTests(TestBase): channel.queue_declare(queue="queue-a", exclusive=True) channel.queue_declare(queue="queue-b", exclusive=True) #put message with specified id on one queue: - channel.message_transfer(routing_key="queue-a", message_id=id, body="DtxMessage") + channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':id}, body="DtxMessage")) #start the transaction: channel.dtx_demarcation_select() @@ -594,12 +594,13 @@ class DtxTests(TestBase): msg.complete(); #re-publish to dest - channel.message_transfer(routing_key=dest, message_id=msg.message_id, body=msg.body) + channel.message_transfer(content=Content(properties={'routing_key':dest, 'message_id':msg.content['message_id']}, + body=msg.content.body)) def assertMessageCount(self, expected, queue): self.assertEqual(expected, self.channel.queue_query(queue=queue).message_count) def assertMessageId(self, expected, queue): self.channel.message_subscribe(queue=queue, destination="results") - self.assertEqual(expected, self.client.queue("results").get(timeout=1).message_id) + self.assertEqual(expected, self.client.queue("results").get(timeout=1).content['message_id']) self.channel.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index e4c80951ac..e3e2c3b095 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -76,10 +76,9 @@ class ExampleTest (TestBase): # Now lets publish a message and see if our consumer gets it. To do # this we need to import the Content class. - body = "Hello World!" - channel.message_transfer(destination="test", - routing_key="key", - body = body) + sent = Content("Hello World!") + sent["routing_key"] = "key" + channel.message_transfer(destination="test", content=sent) # Now we'll wait for the message to arrive. We can use the timeout # argument in case the server hangs. By default queue.get() will wait @@ -87,7 +86,7 @@ class ExampleTest (TestBase): msg = queue.get(timeout=10) # And check that we got the right response with assertEqual - self.assertEqual(body, msg.body) + self.assertEqual(sent.body, msg.content.body) # Now acknowledge the message. msg.complete() diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 3a47ffff8c..4137eb7a51 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -61,10 +61,10 @@ class StandardExchangeVerifier: self.assertPublishGet(q, ex, "a.x.b.x") self.assertPublishGet(q, ex, "a.x.x.b.x") # Shouldn't match - self.channel.message_transfer(destination=ex, routing_key="a.b", body="") - self.channel.message_transfer(destination=ex, routing_key="a.b.x.y", body="") - self.channel.message_transfer(destination=ex, routing_key="x.a.b.x", body="") - self.channel.message_transfer(destination=ex, routing_key="a.b", body="") + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b.x.y"})) + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"x.a.b.x"})) + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) self.assert_(q.empty()) def verifyHeadersExchange(self, ex): @@ -74,7 +74,7 @@ class StandardExchangeVerifier: q = self.consume("q") headers = {"name":"fred", "age":3} self.assertPublishGet(q, exchange=ex, properties=headers) - self.channel.message_transfer(destination=ex, body="") # No headers, won't deliver + self.channel.message_transfer(destination=ex) # No headers, won't deliver self.assertEmpty(q); @@ -275,7 +275,7 @@ class HeadersExchangeTests(TestBase): self.assertPublishGet(self.q, exchange="amq.match", properties=headers) def myBasicPublish(self, headers): - self.channel.message_transfer(destination="amq.match", body="foobar", application_headers=headers) + self.channel.message_transfer(destination="amq.match", content=Content("foobar", properties={'application_headers':headers})) def testMatchAll(self): self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 6cf2f3ef89..f08f437a65 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -38,14 +38,14 @@ class MessageTests(TestBase): channel.message_subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) #send a message - channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local") - channel.message_transfer(routing_key="test-queue-1b", body="consume_no_local") + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1b"}, body="consume_no_local")) #check the queues of the two consumers excluded = self.client.queue("local_excluded") included = self.client.queue("local_included") msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.body) + self.assertEqual("consume_no_local", msg.content.body) try: excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") @@ -125,14 +125,14 @@ class MessageTests(TestBase): #setup, declare a queue: channel.queue_declare(queue="test-queue-4", exclusive=True) channel.message_subscribe(destination="my-consumer", queue="test-queue-4") - channel.message_transfer(routing_key="test-queue-4", body="One") + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="One")) #cancel should stop messages being delivered channel.message_cancel(destination="my-consumer") - channel.message_transfer(routing_key="test-queue-4", body="Two") + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="Two")) myqueue = self.client.queue("my-consumer") msg = myqueue.get(timeout=1) - self.assertEqual("One", msg.body) + self.assertEqual("One", msg.content.body) try: msg = myqueue.get(timeout=1) self.fail("Got message after cancellation: " + msg) @@ -153,11 +153,11 @@ class MessageTests(TestBase): channel.message_subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") - channel.message_transfer(routing_key="test-ack-queue", body="One") - channel.message_transfer(routing_key="test-ack-queue", body="Two") - channel.message_transfer(routing_key="test-ack-queue", body="Three") - channel.message_transfer(routing_key="test-ack-queue", body="Four") - channel.message_transfer(routing_key="test-ack-queue", body="Five") + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="One")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Two")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Three")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Four")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Five")) msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) @@ -165,11 +165,11 @@ class MessageTests(TestBase): msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - self.assertEqual("One", msg1.body) - self.assertEqual("Two", msg2.body) - self.assertEqual("Three", msg3.body) - self.assertEqual("Four", msg4.body) - self.assertEqual("Five", msg5.body) + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) msg2.complete(cumulative=True)#One and Two msg4.complete(cumulative=False) @@ -179,12 +179,12 @@ class MessageTests(TestBase): msg3b = queue.get(timeout=1) msg5b = queue.get(timeout=1) - self.assertEqual("Three", msg3b.body) - self.assertEqual("Five", msg5b.body) + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) try: extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) + self.fail("Got unexpected message: " + extra.content.body) except Empty: None def test_recover_requeue(self): @@ -197,11 +197,11 @@ class MessageTests(TestBase): channel.message_subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") - channel.message_transfer(routing_key="test-requeue", body="One") - channel.message_transfer(routing_key="test-requeue", body="Two") - channel.message_transfer(routing_key="test-requeue", body="Three") - channel.message_transfer(routing_key="test-requeue", body="Four") - channel.message_transfer(routing_key="test-requeue", body="Five") + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Two")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) @@ -209,11 +209,11 @@ class MessageTests(TestBase): msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - self.assertEqual("One", msg1.body) - self.assertEqual("Two", msg2.body) - self.assertEqual("Three", msg3.body) - self.assertEqual("Four", msg4.body) - self.assertEqual("Five", msg5.body) + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) msg2.complete(cumulative=True) #One and Two msg4.complete(cumulative=False) #Four @@ -221,7 +221,7 @@ class MessageTests(TestBase): channel.message_cancel(destination="consumer_tag") #publish a new message - channel.message_transfer(routing_key="test-requeue", body="Six") + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Six")) #requeue unacked messages (Three and Five) channel.message_recover(requeue=True) @@ -231,21 +231,21 @@ class MessageTests(TestBase): msg3b = queue2.get(timeout=1) msg5b = queue2.get(timeout=1) - self.assertEqual("Three", msg3b.body) - self.assertEqual("Five", msg5b.body) + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) - self.assertEqual(True, msg3b.redelivered) - self.assertEqual(True, msg5b.redelivered) + self.assertEqual(True, msg3b.content['redelivered']) + self.assertEqual(True, msg5b.content['redelivered']) - self.assertEqual("Six", queue2.get(timeout=1).body) + self.assertEqual("Six", queue2.get(timeout=1).content.body) try: extra = queue2.get(timeout=1) - self.fail("Got unexpected message in second queue: " + extra.body) + self.fail("Got unexpected message in second queue: " + extra.content.body) except Empty: None try: extra = queue.get(timeout=1) - self.fail("Got unexpected message in original queue: " + extra.body) + self.fail("Got unexpected message in original queue: " + extra.content.body) except Empty: None @@ -264,15 +264,15 @@ class MessageTests(TestBase): #publish 10 messages: for i in range(1, 11): - channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i) + channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-count"}, body="Message %d" % i)) #only 5 messages should have been delivered: for i in range(1, 6): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) try: extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.body) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) except Empty: None #ack messages and check that the next set arrive ok: @@ -280,13 +280,13 @@ class MessageTests(TestBase): for i in range(6, 11): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) msg.complete() try: extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.body) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) except Empty: None @@ -306,16 +306,16 @@ class MessageTests(TestBase): #publish 10 messages: for i in range(1, 11): - channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i) + channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body="Message %d" % i)) #only 5 messages should have been delivered (i.e. 45 bytes worth): for i in range(1, 6): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) try: extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.body) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) except Empty: None #ack messages and check that the next set arrive ok: @@ -323,328 +323,38 @@ class MessageTests(TestBase): for i in range(6, 11): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) msg.complete() try: extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.body) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) except Empty: None #make sure that a single oversized message still gets delivered large = "abcdefghijklmnopqrstuvwxyz" large = large + "-" + large; - channel.message_transfer(routing_key="test-prefetch-size", body=large) + channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body=large)) msg = queue.get(timeout=1) - self.assertEqual(large, msg.body) + self.assertEqual(large, msg.content.body) - def test_get(self): - """ - Test message_get method - """ - channel = self.channel - channel.queue_declare(queue="test-get", exclusive=True) - - #publish some messages (no_ack=True) - for i in range(1, 11): - channel.message_transfer(routing_key="test-get", body="Message %d" % i) - - #use message_get to read back the messages, and check that we get an empty at the end - for i in range(1, 11): - tag = "queue %d" % i - reply = channel.message_get(no_ack=True, queue="test-get", destination=tag) - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "ok") - self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body) - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "empty") - - #repeat for confirm_mode=1 - for i in range(11, 21): - channel.message_transfer(routing_key="test-get", body="Message %d" % i) - - for i in range(11, 21): - tag = "queue %d" % i - reply = channel.message_get(confirm_mode=1, queue="test-get", destination=tag) - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "ok") - msg = self.client.queue(tag).get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - if (i==13): - msg.complete()#11, 12 & 13 - if(i in [15, 17, 19]): - msg.complete(cumulative=False) - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "empty") - - #recover(requeue=True) - channel.message_recover(requeue=True) - - #get the unacked messages again (14, 16, 18, 20) - for i in [14, 16, 18, 20]: - tag = "queue %d" % i - reply = channel.message_get(confirm_mode=1, queue="test-get", destination=tag) - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "ok") - msg = self.client.queue(tag).get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - msg.complete() - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "empty") - - channel.message_recover(requeue=True) - - reply = channel.message_get(no_ack=True, queue="test-get") - self.assertEqual(reply.method.klass.name, "message") - self.assertEqual(reply.method.name, "empty") - - def test_reference_simple(self): - """ - Test basic ability to handle references - """ - channel = self.channel - channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_subscribe(queue="ref_queue", destination="c1") - queue = self.client.queue("c1") - - refId = "myref" - channel.message_open(reference=refId) - channel.message_append(reference=refId, bytes="abcd") - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) - channel.synchronous = True - - channel.message_append(reference=refId, bytes="efgh") - channel.message_append(reference=refId, bytes="ijkl") - channel.message_close(reference=refId) - - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - self.assertDataEquals(channel, queue.get(timeout=1), "abcdefghijkl") - - - def test_reference_large(self): - """ - Test basic ability to handle references whose content exceeds max frame size - """ - channel = self.channel - self.queue_declare(queue="ref_queue") - - #generate a big data string (> max frame size of consumer): - data = "0123456789" - for i in range(0, 10): - data += data - #send it inline - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", body=data) - channel.synchronous = True - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - #create a new connection for consumer, with specific max frame size (< data) - other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0}) - ch2 = other.channel(1) - ch2.channel_open() - ch2.message_subscribe(queue="ref_queue", destination="c1") - queue = other.queue("c1") - - msg = queue.get(timeout=1) - self.assertTrue(isinstance(msg.body, ReferenceId)) - self.assertTrue(msg.reference) - self.assertEquals(data, msg.reference.get_complete()) - - def test_reference_completion(self): - """ - Test that reference transfer are not deemed complete until - closed (therefore are not acked or routed until that point) - """ - channel = self.channel - channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_subscribe(queue="ref_queue", destination="c1") - queue = self.client.queue("c1") - - refId = "myref" - channel.message_open(reference=refId) - channel.message_append(reference=refId, bytes="abcd") - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId)) - channel.synchronous = True - - try: - msg = queue.get(timeout=1) - self.fail("Got unexpected message on queue: " + msg) - except Empty: None - - self.assertTrue(not ack.is_complete()) - - channel.message_close(reference=refId) - - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - self.assertDataEquals(channel, queue.get(timeout=1), "abcd") - def test_reference_multi_transfer(self): - """ - Test that multiple transfer requests for the same reference are - correctly handled. - """ - channel = self.channel - #declare and consume from two queues - channel.queue_declare(queue="q-one", exclusive=True) - channel.queue_declare(queue="q-two", exclusive=True) - channel.message_subscribe(queue="q-one", destination="q-one") - channel.message_subscribe(queue="q-two", destination="q-two") - queue1 = self.client.queue("q-one") - queue2 = self.client.queue("q-two") - - #transfer a single ref to both queues (in separate commands) - channel.message_open(reference="my-ref") - channel.synchronous = False - ack1 = channel.message_transfer(routing_key="q-one", body=ReferenceId("my-ref")) - channel.message_append(reference="my-ref", bytes="my data") - ack2 = channel.message_transfer(routing_key="q-two", body=ReferenceId("my-ref")) - channel.synchronous = True - channel.message_close(reference="my-ref") - - #check that both queues have the message - self.assertDataEquals(channel, queue1.get(timeout=1), "my data") - self.assertDataEquals(channel, queue2.get(timeout=1), "my data") - self.assertEmpty(queue1) - self.assertEmpty(queue2) - - #transfer a single ref to the same queue twice (in separate commands) - channel.message_open(reference="my-ref") - channel.synchronous = False - ack1 = channel.message_transfer(routing_key="q-one", message_id="abc", body=ReferenceId("my-ref")) - channel.message_append(reference="my-ref", bytes="second message") - ack2 = channel.message_transfer(routing_key="q-one", message_id="xyz", body=ReferenceId("my-ref")) - channel.synchronous = True - channel.message_close(reference="my-ref") - - msg1 = queue1.get(timeout=1) - msg2 = queue1.get(timeout=1) - #order is undefined - if msg1.message_id == "abc": - self.assertEquals(msg2.message_id, "xyz") - else: - self.assertEquals(msg1.message_id, "xyz") - self.assertEquals(msg2.message_id, "abc") - - #would be legal for the incoming messages to be transfered - #inline or by reference in any combination - - if isinstance(msg1.body, ReferenceId): - self.assertEquals("second message", msg1.reference.get_complete()) - if isinstance(msg2.body, ReferenceId): - if msg1.body != msg2.body: - self.assertEquals("second message", msg2.reference.get_complete()) - #else ok, as same ref as msg1 - else: - self.assertEquals("second message", msg1.body) - if isinstance(msg2.body, ReferenceId): - self.assertEquals("second message", msg2.reference.get_complete()) - else: - self.assertEquals("second message", msg2.body) - - self.assertEmpty(queue1) - - def test_reference_unopened_on_append_error(self): - channel = self.channel - try: - channel.message_append(reference="unopened") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_reference_unopened_on_close_error(self): - channel = self.channel - try: - channel.message_close(reference="unopened") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_reference_unopened_on_transfer_error(self): - channel = self.channel - try: - channel.message_transfer(body=ReferenceId("unopened")) - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_reference_already_opened_error(self): - channel = self.channel - channel.message_open(reference="a") - try: - channel.message_open(reference="a") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_empty_reference(self): - channel = self.channel - channel.queue_declare(queue="ref_queue", exclusive=True) - channel.message_subscribe(queue="ref_queue", destination="c1") - queue = self.client.queue("c1") - - refId = "myref" - channel.message_open(reference=refId) - channel.synchronous = False - ack = channel.message_transfer(routing_key="ref_queue", message_id="empty-msg", body=ReferenceId(refId)) - channel.synchronous = True - channel.message_close(reference=refId) - - #first, wait for the ok for the transfer - ack.get_response(timeout=1) - - msg = queue.get(timeout=1) - self.assertEquals(msg.message_id, "empty-msg") - self.assertDataEquals(channel, msg, "") def test_reject(self): channel = self.channel channel.queue_declare(queue = "q", exclusive=True) channel.message_subscribe(queue = "q", destination = "consumer") - channel.message_transfer(routing_key = "q", body="blah, blah") + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body="blah, blah")) msg = self.client.queue("consumer").get(timeout = 1) - self.assertEquals(msg.body, "blah, blah") + self.assertEquals(msg.content.body, "blah, blah") channel.message_cancel(destination = "consumer") msg.reject() channel.message_subscribe(queue = "q", destination = "checker") msg = self.client.queue("checker").get(timeout = 1) - self.assertEquals(msg.body, "blah, blah") - - def test_checkpoint(self): - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) - - channel.message_open(reference="my-ref") - channel.message_append(reference="my-ref", bytes="abcdefgh") - channel.message_append(reference="my-ref", bytes="ijklmnop") - channel.message_checkpoint(reference="my-ref", identifier="my-checkpoint") - channel.channel_close() - - channel = self.client.channel(2) - channel.channel_open() - channel.message_subscribe(queue = "q", destination = "consumer") - offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value - self.assertTrue(offset<=16) - channel.message_append(reference="my-ref", bytes="qrstuvwxyz") - channel.synchronous = False - channel.message_transfer(routing_key="q-one", message_id="abcd", body=ReferenceId("my-ref")) - channel.synchronous = True - channel.message_close(reference="my-ref") - - self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz") - self.assertEmpty(self.client.queue("consumer")) + self.assertEquals(msg.content.body, "blah, blah") def test_credit_flow_messages(self): """ @@ -660,7 +370,7 @@ class MessageTests(TestBase): channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): - channel.message_transfer(routing_key = "q", body = "Message %d" % i) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) channel.message_flow(unit = 0, value = 5, destination = "c") @@ -692,7 +402,7 @@ class MessageTests(TestBase): channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): - channel.message_transfer(routing_key = "q", body = "abcdefgh") + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) #each message is currently interpreted as requiring 75 bytes of credit #set byte credit to finite amount (less than enough for all messages) @@ -726,7 +436,7 @@ class MessageTests(TestBase): channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): - channel.message_transfer(routing_key = "q", body = "Message %d" % i) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) channel.message_flow(unit = 0, value = 5, destination = "c") @@ -760,7 +470,7 @@ class MessageTests(TestBase): channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): - channel.message_transfer(routing_key = "q", body = "abcdefgh") + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) #each message is currently interpreted as requiring 75 bytes of credit #set byte credit to finite amount (less than enough for all messages) @@ -783,11 +493,5 @@ class MessageTests(TestBase): self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) - - def assertDataEquals(self, channel, msg, expected): - if isinstance(msg.body, ReferenceId): - data = msg.reference.get_complete() - else: - data = msg.body - self.assertEquals(expected, data) + self.assertEquals(expected, msg.content.body) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 8d99c50d32..05fa1aebc6 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -33,9 +33,9 @@ class QueueTests(TestBase): channel.exchange_declare(exchange="test-exchange", type="direct") channel.queue_declare(queue="test-queue", exclusive=True) channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - channel.message_transfer(destination="test-exchange", routing_key="key", body="one") - channel.message_transfer(destination="test-exchange", routing_key="key", body="two") - channel.message_transfer(destination="test-exchange", routing_key="key", body="three") + channel.message_transfer(destination="test-exchange", content=Content("one", properties={'routing_key':"key"})) + channel.message_transfer(destination="test-exchange", content=Content("two", properties={'routing_key':"key"})) + channel.message_transfer(destination="test-exchange", content=Content("three", properties={'routing_key':"key"})) #check that the queue now reports 3 messages: channel.queue_declare(queue="test-queue") @@ -48,11 +48,11 @@ class QueueTests(TestBase): self.assertEqual(0, reply.message_count) #send a further message and consume it, ensuring that the other messages are really gone - channel.message_transfer(destination="test-exchange", routing_key="key", body="four") + channel.message_transfer(destination="test-exchange", content=Content("four", properties={'routing_key':"key"})) channel.message_subscribe(queue="test-queue", destination="tag") queue = self.client.queue("tag") msg = queue.get(timeout=1) - self.assertEqual("four", msg.body) + self.assertEqual("four", msg.content.body) #check error conditions (use new channels): channel = self.client.channel(2) @@ -179,23 +179,25 @@ class QueueTests(TestBase): channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) #send a message that will match both bindings - channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="one") + channel.message_transfer(destination=exchange, + content=Content("one", properties={'routing_key':routing_key, 'application_headers':headers})) #unbind first queue channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) #send another message - channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="two") + channel.message_transfer(destination=exchange, + content=Content("two", properties={'routing_key':routing_key, 'application_headers':headers})) #check one queue has both messages and the other has only one - self.assertEquals("one", queue1.get(timeout=1).body) + self.assertEquals("one", queue1.get(timeout=1).content.body) try: msg = queue1.get(timeout=1) - self.fail("Got extra message: %s" % msg.body) + self.fail("Got extra message: %s" % msg.content.body) except Empty: pass - self.assertEquals("one", queue2.get(timeout=1).body) - self.assertEquals("two", queue2.get(timeout=1).body) + self.assertEquals("one", queue2.get(timeout=1).content.body) + self.assertEquals("two", queue2.get(timeout=1).content.body) try: msg = queue2.get(timeout=1) self.fail("Got extra message: " + msg) @@ -210,9 +212,9 @@ class QueueTests(TestBase): #straight-forward case: channel.queue_declare(queue="delete-me") - channel.message_transfer(routing_key="delete-me", body="a") - channel.message_transfer(routing_key="delete-me", body="b") - channel.message_transfer(routing_key="delete-me", body="c") + channel.message_transfer(content=Content("a", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("b", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("c", properties={'routing_key':"delete-me"})) channel.queue_delete(queue="delete-me") #check that it has gone be declaring passively try: @@ -241,7 +243,7 @@ class QueueTests(TestBase): #create a queue and add a message to it (use default binding): channel.queue_declare(queue="delete-me-2") channel.queue_declare(queue="delete-me-2", passive="True") - channel.message_transfer(routing_key="delete-me-2", body="message") + channel.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) #try to delete, but only if empty: try: @@ -258,7 +260,7 @@ class QueueTests(TestBase): channel.message_subscribe(destination="consumer_tag", queue="delete-me-2") queue = self.client.queue("consumer_tag") msg = queue.get(timeout=1) - self.assertEqual("message", msg.body) + self.assertEqual("message", msg.content.body) channel.message_cancel(destination="consumer_tag") #retry deletion on empty queue: diff --git a/qpid/python/tests_0-10/testlib.py b/qpid/python/tests_0-10/testlib.py index f345fbbd80..a0355c4ce0 100644 --- a/qpid/python/tests_0-10/testlib.py +++ b/qpid/python/tests_0-10/testlib.py @@ -49,7 +49,7 @@ class TestBaseTest(TestBase): def testAssertEmptyFail(self): self.queue_declare(queue="full") q = self.consume("full") - self.channel.message_transfer(routing_key="full", body="") + self.channel.message_transfer(content=Content("", properties={'routing_key':"full"})) try: self.assertEmpty(q); self.fail("assertEmpty did not assert on non-empty queue") diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 4c2f75d35e..7c50de4ee2 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -53,21 +53,21 @@ class TxTests(TestBase): #check results for i in range(1, 5): msg = queue_c.get(timeout=1) - self.assertEqual("TxMessage %d" % i, msg.body) + self.assertEqual("TxMessage %d" % i, msg.content.body) msg.complete() msg = queue_b.get(timeout=1) - self.assertEqual("TxMessage 6", msg.body) + self.assertEqual("TxMessage 6", msg.content.body) msg.complete() msg = queue_a.get(timeout=1) - self.assertEqual("TxMessage 7", msg.body) + self.assertEqual("TxMessage 7", msg.content.body) msg.complete() for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) + self.fail("Got unexpected message: " + extra.content.body) except Empty: None #cleanup @@ -83,7 +83,7 @@ class TxTests(TestBase): for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) + self.fail("Got unexpected message: " + extra.content.body) except Empty: None channel.tx_rollback() @@ -91,21 +91,21 @@ class TxTests(TestBase): #check results for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) msg.complete() msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.body) + self.assertEqual("Message 6", msg.content.body) msg.complete() msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.body) + self.assertEqual("Message 7", msg.content.body) msg.complete() for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) + self.fail("Got unexpected message: " + extra.content.body) except Empty: None #cleanup @@ -121,7 +121,7 @@ class TxTests(TestBase): for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) + self.fail("Got unexpected message: " + extra.content.body) except Empty: None channel.tx_rollback() @@ -129,21 +129,21 @@ class TxTests(TestBase): #check results for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) msg.complete() msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.body) + self.assertEqual("Message 6", msg.content.body) msg.complete() msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.body) + self.assertEqual("Message 7", msg.content.body) msg.complete() for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.body) + self.fail("Got unexpected message: " + extra.content.body) except Empty: None #cleanup @@ -166,10 +166,12 @@ class TxTests(TestBase): channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) for i in range(1, 5): - channel.message_transfer(routing_key=name_a, message_id="msg%d" % i, body="Message %d" % i) + channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"msg%d" % i}, body="Message %d" % i)) - channel.message_transfer(routing_key=key, destination="amq.direct", message_id="msg6", body="Message 6") - channel.message_transfer(routing_key=topic, destination="amq.topic", message_id="msg7", body="Message 7") + channel.message_transfer(destination="amq.direct", + content=Content(properties={'routing_key':key, 'message_id':"msg6"}, body="Message 6")) + channel.message_transfer(destination="amq.topic", + content=Content(properties={'routing_key':topic, 'message_id':"msg7"}, body="Message 7")) channel.tx_select() @@ -178,27 +180,31 @@ class TxTests(TestBase): queue_a = self.client.queue("sub_a") for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) + self.assertEqual("Message %d" % i, msg.content.body) msg.complete() channel.message_subscribe(queue=name_b, destination="sub_b", confirm_mode=1) queue_b = self.client.queue("sub_b") msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.body) + self.assertEqual("Message 6", msg.content.body) msg.complete() sub_c = channel.message_subscribe(queue=name_c, destination="sub_c", confirm_mode=1) queue_c = self.client.queue("sub_c") msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.body) + self.assertEqual("Message 7", msg.content.body) msg.complete() #publish messages for i in range(1, 5): - channel.message_transfer(routing_key=topic, destination="amq.topic", message_id="tx-msg%d" % i, body="TxMessage %d" % i) - - channel.message_transfer(routing_key=key, destination="amq.direct", message_id="tx-msg6", body="TxMessage 6") - channel.message_transfer(routing_key=name_a, message_id="tx-msg7", body="TxMessage 7") - + channel.message_transfer(destination="amq.topic", + content=Content(properties={'routing_key':topic, 'message_id':"tx-msg%d" % i}, + body="TxMessage %d" % i)) + + channel.message_transfer(destination="amq.direct", + content=Content(properties={'routing_key':key, 'message_id':"tx-msg6"}, + body="TxMessage 6")) + channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"tx-msg7"}, + body="TxMessage 7")) return queue_a, queue_b, queue_c -- cgit v1.2.1 From 24da8ddb4a9f865fd6f0f192b1ff8d83816dd641 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 3 Sep 2007 09:15:35 +0000 Subject: Fix for QPID-561 Renabled MessageBuilderTest git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@572264 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/dtx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index ea587f5998..29a4d3bf0d 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -39,7 +39,7 @@ class DtxTests(TestBase): XA_RBROLLBACK = 1 XA_RBTIMEOUT = 2 - XA_OK = 8 + XA_OK = 0 tx_counter = 0 def reset_channel(self): -- cgit v1.2.1 From 7f9a19a373454f3ca68a2fdb42e7bdb69cae04ca Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 3 Sep 2007 17:35:35 +0000 Subject: Initial implementation (plus very simple tests) of message.acquire, message.release, message.reject and message.flush. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@572394 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 1 - qpid/python/qpid/client.py | 3 ++ qpid/python/qpid/peer.py | 1 + qpid/python/tests_0-10/message.py | 78 +++++++++++++++++++++++++++++++++++---- 4 files changed, 75 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 97cf420717..5b2fb593e1 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,4 +1,3 @@ tests_0-10.alternate-exchange.AlternateExchangeTests.test_immediate -tests_0-10.message.MessageTests.test_reject tests_0-10.basic.BasicTests.test_get diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index edcd1b8ad2..5734873c6f 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -126,6 +126,9 @@ class ClientDelegate(Delegate): def message_append(self, ch, msg): ch.references.get(msg.reference).append(msg.bytes) + def message_acquired(self, ch, msg): + ch.control_queue.put(msg) + def basic_deliver(self, ch, msg): self.client.queue(msg.consumer_tag).put(msg) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 6ad5482f09..b9dd4e466a 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -190,6 +190,7 @@ class Channel: self.completion = OutgoingCompletion() self.incoming_completion = IncomingCompletion(self) self.futures = {} + self.control_queue = Queue(0)#used for incoming methods that appas may want to handle themselves # Use reliable framing if version == 0-9. if spec.major == 0 and spec.minor == 9: diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index f08f437a65..8089709314 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -339,20 +339,19 @@ class MessageTests(TestBase): msg = queue.get(timeout=1) self.assertEqual(large, msg.content.body) - - def test_reject(self): channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, alternate_exchange="amq.fanout") + channel.queue_declare(queue = "r", exclusive=True) + channel.queue_bind(queue = "r", exchange = "amq.fanout") - channel.message_subscribe(queue = "q", destination = "consumer") + channel.message_subscribe(queue = "q", destination = "consumer", confirm_mode = 1) channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body="blah, blah")) msg = self.client.queue("consumer").get(timeout = 1) self.assertEquals(msg.content.body, "blah, blah") - channel.message_cancel(destination = "consumer") - msg.reject() + channel.message_reject([msg.command_id, msg.command_id]) - channel.message_subscribe(queue = "q", destination = "checker") + channel.message_subscribe(queue = "r", destination = "checker") msg = self.client.queue("checker").get(timeout = 1) self.assertEquals(msg.content.body, "blah, blah") @@ -493,5 +492,70 @@ class MessageTests(TestBase): self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) + def test_subscribe_not_acquired(self): + """ + Test the not-acquired modes works as expected for a simple case + """ + #NOTE: I'm using not-acquired == 1 and pre-acquired == 0 as + #that keeps the default behaviour as expected. This was + #discussed by the SIG, and I'd rather not change all the + #existing tests twice. + + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + for i in range(1, 6): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + + channel.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) + channel.message_subscribe(queue = "q", destination = "b", acquire_mode = 1) + + for i in range(6, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + + #both subscribers should see all messages + qA = self.client.queue("a") + qB = self.client.queue("b") + for i in range(1, 11): + for q in [qA, qB]: + msg = q.get(timeout = 1) + self.assertEquals("Message %s" % i, msg.content.body) + msg.complete() + + #messages should still be on the queue: + self.assertEquals(10, channel.queue_query(queue = "q").message_count) + + def test_acquire(self): + """ + Test explicit acquire function + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) + + channel.message_subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) + msg = self.client.queue("a").get(timeout = 1) + channel.message_acquire([msg.command_id, msg.command_id]) + msg.complete() + + #message should have been removed from the queue: + self.assertEquals(0, channel.queue_query(queue = "q").message_count) + + def test_release(self): + """ + Test explicit release function + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "release me")) + + channel.message_subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) + msg = self.client.queue("a").get(timeout = 1) + channel.message_cancel(destination = "a") + channel.message_release([msg.command_id, msg.command_id]) + msg.complete() + + #message should not have been removed from the queue: + self.assertEquals(1, channel.queue_query(queue = "q").message_count) + def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) -- cgit v1.2.1 From 8b455df678dcdac6107fa6861603f99bc1bbe1f5 Mon Sep 17 00:00:00 2001 From: Andrew Stitcher Date: Wed, 12 Sep 2007 00:37:17 +0000 Subject: * python/qpid/codec.py Comment typo * cpp/src/qpid/broker/RecoveryManagerImpl.cpp Cruft removal * python/qpid/codec.py * python/qpid/connection.py * cpp/src/qpid/framing/AMQFrame.h * cpp/src/qpid/framing/AMQFrame.cpp Initial implementation of 0-10 framing - This uses the new 12 byte frame header, but doesn't support splitting segments/framesets over multiple frames yet. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@574735 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 2 +- qpid/python/qpid/connection.py | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index ae113619ae..c1a912f5d0 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -53,7 +53,7 @@ class Codec: def read(self, n): """ - reads in 'n' bytes from the stream. Can raise EFO exception + reads in 'n' bytes from the stream. Can raise EOF exception """ data = self.stream.read(n) if n > 0 and len(data) == 0: diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 39bcde17df..11a0d3ec6a 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -92,23 +92,44 @@ class Connection: def write(self, frame): c = self.codec + c.encode_octet(0x0f) # TODO: currently fixed at ver=0, B=E=b=e=1 c.encode_octet(self.spec.constants.byname[frame.type].id) - c.encode_short(frame.channel) body = StringIO() enc = codec.Codec(body, self.spec) frame.encode(enc) enc.flush() - c.encode_longstr(body.getvalue()) + frame_size = len(body.getvalue()) + 12 # TODO: Magic number (frame header size) + c.encode_short(frame_size) + c.encode_octet(0) # Reserved + c.encode_octet(frame.subchannel & 0x0f) + c.encode_short(frame.channel) + c.encode_long(0) # Reserved + c.write(body.getvalue()) c.encode_octet(self.FRAME_END) def read(self): c = self.codec + flags = c.decode_octet() # TODO: currently ignoring flags + framing_version = (flags & 0xc0) >> 6 + if framing_version != 0: + raise "frame error: unknown framing version" type = self.spec.constants.byid[c.decode_octet()].name + frame_size = c.decode_short() + if frame_size < 12: # TODO: Magic number (frame header size) + raise "frame error: frame size too small" + reserved1 = c.decode_octet() + field = c.decode_octet() + subchannel = field & 0x0f channel = c.decode_short() - body = c.decode_longstr() + reserved2 = c.decode_long() # TODO: reserved maybe need to ensure 0 + if (flags & 0x30) != 0 or reserved1 != 0 or (field & 0xf0) != 0: + raise "frame error: reserved bits not all zero" + body_size = frame_size - 12 # TODO: Magic number (frame header size) + body = c.read(body_size) dec = codec.Codec(StringIO(body), self.spec) frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) frame.channel = channel + frame.subchannel = subchannel end = c.decode_octet() if end != self.FRAME_END: garbage = "" @@ -145,6 +166,7 @@ class Frame: def init(self, args, kwargs): self.channel = kwargs.pop("channel", 0) + self.subchannel = kwargs.pop("subchannel", 0) def encode(self, enc): abstract -- cgit v1.2.1 From e5dcba2f7ee5ff71658532b7ac2f3ff47bf77db4 Mon Sep 17 00:00:00 2001 From: Andrew Stitcher Date: Wed, 12 Sep 2007 13:11:52 +0000 Subject: * Python: reinstated 0-8 framing in parallel with the new 0-10 framing git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@574936 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 11a0d3ec6a..3ae88979fd 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -77,7 +77,9 @@ class Connection: self.codec = codec.Codec(io, spec) self.spec = spec self.FRAME_END = self.spec.constants.byname["frame_end"].id - + self.write = getattr(self, "write_%s_%s" % (self.spec.major, self.spec.minor)) + self.read = getattr(self, "read_%s_%s" % (self.spec.major, self.spec.minor)) + def flush(self): self.codec.flush() @@ -89,8 +91,36 @@ class Connection: def tini(self): self.codec.unpack(Connection.INIT) + + def write_8_0(self, frame): + c = self.codec + c.encode_octet(self.spec.constants.byname[frame.type].id) + c.encode_short(frame.channel) + body = StringIO() + enc = codec.Codec(body, self.spec) + frame.encode(enc) + enc.flush() + c.encode_longstr(body.getvalue()) + c.encode_octet(self.FRAME_END) + + def read_8_0(self): + c = self.codec + type = self.spec.constants.byid[c.decode_octet()].name + channel = c.decode_short() + body = c.decode_longstr() + dec = codec.Codec(StringIO(body), self.spec) + frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) + frame.channel = channel + end = c.decode_octet() + if end != self.FRAME_END: + garbage = "" + while end != self.FRAME_END: + garbage += chr(end) + end = c.decode_octet() + raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) + return frame - def write(self, frame): + def write_0_10(self, frame): c = self.codec c.encode_octet(0x0f) # TODO: currently fixed at ver=0, B=E=b=e=1 c.encode_octet(self.spec.constants.byname[frame.type].id) @@ -107,7 +137,7 @@ class Connection: c.write(body.getvalue()) c.encode_octet(self.FRAME_END) - def read(self): + def read_0_10(self): c = self.codec flags = c.decode_octet() # TODO: currently ignoring flags framing_version = (flags & 0xc0) >> 6 -- cgit v1.2.1 From 813499376e288db736428c2eb6b4634dde40a34c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 12 Sep 2007 14:49:12 +0000 Subject: In ClientChannel: Use subscribe and flush in place of get; use per-subscriber flow control for managing prefetches. In brokers Session: set credit to 0 when subscription is created (modified python tests accordingly) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@574979 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 1 - qpid/python/qpid/testlib.py | 9 +++++ qpid/python/tests_0-10/alternate-exchange.py | 8 ++--- qpid/python/tests_0-10/broker.py | 12 ++++--- qpid/python/tests_0-10/dtx.py | 4 ++- qpid/python/tests_0-10/example.py | 2 ++ qpid/python/tests_0-10/message.py | 52 ++++++++++++---------------- qpid/python/tests_0-10/queue.py | 10 +++--- qpid/python/tests_0-10/tx.py | 12 +++---- 9 files changed, 58 insertions(+), 52 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 5b2fb593e1..878afee3c5 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,3 +1,2 @@ tests_0-10.alternate-exchange.AlternateExchangeTests.test_immediate -tests_0-10.basic.BasicTests.test_get diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 28c07ba43a..c2e3024a7e 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -259,8 +259,17 @@ class TestBase(unittest.TestCase): else: self.uniqueTag += 1 consumer_tag = "tag" + str(self.uniqueTag) self.channel.message_subscribe(queue=queueName, destination=consumer_tag) + self.channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) + self.channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) return self.client.queue(consumer_tag) + def subscribe(self, channel=None, **keys): + channel = channel or self.channel + consumer_tag = keys["destination"] + channel.message_subscribe(**keys) + channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) + channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + def assertEmpty(self, queue): """Assert that the queue is empty""" try: diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py index d6ac62ccfe..a749d733b0 100644 --- a/qpid/python/tests_0-10/alternate-exchange.py +++ b/qpid/python/tests_0-10/alternate-exchange.py @@ -39,13 +39,13 @@ class AlternateExchangeTests(TestBase): #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages channel.queue_declare(queue="returns", exclusive=True) channel.queue_bind(queue="returns", exchange="secondary") - channel.message_subscribe(destination="a", queue="returns") + self.subscribe(destination="a", queue="returns") returned = self.client.queue("a") #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages channel.queue_declare(queue="processed", exclusive=True) channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") - channel.message_subscribe(destination="b", queue="processed") + self.subscribe(destination="b", queue="processed") processed = self.client.queue("b") #publish to the primary exchange @@ -73,7 +73,7 @@ class AlternateExchangeTests(TestBase): channel.exchange_declare(exchange="dlq", type="fanout") channel.queue_declare(queue="deleted", exclusive=True) channel.queue_bind(exchange="dlq", queue="deleted") - channel.message_subscribe(destination="dlq", queue="deleted") + self.subscribe(destination="dlq", queue="deleted") dlq = self.client.queue("dlq") #create a queue using the dlq as its alternate exchange: @@ -103,7 +103,7 @@ class AlternateExchangeTests(TestBase): channel.exchange_declare(exchange="dlq", type="fanout") channel.queue_declare(queue="immediate", exclusive=True) channel.queue_bind(exchange="dlq", queue="immediate") - channel.message_subscribe(destination="dlq", queue="immediate") + self.subscribe(destination="dlq", queue="immediate") dlq = self.client.queue("dlq") #create a queue using the dlq as its alternate exchange: diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 0eb71287ec..0df7eb09fa 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -35,7 +35,7 @@ class BrokerTests(TestBase): # No ack consumer ctag = "tag1" - ch.message_subscribe(queue = "myqueue", destination = ctag, confirm_mode = 0) + self.subscribe(ch, queue = "myqueue", destination = ctag) body = "test no-ack" ch.message_transfer(content = Content(body, properties = {"routing_key" : "myqueue"})) msg = self.client.queue(ctag).get(timeout = 5) @@ -44,7 +44,9 @@ class BrokerTests(TestBase): # Acknowledging consumer self.queue_declare(ch, queue = "otherqueue") ctag = "tag2" - ch.message_subscribe(queue = "otherqueue", destination = ctag, confirm_mode = 1) + self.subscribe(ch, queue = "otherqueue", destination = ctag, confirm_mode = 1) + ch.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) + ch.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) body = "test ack" ch.message_transfer(content = Content(body, properties = {"routing_key" : "otherqueue"})) msg = self.client.queue(ctag).get(timeout = 5) @@ -60,7 +62,7 @@ class BrokerTests(TestBase): self.queue_declare(channel, queue="test-queue") channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") consumer_tag = "tag1" - channel.message_subscribe(queue="test-queue", destination=consumer_tag, confirm_mode = 0) + self.subscribe(queue="test-queue", destination=consumer_tag) queue = self.client.queue(consumer_tag) body = "Immediate Delivery" @@ -84,7 +86,7 @@ class BrokerTests(TestBase): channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) consumer_tag = "tag1" - channel.message_subscribe(queue="test-queue", destination=consumer_tag, confirm_mode = 0) + self.subscribe(queue="test-queue", destination=consumer_tag) queue = self.client.queue(consumer_tag) msg = queue.get(timeout=5) self.assert_(msg.content.body == body) @@ -111,7 +113,7 @@ class BrokerTests(TestBase): def test_channel_flow(self): channel = self.channel channel.queue_declare(queue="flow_test_queue", exclusive=True) - channel.message_subscribe(destination="my-tag", queue="flow_test_queue") + self.subscribe(destination="my-tag", queue="flow_test_queue") incoming = self.client.queue("my-tag") channel.channel_flow(active=False) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 29a4d3bf0d..b5645cb596 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -366,7 +366,7 @@ class DtxTests(TestBase): #check the second message is available, but not the first self.assertMessageCount(1, "tx-queue") - channel.message_subscribe(queue="tx-queue", destination="results", confirm_mode=1) + self.subscribe(channel, queue="tx-queue", destination="results", confirm_mode=1) msg = self.client.queue("results").get(timeout=1) self.assertEqual("two", msg.content['message_id']) channel.message_cancel(destination="results") @@ -602,5 +602,7 @@ class DtxTests(TestBase): def assertMessageId(self, expected, queue): self.channel.message_subscribe(queue=queue, destination="results") + self.channel.message_flow(destination="results", unit=0, value=1) + self.channel.message_flow(destination="results", unit=1, value=0xFFFFFFFF) self.assertEqual(expected, self.client.queue("results").get(timeout=1).content['message_id']) self.channel.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index e3e2c3b095..9dbe73e3cb 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -69,6 +69,8 @@ class ExampleTest (TestBase): # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. channel.message_subscribe(queue="test-queue", destination="consumer_tag") + channel.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) + channel.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) # We can use the Client.queue(...) method to access the queue # corresponding to our consumer_tag. diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 8089709314..ba26dda309 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -34,8 +34,8 @@ class MessageTests(TestBase): channel.queue_declare(queue="test-queue-1a", exclusive=True) channel.queue_declare(queue="test-queue-1b", exclusive=True) #establish two consumers one of which excludes delivery of locally sent messages - channel.message_subscribe(destination="local_included", queue="test-queue-1a") - channel.message_subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) + self.subscribe(destination="local_included", queue="test-queue-1a") + self.subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) #send a message channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) @@ -61,9 +61,9 @@ class MessageTests(TestBase): channel.queue_declare(queue="test-queue-2", exclusive=True) #check that an exclusive consumer prevents other consumer being created: - channel.message_subscribe(destination="first", queue="test-queue-2", exclusive=True) + self.subscribe(destination="first", queue="test-queue-2", exclusive=True) try: - channel.message_subscribe(destination="second", queue="test-queue-2") + self.subscribe(destination="second", queue="test-queue-2") self.fail("Expected consume request to fail due to previous exclusive consumer") except Closed, e: self.assertChannelException(403, e.args[0]) @@ -73,9 +73,9 @@ class MessageTests(TestBase): channel.channel_open() #check that an exclusive consumer cannot be created if a consumer already exists: - channel.message_subscribe(destination="first", queue="test-queue-2") + self.subscribe(channel, destination="first", queue="test-queue-2") try: - channel.message_subscribe(destination="second", queue="test-queue-2", exclusive=True) + self.subscribe(destination="second", queue="test-queue-2", exclusive=True) self.fail("Expected exclusive consume request to fail due to previous consumer") except Closed, e: self.assertChannelException(403, e.args[0]) @@ -87,7 +87,7 @@ class MessageTests(TestBase): channel = self.channel try: #queue specified but doesn't exist: - channel.message_subscribe(queue="invalid-queue") + self.subscribe(queue="invalid-queue", destination="") self.fail("Expected failure when consuming from non-existent queue") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -96,7 +96,7 @@ class MessageTests(TestBase): channel.channel_open() try: #queue not specified and none previously declared for channel: - channel.message_subscribe(queue="") + self.subscribe(channel, queue="", destination="") self.fail("Expected failure when consuming from unspecified queue") except Closed, e: self.assertConnectionException(530, e.args[0]) @@ -110,9 +110,9 @@ class MessageTests(TestBase): channel.queue_declare(queue="test-queue-3", exclusive=True) #check that attempts to use duplicate tags are detected and prevented: - channel.message_subscribe(destination="first", queue="test-queue-3") + self.subscribe(destination="first", queue="test-queue-3") try: - channel.message_subscribe(destination="first", queue="test-queue-3") + self.subscribe(destination="first", queue="test-queue-3") self.fail("Expected consume request to fail due to non-unique tag") except Closed, e: self.assertConnectionException(530, e.args[0]) @@ -124,7 +124,7 @@ class MessageTests(TestBase): channel = self.channel #setup, declare a queue: channel.queue_declare(queue="test-queue-4", exclusive=True) - channel.message_subscribe(destination="my-consumer", queue="test-queue-4") + self.subscribe(destination="my-consumer", queue="test-queue-4") channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="One")) #cancel should stop messages being delivered @@ -150,7 +150,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue="test-ack-queue", exclusive=True) - channel.message_subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) + self.subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="One")) @@ -194,7 +194,7 @@ class MessageTests(TestBase): channel = self.channel channel.queue_declare(queue="test-requeue", exclusive=True) - channel.message_subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) + self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) @@ -225,7 +225,7 @@ class MessageTests(TestBase): #requeue unacked messages (Three and Five) channel.message_recover(requeue=True) - channel.message_subscribe(queue="test-requeue", destination="consumer_tag") + self.subscribe(queue="test-requeue", destination="consumer_tag") queue2 = self.client.queue("consumer_tag") msg3b = queue2.get(timeout=1) @@ -256,7 +256,7 @@ class MessageTests(TestBase): #setup: declare queue and subscribe channel = self.channel channel.queue_declare(queue="test-prefetch-count", exclusive=True) - subscription = channel.message_subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) + subscription = self.subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") #set prefetch to 5: @@ -298,7 +298,7 @@ class MessageTests(TestBase): #setup: declare queue and subscribe channel = self.channel channel.queue_declare(queue="test-prefetch-size", exclusive=True) - subscription = channel.message_subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) + subscription = self.subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") #set prefetch to 50 bytes (each message is 9 or 10 bytes): @@ -345,13 +345,13 @@ class MessageTests(TestBase): channel.queue_declare(queue = "r", exclusive=True) channel.queue_bind(queue = "r", exchange = "amq.fanout") - channel.message_subscribe(queue = "q", destination = "consumer", confirm_mode = 1) + self.subscribe(queue = "q", destination = "consumer", confirm_mode = 1) channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body="blah, blah")) msg = self.client.queue("consumer").get(timeout = 1) self.assertEquals(msg.content.body, "blah, blah") channel.message_reject([msg.command_id, msg.command_id]) - channel.message_subscribe(queue = "r", destination = "checker") + self.subscribe(queue = "r", destination = "checker") msg = self.client.queue("checker").get(timeout = 1) self.assertEquals(msg.content.body, "blah, blah") @@ -365,8 +365,6 @@ class MessageTests(TestBase): #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") - #set credit to zero (can remove this once move to proper default for subscribe method) - channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) @@ -397,8 +395,6 @@ class MessageTests(TestBase): #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") - #set credit to zero (can remove this once move to proper default for subscribe method) - channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) @@ -431,8 +427,6 @@ class MessageTests(TestBase): #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") - #set credit to zero (can remove this once move to proper default for subscribe method) - channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) @@ -465,8 +459,6 @@ class MessageTests(TestBase): #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") - #set credit to zero (can remove this once move to proper default for subscribe method) - channel.message_stop(destination = "c") #send batch of messages to queue for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) @@ -506,8 +498,8 @@ class MessageTests(TestBase): for i in range(1, 6): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) - channel.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) - channel.message_subscribe(queue = "q", destination = "b", acquire_mode = 1) + self.subscribe(queue = "q", destination = "a", acquire_mode = 1) + self.subscribe(queue = "q", destination = "b", acquire_mode = 1) for i in range(6, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) @@ -532,7 +524,7 @@ class MessageTests(TestBase): channel.queue_declare(queue = "q", exclusive=True) channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) - channel.message_subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) + self.subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) msg = self.client.queue("a").get(timeout = 1) channel.message_acquire([msg.command_id, msg.command_id]) msg.complete() @@ -548,7 +540,7 @@ class MessageTests(TestBase): channel.queue_declare(queue = "q", exclusive=True) channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "release me")) - channel.message_subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) + self.subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) msg = self.client.queue("a").get(timeout = 1) channel.message_cancel(destination = "a") channel.message_release([msg.command_id, msg.command_id]) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 05fa1aebc6..e3438116c8 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -49,7 +49,7 @@ class QueueTests(TestBase): #send a further message and consume it, ensuring that the other messages are really gone channel.message_transfer(destination="test-exchange", content=Content("four", properties={'routing_key':"key"})) - channel.message_subscribe(queue="test-queue", destination="tag") + self.subscribe(queue="test-queue", destination="tag") queue = self.client.queue("tag") msg = queue.get(timeout=1) self.assertEqual("four", msg.content.body) @@ -169,8 +169,8 @@ class QueueTests(TestBase): channel.queue_declare(queue="queue-1", exclusive="True") channel.queue_declare(queue="queue-2", exclusive="True") - channel.message_subscribe(queue="queue-1", destination="queue-1") - channel.message_subscribe(queue="queue-2", destination="queue-2") + self.subscribe(queue="queue-1", destination="queue-1") + self.subscribe(queue="queue-2", destination="queue-2") queue1 = self.client.queue("queue-1") queue2 = self.client.queue("queue-2") @@ -257,7 +257,7 @@ class QueueTests(TestBase): channel.channel_open() #empty queue: - channel.message_subscribe(destination="consumer_tag", queue="delete-me-2") + self.subscribe(channel, destination="consumer_tag", queue="delete-me-2") queue = self.client.queue("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.content.body) @@ -282,7 +282,7 @@ class QueueTests(TestBase): #create a queue and register a consumer: channel.queue_declare(queue="delete-me-3") channel.queue_declare(queue="delete-me-3", passive="True") - channel.message_subscribe(destination="consumer_tag", queue="delete-me-3") + self.subscribe(destination="consumer_tag", queue="delete-me-3") #need new channel now: channel2 = self.client.channel(2) diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 7c50de4ee2..2415a88fb2 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -41,13 +41,13 @@ class TxTests(TestBase): channel = self.channel channel.tx_select() - channel.message_subscribe(queue="tx-commit-a", destination="qa", confirm_mode=1) + self.subscribe(channel, queue="tx-commit-a", destination="qa", confirm_mode=1) queue_a = self.client.queue("qa") - channel.message_subscribe(queue="tx-commit-b", destination="qb", confirm_mode=1) + self.subscribe(channel, queue="tx-commit-b", destination="qb", confirm_mode=1) queue_b = self.client.queue("qb") - channel.message_subscribe(queue="tx-commit-c", destination="qc", confirm_mode=1) + self.subscribe(channel, queue="tx-commit-c", destination="qc", confirm_mode=1) queue_c = self.client.queue("qc") #check results @@ -176,7 +176,7 @@ class TxTests(TestBase): channel.tx_select() #consume and ack messages - channel.message_subscribe(queue=name_a, destination="sub_a", confirm_mode=1) + self.subscribe(channel, queue=name_a, destination="sub_a", confirm_mode=1) queue_a = self.client.queue("sub_a") for i in range(1, 5): msg = queue_a.get(timeout=1) @@ -184,13 +184,13 @@ class TxTests(TestBase): msg.complete() - channel.message_subscribe(queue=name_b, destination="sub_b", confirm_mode=1) + self.subscribe(channel, queue=name_b, destination="sub_b", confirm_mode=1) queue_b = self.client.queue("sub_b") msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.content.body) msg.complete() - sub_c = channel.message_subscribe(queue=name_c, destination="sub_c", confirm_mode=1) + sub_c = self.subscribe(channel, queue=name_c, destination="sub_c", confirm_mode=1) queue_c = self.client.queue("sub_c") msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.content.body) -- cgit v1.2.1 From a722b446dd1bd1a1503cf52ce4a73f28eaf4a444 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 13 Sep 2007 17:29:16 +0000 Subject: Use frameset begin/end flags for determining frameset boundaries. Set frameset & segment begin/end flags for content bearing methods (i.e. messages). git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@575377 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 3ae88979fd..d23a3b909e 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -122,7 +122,13 @@ class Connection: def write_0_10(self, frame): c = self.codec - c.encode_octet(0x0f) # TODO: currently fixed at ver=0, B=E=b=e=1 + flags = 0 + if frame.bof: flags |= 0x08 + if frame.eof: flags |= 0x04 + if frame.bos: flags |= 0x02 + if frame.eos: flags |= 0x01 + + c.encode_octet(flags) # TODO: currently fixed at ver=0, B=E=b=e=1 c.encode_octet(self.spec.constants.byname[frame.type].id) body = StringIO() enc = codec.Codec(body, self.spec) @@ -197,6 +203,10 @@ class Frame: def init(self, args, kwargs): self.channel = kwargs.pop("channel", 0) self.subchannel = kwargs.pop("subchannel", 0) + self.bos = True + self.eos = True + self.bof = True + self.eof = True def encode(self, enc): abstract @@ -216,6 +226,7 @@ class Method(Frame): self.method = method self.method_type = method self.args = args + self.eof = not method.content def encode(self, c): c.encode_short(self.method.klass.id) @@ -302,6 +313,8 @@ class Header(Frame): self.weight = weight self.size = size self.properties = properties + self.eof = size == 0 + self.bof = False def __getitem__(self, name): return self.properties[name] @@ -429,6 +442,8 @@ class Body(Frame): def __init__(self, content): self.content = content + self.eof = True + self.bof = False def encode(self, enc): enc.write(self.content) -- cgit v1.2.1 From cd6f6ef2239e49f9fcb1118f4ff7a72cf7ef2b2a Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 18 Sep 2007 12:54:10 +0000 Subject: made testlib use session_open for 0-10 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@576873 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index c2e3024a7e..ce1ba462a0 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -208,7 +208,11 @@ class TestBase(unittest.TestCase): self.exchanges = [] self.client = self.connect() self.channel = self.client.channel(1) - self.channel.channel_open() + version = (self.client.spec.major, self.client.spec.minor) + if version == (8, 0) or "transitional" in self.client.spec.file: + self.channel.channel_open() + else: + self.channel.session_open() def tearDown(self): try: -- cgit v1.2.1 From e73a718ba9b590fbe8b5273c0aac266f4a937a9c Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 19 Sep 2007 13:01:15 +0000 Subject: Added routing key for more readable logs. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@577295 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/execution.py b/qpid/python/tests_0-10/execution.py index f2facfe42b..9541369444 100644 --- a/qpid/python/tests_0-10/execution.py +++ b/qpid/python/tests_0-10/execution.py @@ -24,6 +24,6 @@ class ExecutionTests (TestBase): def test_flush(self): channel = self.channel for i in [1, 2, 3]: - channel.basic_publish() + channel.basic_publish(routing_key=str(i)) channel.execution_flush() assert(channel.completion.wait(channel.completion.command_id, timeout=1)) -- cgit v1.2.1 From 84dc7855c13e29e6fbeb737c63f1495685455c30 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 19 Sep 2007 14:47:48 +0000 Subject: Fixed (il)logic in timeout management when waiting for completion. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@577321 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index b9dd4e466a..5cabf98236 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -434,8 +434,8 @@ class OutgoingCompletion: while not self.closed and point_of_interest > self.mark: #print "waiting for ", point_of_interest, " mark is currently at ", self.mark self.condition.wait(remaining) - if timeout: - if start_time + timeout > time(): break + if not self.closed and point_of_interest > self.mark and timeout: + if (start_time + timeout) < time(): break else: remaining = timeout - (time() - start_time) finally: self.condition.release() -- cgit v1.2.1 From ac502eb4336c57f5f1bf102f2d58f003d71dc82e Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 21 Sep 2007 10:39:36 +0000 Subject: Use octet each for class and method id (changed c++ and python) Modified indexes in xml for message.empty, message.offset and the c++ cluster class Fixed encoding for rfc1982-long-set in c++ and python (its a size not a count that is prepended) Fixed minor typo in configuration option help string Use session.open/close in python tests, handle session.closed Commented out the response tag in session.close due to pythons ambiguity as to whether session.closed is a response or not Disabled broker.test_closed_channel (due to above issue); broker behaves as expected but test fails; test_invalid_channel is safe enough for now. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@578053 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 1 + qpid/python/qpid/client.py | 3 +++ qpid/python/qpid/codec.py | 9 ++++----- qpid/python/qpid/connection.py | 18 +++++++++++++---- qpid/python/qpid/peer.py | 5 +++-- qpid/python/qpid/spec.py | 2 +- qpid/python/qpid/testlib.py | 15 +++++++++----- qpid/python/tests_0-10/broker.py | 22 ++------------------ qpid/python/tests_0-10/dtx.py | 40 ++++++++++++++++++------------------- qpid/python/tests_0-10/exchange.py | 2 +- qpid/python/tests_0-10/execution.py | 2 +- qpid/python/tests_0-10/queue.py | 20 +++++++++---------- qpid/python/tests_0-10/tx.py | 4 ++-- 13 files changed, 72 insertions(+), 71 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 878afee3c5..8a8a3e1b20 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,2 +1,3 @@ tests_0-10.alternate-exchange.AlternateExchangeTests.test_immediate +tests_0-10.broker.BrokerTests.test_closed_channel diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 5734873c6f..f0d7935283 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -138,6 +138,9 @@ class ClientDelegate(Delegate): def channel_close(self, ch, msg): ch.close(msg) + def session_closed(self, ch, msg): + ch.close(msg) + def connection_close(self, ch, msg): self.client.peer.close(msg) diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index c1a912f5d0..d0a95debb3 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -346,23 +346,22 @@ class Codec: return self.decode_long() def encode_rfc1982_long_set(self, s): - self.encode_short(len(s)) + self.encode_short(len(s) * 4) for i in s: self.encode_long(i) def decode_rfc1982_long_set(self): - count = self.decode_short() + count = self.decode_short() / 4 set = [] for i in range(0, count): set.append(self.decode_long()) return set; - #not correct for 0-10 yet def encode_uuid(self, s): - self.encode_longstr(s) + self.pack("16s", s) def decode_uuid(self): - return self.decode_longstr() + return self.unpack("16s") def encode_struct(self, type, s): for f in type.fields: diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index d23a3b909e..98fff9cd2f 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -229,14 +229,24 @@ class Method(Frame): self.eof = not method.content def encode(self, c): - c.encode_short(self.method.klass.id) - c.encode_short(self.method.id) + version = (c.spec.major, c.spec.minor) + if version == (0, 10): + c.encode_octet(self.method.klass.id) + c.encode_octet(self.method.id) + else: + c.encode_short(self.method.klass.id) + c.encode_short(self.method.id) for field, arg in zip(self.method.fields, self.args): c.encode(field.type, arg) def decode(spec, c, size): - klass = spec.classes.byid[c.decode_short()] - meth = klass.methods.byid[c.decode_short()] + version = (c.spec.major, c.spec.minor) + if version == (0, 10): + klass = spec.classes.byid[c.decode_octet()] + meth = klass.methods.byid[c.decode_octet()] + else: + klass = spec.classes.byid[c.decode_short()] + meth = klass.methods.byid[c.decode_short()] args = tuple([c.decode(f.type) for f in meth.fields]) return Method(meth, args) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 5cabf98236..6e91c09806 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -261,7 +261,7 @@ class Channel: self.responder.respond(method, batch, request) def invoke(self, type, args, kwargs): - if type.klass.name == "channel" and (type.name == "close" or type.name == "open"): + if (type.klass.name in ["channel", "session"]) and (type.name in ["close", "open", "closed"]): self.completion.reset() self.incoming_completion.reset() self.completion.next_command(type) @@ -421,6 +421,7 @@ class OutgoingCompletion: self.condition.acquire() try: self.mark = mark + #print "set mark to %s [%s] " % (self.mark, self) self.condition.notifyAll() finally: self.condition.release() @@ -432,7 +433,7 @@ class OutgoingCompletion: self.condition.acquire() try: while not self.closed and point_of_interest > self.mark: - #print "waiting for ", point_of_interest, " mark is currently at ", self.mark + #print "waiting for %s, mark = %s [%s]" % (point_of_interest, self.mark, self) self.condition.wait(remaining) if not self.closed and point_of_interest > self.mark and timeout: if (start_time + timeout) < time(): break diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 3febab7e09..3cb5f0ca25 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -208,7 +208,7 @@ class Method(Metadata): self.response = False def is_l4_command(self): - return self.klass.name not in ["execution", "channel", "connection"] + return self.klass.name not in ["execution", "channel", "connection", "session"] def arguments(self, *args, **kwargs): nargs = len(args) + len(kwargs) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index ce1ba462a0..c4f55be18a 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -208,8 +208,8 @@ class TestBase(unittest.TestCase): self.exchanges = [] self.client = self.connect() self.channel = self.client.channel(1) - version = (self.client.spec.major, self.client.spec.minor) - if version == (8, 0) or "transitional" in self.client.spec.file: + self.version = (self.client.spec.major, self.client.spec.minor) + if self.version == (8, 0): self.channel.channel_open() else: self.channel.session_open() @@ -313,9 +313,14 @@ class TestBase(unittest.TestCase): self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) def assertChannelException(self, expectedCode, message): - if not isinstance(message, Message): self.fail("expected channel_close method, got %s" % (message)) - self.assertEqual("channel", message.method.klass.name) - self.assertEqual("close", message.method.name) + if self.version == (8, 0): #or "transitional" in self.client.spec.file: + if not isinstance(message, Message): self.fail("expected channel_close method, got %s" % (message)) + self.assertEqual("channel", message.method.klass.name) + self.assertEqual("close", message.method.name) + else: + if not isinstance(message, Message): self.fail("expected session_closed method, got %s" % (message)) + self.assertEqual("session", message.method.klass.name) + self.assertEqual("closed", message.method.name) self.assertEqual(expectedCode, message.reply_code) diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 0df7eb09fa..99936ba742 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -101,29 +101,11 @@ class BrokerTests(TestBase): def test_closed_channel(self): channel = self.client.channel(200) - channel.channel_open() - channel.channel_close() + channel.session_open() + channel.session_close() try: channel.queue_declare(exclusive=True) self.fail("Expected error on queue_declare for closed channel") except Closed, e: if isinstance(e.args[0], str): self.fail(e) self.assertConnectionException(504, e.args[0]) - - def test_channel_flow(self): - channel = self.channel - channel.queue_declare(queue="flow_test_queue", exclusive=True) - self.subscribe(destination="my-tag", queue="flow_test_queue") - incoming = self.client.queue("my-tag") - - channel.channel_flow(active=False) - c = Content("abcdefghijklmnopqrstuvwxyz", properties = {"routing_key" : "flow_test_queue"}) - channel.message_transfer(content = c) - try: - incoming.get(timeout=1) - self.fail("Received message when flow turned off.") - except Empty: None - - channel.channel_flow(active=True) - msg = incoming.get(timeout=1) - self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.content.body) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index b5645cb596..000eddff21 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -43,8 +43,9 @@ class DtxTests(TestBase): tx_counter = 0 def reset_channel(self): - self.channel.channel_close() - self.channel.channel_open() + self.channel.session_close() + self.channel = self.client.channel(self.channel.id + 1) + self.channel.session_open() def test_simple_commit(self): """ @@ -171,7 +172,7 @@ class DtxTests(TestBase): other = self.connect() channel2 = other.channel(1) - channel2.channel_open() + channel2.session_open() channel2.dtx_demarcation_select() #create a xid @@ -202,17 +203,16 @@ class DtxTests(TestBase): Verify that a xid is 'forgotten' - and can therefore be used again - once it is completed. """ - channel = self.channel #do some transactional work & complete the transaction self.test_simple_commit() # channel has been reset, so reselect for use with dtx - channel.dtx_demarcation_select() + self.channel.dtx_demarcation_select() #start association for the same xid as the previously completed txn tx = self.xid("my-xid") - channel.dtx_demarcation_start(xid=tx) - channel.dtx_demarcation_end(xid=tx) - channel.dtx_coordination_rollback(xid=tx) + self.channel.dtx_demarcation_start(xid=tx) + self.channel.dtx_demarcation_end(xid=tx) + self.channel.dtx_coordination_rollback(xid=tx) def test_start_join_and_resume(self): """ @@ -242,7 +242,7 @@ class DtxTests(TestBase): channel1.dtx_demarcation_select() channel2 = self.client.channel(2) - channel2.channel_open() + channel2.session_open() channel2.dtx_demarcation_select() #setup @@ -323,9 +323,9 @@ class DtxTests(TestBase): #cleanup other = self.connect() channel = other.channel(1) - channel.channel_open() + channel.session_open() channel.dtx_coordination_rollback(xid=tx) - channel.channel_close() + channel.session_close() other.close() @@ -351,7 +351,7 @@ class DtxTests(TestBase): operations are non-transactional """ channel = self.client.channel(2) - channel.channel_open() + channel.session_open() channel.queue_declare(queue="tx-queue", exclusive=True) #publish a message under a transaction @@ -372,7 +372,7 @@ class DtxTests(TestBase): channel.message_cancel(destination="results") #ack the message then close the channel msg.complete() - channel.channel_close() + channel.session_close() channel = self.channel #commit the transaction and check that the first message (and @@ -388,7 +388,7 @@ class DtxTests(TestBase): """ other = self.connect() tester = other.channel(1) - tester.channel_open() + tester.session_open() tester.queue_declare(queue="dummy", exclusive=True) tester.dtx_demarcation_select() tx = self.xid("dummy") @@ -407,7 +407,7 @@ class DtxTests(TestBase): self.channel.dtx_coordination_rollback(xid=tx) self.assertConnectionException(503, e.args[0]) else: - tester.channel_close() + tester.session_close() other.close() self.fail("Invalid use of one_phase=True, expected exception!") @@ -422,7 +422,7 @@ class DtxTests(TestBase): """ other = self.connect() tester = other.channel(1) - tester.channel_open() + tester.session_open() tester.queue_declare(queue="dummy", exclusive=True) tester.dtx_demarcation_select() tx = self.xid("dummy") @@ -440,7 +440,7 @@ class DtxTests(TestBase): self.channel.dtx_coordination_rollback(xid=tx) self.assertConnectionException(503, e.args[0]) else: - tester.channel_close() + tester.session_close() other.close() self.fail("Invalid use of one_phase=False, expected exception!") @@ -452,7 +452,7 @@ class DtxTests(TestBase): """ channel1 = self.channel channel2 = self.client.channel(2) - channel2.channel_open() + channel2.session_open() #setup: channel2.queue_declare(queue="dummy", exclusive=True) @@ -464,7 +464,7 @@ class DtxTests(TestBase): channel2.message_get(queue="dummy", destination="dummy") self.client.queue("dummy").get(timeout=1).complete() channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - channel2.channel_close() + channel2.session_close() self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).status) channel1.dtx_coordination_rollback(xid=tx) @@ -492,7 +492,7 @@ class DtxTests(TestBase): """ #open new channel to allow self.channel to be used in checking te queue channel = self.client.channel(2) - channel.channel_open() + channel.session_open() #setup: tx = self.xid("dummy") channel.queue_declare(queue="queue-a", exclusive=True) diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 4137eb7a51..4d8b254df7 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -322,6 +322,6 @@ class MiscellaneousErrorsTests(TestBase): #cleanup other = self.connect() c2 = other.channel(1) - c2.channel_open() + c2.session_open() c2.exchange_delete(exchange="test_different_declared_type_exchange") diff --git a/qpid/python/tests_0-10/execution.py b/qpid/python/tests_0-10/execution.py index 9541369444..950ff59d97 100644 --- a/qpid/python/tests_0-10/execution.py +++ b/qpid/python/tests_0-10/execution.py @@ -25,5 +25,5 @@ class ExecutionTests (TestBase): channel = self.channel for i in [1, 2, 3]: channel.basic_publish(routing_key=str(i)) - channel.execution_flush() + #channel.execution_flush() assert(channel.completion.wait(channel.completion.command_id, timeout=1)) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index e3438116c8..ba017bb286 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -56,7 +56,7 @@ class QueueTests(TestBase): #check error conditions (use new channels): channel = self.client.channel(2) - channel.channel_open() + channel.session_open() try: #queue specified but doesn't exist: channel.queue_purge(queue="invalid-queue") @@ -65,7 +65,7 @@ class QueueTests(TestBase): self.assertChannelException(404, e.args[0]) channel = self.client.channel(3) - channel.channel_open() + channel.session_open() try: #queue not specified and none previously declared for channel: channel.queue_purge() @@ -76,7 +76,7 @@ class QueueTests(TestBase): #cleanup other = self.connect() channel = other.channel(1) - channel.channel_open() + channel.session_open() channel.exchange_delete(exchange="test-exchange") def test_declare_exclusive(self): @@ -88,7 +88,7 @@ class QueueTests(TestBase): # Here we open a second separate connection: other = self.connect() c2 = other.channel(1) - c2.channel_open() + c2.session_open() #declare an exclusive queue: c1.queue_declare(queue="exclusive-queue", exclusive="True") @@ -141,7 +141,7 @@ class QueueTests(TestBase): #need to reopen a channel: channel = self.client.channel(2) - channel.channel_open() + channel.session_open() #try and bind non-existant queue: try: @@ -225,7 +225,7 @@ class QueueTests(TestBase): #check attempted deletion of non-existant queue is handled correctly: channel = self.client.channel(2) - channel.channel_open() + channel.session_open() try: channel.queue_delete(queue="i-dont-exist", if_empty="True") self.fail("Expected delete of non-existant queue to fail") @@ -254,7 +254,7 @@ class QueueTests(TestBase): #need new channel now: channel = self.client.channel(2) - channel.channel_open() + channel.session_open() #empty queue: self.subscribe(channel, destination="consumer_tag", queue="delete-me-2") @@ -286,7 +286,7 @@ class QueueTests(TestBase): #need new channel now: channel2 = self.client.channel(2) - channel2.channel_open() + channel2.session_open() #try to delete, but only if empty: try: channel2.queue_delete(queue="delete-me-3", if_unused="True") @@ -312,7 +312,7 @@ class QueueTests(TestBase): channel = self.channel other = self.connect() channel2 = other.channel(1) - channel2.channel_open() + channel2.session_open() channel.queue_declare(queue="auto-delete-me", auto_delete=True) @@ -321,7 +321,7 @@ class QueueTests(TestBase): channel2.basic_consume(queue="auto-delete-me") #implicit cancel - channel2.channel_close() + channel2.session_close() #check it is still there channel.queue_declare(queue="auto-delete-me", passive=True) diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 2415a88fb2..84c07d51c1 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -31,10 +31,10 @@ class TxTests(TestBase): Test that commited publishes are delivered and commited acks are not re-delivered """ channel2 = self.client.channel(2) - channel2.channel_open() + channel2.session_open() self.perform_txn_work(channel2, "tx-commit-a", "tx-commit-b", "tx-commit-c") channel2.tx_commit() - channel2.channel_close() + channel2.session_close() #use a different channel with new subscriptions to ensure #there is no redelivery of acked messages: -- cgit v1.2.1 From fd4e234dea02bdef1c4db2bea061360de015c8f5 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 21 Sep 2007 15:13:52 +0000 Subject: Moved c++ over to using the same preview file for 0-10 work as java. Removed all channel class related code from broker as a result. Did the same for some python tests I missed earlier. Renamed ChannelAdapter to ChannelHandler. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@578167 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/alternate-exchange.py | 8 ++++---- qpid/python/tests_0-10/dtx.py | 10 ++++++++-- qpid/python/tests_0-10/message.py | 4 ++-- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py index a749d733b0..a857496a48 100644 --- a/qpid/python/tests_0-10/alternate-exchange.py +++ b/qpid/python/tests_0-10/alternate-exchange.py @@ -136,9 +136,9 @@ class AlternateExchangeTests(TestBase): #cleanup: other = self.connect() channel = other.channel(1) - channel.channel_open() + channel.session_open() channel.exchange_delete(exchange="alternate") - channel.channel_close(200, "ok") + channel.session_close() other.close() self.assertConnectionException(530, e.args[0]) @@ -162,10 +162,10 @@ class AlternateExchangeTests(TestBase): #cleanup: other = self.connect() channel = other.channel(1) - channel.channel_open() + channel.session_open() channel.exchange_delete(exchange="e") channel.exchange_delete(exchange="alternate") - channel.channel_close(200, "ok") + channel.session_close() other.close() self.assertConnectionException(530, e.args[0]) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 000eddff21..ce6e467e0a 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -461,8 +461,11 @@ class DtxTests(TestBase): channel2.dtx_demarcation_select() channel2.dtx_demarcation_start(xid=tx) - channel2.message_get(queue="dummy", destination="dummy") + channel2.message_subscribe(queue="dummy", destination="dummy", confirm_mode=1) + channel2.message_flow(destination="dummy", unit=0, value=1) + channel2.message_flow(destination="dummy", unit=1, value=0xFFFFFFFF) self.client.queue("dummy").get(timeout=1).complete() + channel2.message_cancel(destination="dummy") channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) channel2.session_close() @@ -589,8 +592,11 @@ class DtxTests(TestBase): def swap(self, channel, src, dest): #consume from src: - channel.message_get(destination="temp-swap", queue=src) + channel.message_subscribe(destination="temp-swap", queue=src, confirm_mode=1) + channel.message_flow(destination="temp-swap", unit=0, value=1) + channel.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) msg = self.client.queue("temp-swap").get(timeout=1) + channel.message_cancel(destination="temp-swap") msg.complete(); #re-publish to dest diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index ba26dda309..b5b058340f 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -70,7 +70,7 @@ class MessageTests(TestBase): #open new channel and cleanup last consumer: channel = self.client.channel(2) - channel.channel_open() + channel.session_open() #check that an exclusive consumer cannot be created if a consumer already exists: self.subscribe(channel, destination="first", queue="test-queue-2") @@ -93,7 +93,7 @@ class MessageTests(TestBase): self.assertChannelException(404, e.args[0]) channel = self.client.channel(2) - channel.channel_open() + channel.session_open() try: #queue not specified and none previously declared for channel: self.subscribe(channel, queue="", destination="") -- cgit v1.2.1 From 67560040f3d825b0902f95858ea06e6b684a92f2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 26 Sep 2007 10:08:15 +0000 Subject: Change to start execution mark at -1 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@579559 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 6e91c09806..68e1883692 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -395,9 +395,10 @@ class OutgoingCompletion: def __init__(self): self.condition = threading.Condition() - self.sequence = Sequence(1) #issues ids for outgoing commands - self.command_id = 0 #last issued id - self.mark = 0 #commands up to this mark are known to be complete + #todo, implement proper wraparound + self.sequence = Sequence(0) #issues ids for outgoing commands + self.command_id = -1 #last issued id + self.mark = -1 #commands up to this mark are known to be complete self.closed = False def next_command(self, method): @@ -406,7 +407,7 @@ class OutgoingCompletion: self.command_id = self.sequence.next() def reset(self): - self.sequence = Sequence(1) #reset counter + self.sequence = Sequence(0) #reset counter def close(self): self.reset() @@ -448,12 +449,12 @@ class IncomingCompletion: """ def __init__(self, channel): - self.sequence = Sequence(1) #issues ids for incoming commands - self.mark = 0 #id of last command of whose completion notification was sent to the other peer + self.sequence = Sequence(0) #issues ids for incoming commands + self.mark = -1 #id of last command of whose completion notification was sent to the other peer self.channel = channel def reset(self): - self.sequence = Sequence(1) #reset counter + self.sequence = Sequence(0) #reset counter def complete(self, mark, cumulative=True): if cumulative: @@ -463,4 +464,7 @@ class IncomingCompletion: else: #TODO: record and manage the ranges properly range = [mark, mark] - self.channel.execution_complete(cumulative_execution_mark=self.mark, ranged_execution_set=range) + if (self.mark == -1):#hack until wraparound is implemented + self.channel.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=range) + else: + self.channel.execution_complete(cumulative_execution_mark=self.mark, ranged_execution_set=range) -- cgit v1.2.1 From 2905b744f1066751e0c05178da475ff4da13440d Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 27 Sep 2007 18:56:23 +0000 Subject: added support for 0-10 field table encoding git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@580122 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 96 +++++++++++++++++++++++++++++++++++++++------- qpid/python/qpid/spec.py | 7 ++-- qpid/python/tests/codec.py | 7 ---- 3 files changed, 87 insertions(+), 23 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index d0a95debb3..f6b0f980cb 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -34,6 +34,11 @@ from reference import ReferenceId class EOF(Exception): pass +TYPE_ALIASES = { + "long_string": "longstr", + "unsigned_int": "long" + } + class Codec: """ @@ -51,6 +56,40 @@ class Codec: self.incoming_bits = [] self.outgoing_bits = [] + self.types = {} + self.codes = {} + self.encodings = { + basestring: "longstr", + int: "long", + long: "long", + None.__class__:"void", + list: "sequence", + tuple: "sequence", + dict: "table" + } + + if False: + for constant in self.spec.constants: + if constant.klass == "field-table-type": + type = constant.name.replace("field_table_", "") + self.typecode(constant.id, TYPE_ALIASES.get(type, type)) + + if not self.types: + self.typecode(ord('S'), "longstr") + self.typecode(ord('I'), "long") + + def typecode(self, code, type): + self.types[code] = type + self.codes[type] = code + + def resolve(self, klass): + if self.encodings.has_key(klass): + return self.encodings[klass] + for base in klass.__bases__: + result = self.resolve(base) + if result != None: + return result + def read(self, n): """ reads in 'n' bytes from the stream. Can raise EOF exception @@ -265,15 +304,14 @@ class Codec: codec = Codec(enc, self.spec) if tbl: for key, value in tbl.items(): - if len(key) > 128: + if self.spec.major == 8 and self.spec.minor == 0 and len(key) > 128: raise ValueError("field table key too long: '%s'" % key) + type = self.resolve(value.__class__) + if type == None: + raise ValueError("no encoding for: " + value.__class__) codec.encode_shortstr(key) - if isinstance(value, basestring): - codec.write("S") - codec.encode_longstr(value) - else: - codec.write("I") - codec.encode_long(value) + codec.encode_octet(self.codes[type]) + codec.encode(type, value) s = enc.getvalue() self.encode_long(len(s)) self.write(s) @@ -287,13 +325,21 @@ class Codec: result = {} while self.nread - start < size: key = self.decode_shortstr() - type = self.read(1) - if type == "S": - value = self.decode_longstr() - elif type == "I": - value = self.decode_long() + code = self.decode_octet() + if self.types.has_key(code): + value = self.decode(self.types[code]) else: - raise ValueError(repr(type)) + w = width(code) + if fixed(code): + value = self.read(w) + elif w == 1: + value = self.decode_shortstr() + elif w == 2: + value = self.dec_str("!H") + elif w == 4: + value = self.decode_longstr() + else: + raise ValueError("illegal width: " + w) result[key] = value return result @@ -390,3 +436,27 @@ class Codec: codec = Codec(StringIO(self.decode_longstr()), self.spec) type = self.spec.structs[codec.decode_short()] return codec.decode_struct(type) + +def fixed(code): + return (code >> 6) != 2 + +def width(code): + # decimal + if code >= 192: + decsel = (code >> 4) & 3 + if decsel == 0: + return 5 + elif decsel == 1: + return 9 + elif decsel == 3: + return 0 + else: + raise ValueError(code) + # variable width + elif code < 192 and code >= 128: + lenlen = (self.code >> 4) & 3 + if lenlen == 3: raise ValueError(code) + return 2 ** lenlen + # fixed width + else: + return (self.code >> 4) & 7 diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 3cb5f0ca25..2542ccc3e1 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -348,9 +348,10 @@ def load(specfile, *errata): # constants for nd in root.query["constant"]: val = nd["@value"] - if val.startswith("0x"): continue - const = Constant(spec, pythonize(nd["@name"]), int(val), - nd["@class"], get_docs(nd)) + if val.startswith("0x"): val = int(val, 16) + else: val = int(val) + const = Constant(spec, pythonize(nd["@name"]), val, nd["@class"], + get_docs(nd)) try: spec.constants.add(const) except ValueError, e: diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py index fbaf05fc20..e23c3deca1 100644 --- a/qpid/python/tests/codec.py +++ b/qpid/python/tests/codec.py @@ -442,13 +442,6 @@ class FieldTableTestCase(BaseDataTypes): """ self.failUnlessEqual(self.callFunc('encode_table', {'$key1':'value1'}), '\x00\x00\x00\x11\x05$key1S\x00\x00\x00\x06value1', 'valid name value pair encoding FAILED...') - # ---------------------------------------------------- - def test_field_table_invalid_field_name_length(self): - """ - field names can have a maximum length of 128 chars - """ - self.failUnlessRaises(Exception, self.codec.encode_table, {'x'*129:'value1'}) - # --------------------------------------------------- def test_field_table_multiple_name_value_pair(self): """ -- cgit v1.2.1 From 97218597faf441586934f8278faaedb0054a7fa8 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 27 Sep 2007 18:57:22 +0000 Subject: issue execution_sync rather than execution_flush prior to waiting for completion git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@580123 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/peer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 68e1883692..5c05e71cf1 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -281,7 +281,7 @@ class Channel: self.request(frame, self.queue_response, content) if not frame.method.responses: if self.use_execution_layer and frame.method_type.is_l4_command(): - self.execution_flush() + self.execution_sync() self.completion.wait() if self.closed: raise Closed(self.reason) @@ -335,12 +335,12 @@ class Channel: return future elif self.synchronous and not frame.method.response \ and self.use_execution_layer and frame.method.is_l4_command(): - self.execution_flush() + self.execution_sync() completed = self.completion.wait(timeout=10) if self.closed: raise Closed(self.reason) if not completed: - self.close("Timed-out waiting for completion") + self.close("Timed-out waiting for completion of %s" % frame) except QueueClosed, e: if self.closed: raise Closed(self.reason) -- cgit v1.2.1 From da12edf961df4c43cc7db5fa161183a8281a5ca6 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 2 Oct 2007 09:54:59 +0000 Subject: Fixed recovery; unacked message records are now updated to hold the new command id when messages are resent. Added unit and python test as previous bug was not being picked up by the existing tests. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@581175 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index b5b058340f..967c03bbea 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -187,6 +187,46 @@ class MessageTests(TestBase): self.fail("Got unexpected message: " + extra.content.body) except Empty: None + + def test_recover(self): + """ + Test recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="queue-a", exclusive=True) + channel.queue_bind(exchange="amq.fanout", queue="queue-a") + channel.queue_declare(queue="queue-b", exclusive=True) + channel.queue_bind(exchange="amq.fanout", queue="queue-b") + + self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) + self.subscribe(queue="queue-b", destination="confirmed", confirm_mode=0) + confirmed = self.client.queue("confirmed") + unconfirmed = self.client.queue("unconfirmed") + + data = ["One", "Two", "Three", "Four", "Five"] + for d in data: + channel.message_transfer(destination="amq.fanout", content=Content(body=d)) + + for q in [confirmed, unconfirmed]: + for d in data: + self.assertEqual(d, q.get(timeout=1).content.body) + self.assertEmpty(q) + + channel.message_recover(requeue=False) + + self.assertEmpty(confirmed) + + while len(data): + msg = None + for d in data: + msg = unconfirmed.get(timeout=1) + self.assertEqual(d, msg.content.body) + self.assertEmpty(unconfirmed) + data.remove(msg.content.body) + msg.complete(cumulative=False) + channel.message_recover(requeue=False) + + def test_recover_requeue(self): """ Test requeing on recovery @@ -551,3 +591,9 @@ class MessageTests(TestBase): def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) + + def assertEmpty(self, queue): + try: + extra = queue.get(timeout=1) + self.fail("Queue not empty, contains: " + extra.content.body) + except Empty: None -- cgit v1.2.1 From a2bf1ed0aefab8ef3cd1236e2ce1a89b0ce0ba1b Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 4 Oct 2007 12:46:42 +0000 Subject: Added test for ordering of released messages git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@581880 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 967c03bbea..6931f83a6b 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -589,6 +589,30 @@ class MessageTests(TestBase): #message should not have been removed from the queue: self.assertEquals(1, channel.queue_query(queue = "q").message_count) + def test_release_ordering(self): + """ + Test order of released messages is as expected + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + for i in range (1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "released message %s" % (i))) + + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + channel.message_flow(unit = 0, value = 10, destination = "a") + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + queue = self.client.queue("a") + first = queue.get(timeout = 1) + for i in range (2, 10): + self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + last = queue.get(timeout = 1) + self.assertEmpty(queue) + channel.message_release([first.command_id, last.command_id]) + last.complete()#will re-allocate credit, as in window mode + for i in range (1, 11): + self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + + def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) -- cgit v1.2.1 From e15418d64f9b44de937b3a4f4ba08ec0a5f58c03 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 4 Oct 2007 16:06:05 +0000 Subject: Additional tests and fixes git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@581957 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 6931f83a6b..416eaeba3e 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -612,6 +612,29 @@ class MessageTests(TestBase): for i in range (1, 11): self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + def test_ranged_ack(self): + """ + Test acking of messages ranges + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True) + for i in range (1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message %s" % (i))) + + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + channel.message_flow(unit = 0, value = 10, destination = "a") + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + queue = self.client.queue("a") + for i in range (1, 11): + self.assertEquals("message %s" % (i), queue.get(timeout = 1).content.body) + self.assertEmpty(queue) + + #ack all but the third message (command id 2) + channel.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=[0,1,3,6,7,7,8,9]) + channel.message_recover() + self.assertEquals("message 3", queue.get(timeout = 1).content.body) + self.assertEmpty(queue) + def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) -- cgit v1.2.1 From ffce54c2550253757dbd208d7b1d4602d08140b8 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 11 Oct 2007 08:44:16 +0000 Subject: Ensure recovered messages have the redelivered flag set git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@583740 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 1 + 1 file changed, 1 insertion(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 416eaeba3e..3e014838aa 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -221,6 +221,7 @@ class MessageTests(TestBase): for d in data: msg = unconfirmed.get(timeout=1) self.assertEqual(d, msg.content.body) + self.assertEqual(True, msg.content['redelivered']) self.assertEmpty(unconfirmed) data.remove(msg.content.body) msg.complete(cumulative=False) -- cgit v1.2.1 From 28cdc6dc38269d56896d981a2accb691e554efda Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 11 Oct 2007 13:29:37 +0000 Subject: Exclusive no longer implies auto-delete on queue.declare. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@583821 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/alternate-exchange.py | 12 ++++---- qpid/python/tests_0-10/dtx.py | 26 ++++++++-------- qpid/python/tests_0-10/message.py | 44 ++++++++++++++-------------- qpid/python/tests_0-10/query.py | 12 ++++---- qpid/python/tests_0-10/queue.py | 38 ++++++++++++------------ qpid/python/tests_0-10/tx.py | 6 ++-- 6 files changed, 69 insertions(+), 69 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py index a857496a48..83f8d85811 100644 --- a/qpid/python/tests_0-10/alternate-exchange.py +++ b/qpid/python/tests_0-10/alternate-exchange.py @@ -37,13 +37,13 @@ class AlternateExchangeTests(TestBase): channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages - channel.queue_declare(queue="returns", exclusive=True) + channel.queue_declare(queue="returns", exclusive=True, auto_delete=True) channel.queue_bind(queue="returns", exchange="secondary") self.subscribe(destination="a", queue="returns") returned = self.client.queue("a") #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages - channel.queue_declare(queue="processed", exclusive=True) + channel.queue_declare(queue="processed", exclusive=True, auto_delete=True) channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") self.subscribe(destination="b", queue="processed") processed = self.client.queue("b") @@ -71,7 +71,7 @@ class AlternateExchangeTests(TestBase): channel = self.channel #set up a 'dead letter queue': channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="deleted", exclusive=True) + channel.queue_declare(queue="deleted", exclusive=True, auto_delete=True) channel.queue_bind(exchange="dlq", queue="deleted") self.subscribe(destination="dlq", queue="deleted") dlq = self.client.queue("dlq") @@ -101,13 +101,13 @@ class AlternateExchangeTests(TestBase): channel = self.channel #set up a 'dead letter queue': channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="immediate", exclusive=True) + channel.queue_declare(queue="immediate", exclusive=True, auto_delete=True) channel.queue_bind(exchange="dlq", queue="immediate") self.subscribe(destination="dlq", queue="immediate") dlq = self.client.queue("dlq") #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True) + channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True, auto_delete=True) #send it some messages: #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) @@ -128,7 +128,7 @@ class AlternateExchangeTests(TestBase): """ channel = self.channel channel.exchange_declare(exchange="alternate", type="fanout") - channel.queue_declare(queue="q", exclusive=True, alternate_exchange="alternate") + channel.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") try: channel.exchange_delete(exchange="alternate") self.fail("Expected deletion of in-use alternate-exchange to fail") diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index ce6e467e0a..8fdd32c2f5 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -246,8 +246,8 @@ class DtxTests(TestBase): channel2.dtx_demarcation_select() #setup - channel1.queue_declare(queue="one", exclusive=True) - channel1.queue_declare(queue="two", exclusive=True) + channel1.queue_declare(queue="one", exclusive=True, auto_delete=True) + channel1.queue_declare(queue="two", exclusive=True, auto_delete=True) channel1.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) channel1.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) @@ -282,8 +282,8 @@ class DtxTests(TestBase): channel.dtx_demarcation_select() #setup - channel.queue_declare(queue="one", exclusive=True) - channel.queue_declare(queue="two", exclusive=True) + channel.queue_declare(queue="one", exclusive=True, auto_delete=True) + channel.queue_declare(queue="two", exclusive=True, auto_delete=True) channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) @@ -352,7 +352,7 @@ class DtxTests(TestBase): """ channel = self.client.channel(2) channel.session_open() - channel.queue_declare(queue="tx-queue", exclusive=True) + channel.queue_declare(queue="tx-queue", exclusive=True, auto_delete=True) #publish a message under a transaction channel.dtx_demarcation_select() @@ -389,7 +389,7 @@ class DtxTests(TestBase): other = self.connect() tester = other.channel(1) tester.session_open() - tester.queue_declare(queue="dummy", exclusive=True) + tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) tester.dtx_demarcation_select() tx = self.xid("dummy") tester.dtx_demarcation_start(xid=tx) @@ -423,7 +423,7 @@ class DtxTests(TestBase): other = self.connect() tester = other.channel(1) tester.session_open() - tester.queue_declare(queue="dummy", exclusive=True) + tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) tester.dtx_demarcation_select() tx = self.xid("dummy") tester.dtx_demarcation_start(xid=tx) @@ -455,7 +455,7 @@ class DtxTests(TestBase): channel2.session_open() #setup: - channel2.queue_declare(queue="dummy", exclusive=True) + channel2.queue_declare(queue="dummy", exclusive=True, auto_delete=True) channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) tx = self.xid("dummy") @@ -498,8 +498,8 @@ class DtxTests(TestBase): channel.session_open() #setup: tx = self.xid("dummy") - channel.queue_declare(queue="queue-a", exclusive=True) - channel.queue_declare(queue="queue-b", exclusive=True) + channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) + channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':"timeout"}, body="DtxMessage")) channel.dtx_demarcation_select() @@ -524,7 +524,7 @@ class DtxTests(TestBase): channel = self.channel channel.dtx_demarcation_select() - channel.queue_declare(queue="dummy", exclusive=True) + channel.queue_declare(queue="dummy", exclusive=True, auto_delete=True) prepared = [] for i in range(1, 10): @@ -575,8 +575,8 @@ class DtxTests(TestBase): def txswap(self, tx, id): channel = self.channel #declare two queues: - channel.queue_declare(queue="queue-a", exclusive=True) - channel.queue_declare(queue="queue-b", exclusive=True) + channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) + channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) #put message with specified id on one queue: channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':id}, body="DtxMessage")) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 3e014838aa..c414b15214 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -31,8 +31,8 @@ class MessageTests(TestBase): """ channel = self.channel #setup, declare two queues: - channel.queue_declare(queue="test-queue-1a", exclusive=True) - channel.queue_declare(queue="test-queue-1b", exclusive=True) + channel.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True) #establish two consumers one of which excludes delivery of locally sent messages self.subscribe(destination="local_included", queue="test-queue-1a") self.subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) @@ -58,7 +58,7 @@ class MessageTests(TestBase): """ channel = self.channel #setup, declare a queue: - channel.queue_declare(queue="test-queue-2", exclusive=True) + channel.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) #check that an exclusive consumer prevents other consumer being created: self.subscribe(destination="first", queue="test-queue-2", exclusive=True) @@ -107,7 +107,7 @@ class MessageTests(TestBase): """ channel = self.channel #setup, declare a queue: - channel.queue_declare(queue="test-queue-3", exclusive=True) + channel.queue_declare(queue="test-queue-3", exclusive=True, auto_delete=True) #check that attempts to use duplicate tags are detected and prevented: self.subscribe(destination="first", queue="test-queue-3") @@ -123,7 +123,7 @@ class MessageTests(TestBase): """ channel = self.channel #setup, declare a queue: - channel.queue_declare(queue="test-queue-4", exclusive=True) + channel.queue_declare(queue="test-queue-4", exclusive=True, auto_delete=True) self.subscribe(destination="my-consumer", queue="test-queue-4") channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="One")) @@ -148,7 +148,7 @@ class MessageTests(TestBase): Test basic ack/recover behaviour """ channel = self.channel - channel.queue_declare(queue="test-ack-queue", exclusive=True) + channel.queue_declare(queue="test-ack-queue", exclusive=True, auto_delete=True) self.subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") @@ -193,9 +193,9 @@ class MessageTests(TestBase): Test recover behaviour """ channel = self.channel - channel.queue_declare(queue="queue-a", exclusive=True) + channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) channel.queue_bind(exchange="amq.fanout", queue="queue-a") - channel.queue_declare(queue="queue-b", exclusive=True) + channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) channel.queue_bind(exchange="amq.fanout", queue="queue-b") self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) @@ -233,7 +233,7 @@ class MessageTests(TestBase): Test requeing on recovery """ channel = self.channel - channel.queue_declare(queue="test-requeue", exclusive=True) + channel.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") @@ -296,7 +296,7 @@ class MessageTests(TestBase): """ #setup: declare queue and subscribe channel = self.channel - channel.queue_declare(queue="test-prefetch-count", exclusive=True) + channel.queue_declare(queue="test-prefetch-count", exclusive=True, auto_delete=True) subscription = self.subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") @@ -338,7 +338,7 @@ class MessageTests(TestBase): """ #setup: declare queue and subscribe channel = self.channel - channel.queue_declare(queue="test-prefetch-size", exclusive=True) + channel.queue_declare(queue="test-prefetch-size", exclusive=True, auto_delete=True) subscription = self.subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") @@ -382,8 +382,8 @@ class MessageTests(TestBase): def test_reject(self): channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, alternate_exchange="amq.fanout") - channel.queue_declare(queue = "r", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True, alternate_exchange="amq.fanout") + channel.queue_declare(queue = "r", exclusive=True, auto_delete=True) channel.queue_bind(queue = "r", exchange = "amq.fanout") self.subscribe(queue = "q", destination = "consumer", confirm_mode = 1) @@ -402,7 +402,7 @@ class MessageTests(TestBase): """ #declare an exclusive queue channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") @@ -432,7 +432,7 @@ class MessageTests(TestBase): """ #declare an exclusive queue channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") @@ -464,7 +464,7 @@ class MessageTests(TestBase): """ #declare an exclusive queue channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") @@ -496,7 +496,7 @@ class MessageTests(TestBase): """ #declare an exclusive queue channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") @@ -535,7 +535,7 @@ class MessageTests(TestBase): #existing tests twice. channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 6): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) @@ -562,7 +562,7 @@ class MessageTests(TestBase): Test explicit acquire function """ channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) self.subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) @@ -578,7 +578,7 @@ class MessageTests(TestBase): Test explicit release function """ channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "release me")) self.subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) @@ -595,7 +595,7 @@ class MessageTests(TestBase): Test order of released messages is as expected """ channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range (1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "released message %s" % (i))) @@ -618,7 +618,7 @@ class MessageTests(TestBase): Test acking of messages ranges """ channel = self.channel - channel.queue_declare(queue = "q", exclusive=True) + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range (1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message %s" % (i))) diff --git a/qpid/python/tests_0-10/query.py b/qpid/python/tests_0-10/query.py index 06f33be85b..eba2ee6dd1 100644 --- a/qpid/python/tests_0-10/query.py +++ b/qpid/python/tests_0-10/query.py @@ -65,8 +65,8 @@ class QueryTests(TestBase): def binding_query_with_key(self, exchange_name): channel = self.channel #setup: create two queues - channel.queue_declare(queue="used-queue", exclusive=True) - channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) channel.queue_bind(exchange=exchange_name, queue="used-queue", routing_key="used-key") @@ -135,8 +135,8 @@ class QueryTests(TestBase): """ channel = self.channel #setup - channel.queue_declare(queue="used-queue", exclusive=True) - channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) channel.queue_bind(exchange="amq.fanout", queue="used-queue") # test detection of any binding to specific queue @@ -163,8 +163,8 @@ class QueryTests(TestBase): """ channel = self.channel #setup - channel.queue_declare(queue="used-queue", exclusive=True) - channel.queue_declare(queue="unused-queue", exclusive=True) + channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) # test detection of any binding to specific queue diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index ba017bb286..d72d12f92d 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -31,7 +31,7 @@ class QueueTests(TestBase): channel = self.channel #setup, declare a queue and add some messages to it: channel.exchange_declare(exchange="test-exchange", type="direct") - channel.queue_declare(queue="test-queue", exclusive=True) + channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") channel.message_transfer(destination="test-exchange", content=Content("one", properties={'routing_key':"key"})) channel.message_transfer(destination="test-exchange", content=Content("two", properties={'routing_key':"key"})) @@ -91,10 +91,10 @@ class QueueTests(TestBase): c2.session_open() #declare an exclusive queue: - c1.queue_declare(queue="exclusive-queue", exclusive="True") + c1.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) try: #other connection should not be allowed to declare this: - c2.queue_declare(queue="exclusive-queue", exclusive="True") + c2.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) self.fail("Expected second exclusive queue_declare to raise a channel exception") except Closed, e: self.assertChannelException(405, e.args[0]) @@ -106,11 +106,11 @@ class QueueTests(TestBase): """ channel = self.channel #declare an exclusive queue: - channel.queue_declare(queue="passive-queue-1", exclusive="True") - channel.queue_declare(queue="passive-queue-1", passive="True") + channel.queue_declare(queue="passive-queue-1", exclusive=True, auto_delete=True) + channel.queue_declare(queue="passive-queue-1", passive=True) try: #other connection should not be allowed to declare this: - channel.queue_declare(queue="passive-queue-2", passive="True") + channel.queue_declare(queue="passive-queue-2", passive=True) self.fail("Expected passive declaration of non-existant queue to raise a channel exception") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -121,7 +121,7 @@ class QueueTests(TestBase): Test various permutations of the queue.bind method """ channel = self.channel - channel.queue_declare(queue="queue-1", exclusive="True") + channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) #straightforward case, both exchange & queue exist so no errors expected: channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") @@ -166,8 +166,8 @@ class QueueTests(TestBase): #bind two queues and consume from them channel = self.channel - channel.queue_declare(queue="queue-1", exclusive="True") - channel.queue_declare(queue="queue-2", exclusive="True") + channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) + channel.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) self.subscribe(queue="queue-1", destination="queue-1") self.subscribe(queue="queue-2", destination="queue-2") @@ -218,7 +218,7 @@ class QueueTests(TestBase): channel.queue_delete(queue="delete-me") #check that it has gone be declaring passively try: - channel.queue_declare(queue="delete-me", passive="True") + channel.queue_declare(queue="delete-me", passive=True) self.fail("Queue has not been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -227,7 +227,7 @@ class QueueTests(TestBase): channel = self.client.channel(2) channel.session_open() try: - channel.queue_delete(queue="i-dont-exist", if_empty="True") + channel.queue_delete(queue="i-dont-exist", if_empty=True) self.fail("Expected delete of non-existant queue to fail") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -242,12 +242,12 @@ class QueueTests(TestBase): #create a queue and add a message to it (use default binding): channel.queue_declare(queue="delete-me-2") - channel.queue_declare(queue="delete-me-2", passive="True") + channel.queue_declare(queue="delete-me-2", passive=True) channel.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) #try to delete, but only if empty: try: - channel.queue_delete(queue="delete-me-2", if_empty="True") + channel.queue_delete(queue="delete-me-2", if_empty=True) self.fail("Expected delete if_empty to fail for non-empty queue") except Closed, e: self.assertChannelException(406, e.args[0]) @@ -264,11 +264,11 @@ class QueueTests(TestBase): channel.message_cancel(destination="consumer_tag") #retry deletion on empty queue: - channel.queue_delete(queue="delete-me-2", if_empty="True") + channel.queue_delete(queue="delete-me-2", if_empty=True) #check that it has gone by declaring passively: try: - channel.queue_declare(queue="delete-me-2", passive="True") + channel.queue_declare(queue="delete-me-2", passive=True) self.fail("Queue has not been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -281,7 +281,7 @@ class QueueTests(TestBase): #create a queue and register a consumer: channel.queue_declare(queue="delete-me-3") - channel.queue_declare(queue="delete-me-3", passive="True") + channel.queue_declare(queue="delete-me-3", passive=True) self.subscribe(destination="consumer_tag", queue="delete-me-3") #need new channel now: @@ -289,17 +289,17 @@ class QueueTests(TestBase): channel2.session_open() #try to delete, but only if empty: try: - channel2.queue_delete(queue="delete-me-3", if_unused="True") + channel2.queue_delete(queue="delete-me-3", if_unused=True) self.fail("Expected delete if_unused to fail for queue with existing consumer") except Closed, e: self.assertChannelException(406, e.args[0]) channel.message_cancel(destination="consumer_tag") - channel.queue_delete(queue="delete-me-3", if_unused="True") + channel.queue_delete(queue="delete-me-3", if_unused=True) #check that it has gone by declaring passively: try: - channel.queue_declare(queue="delete-me-3", passive="True") + channel.queue_declare(queue="delete-me-3", passive=True) self.fail("Queue has not been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 84c07d51c1..6a682e9ae7 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -155,9 +155,9 @@ class TxTests(TestBase): commit and rollback """ #setup: - channel.queue_declare(queue=name_a, exclusive=True) - channel.queue_declare(queue=name_b, exclusive=True) - channel.queue_declare(queue=name_c, exclusive=True) + channel.queue_declare(queue=name_a, exclusive=True, auto_delete=True) + channel.queue_declare(queue=name_b, exclusive=True, auto_delete=True) + channel.queue_declare(queue=name_c, exclusive=True, auto_delete=True) key = "my_key_" + name_b topic = "my_topic_" + name_c -- cgit v1.2.1 From fbea92622c4c6f573ea3b49c5894cfcccea191c5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 11 Oct 2007 18:39:22 +0000 Subject: whitespace cleanup git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@583908 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 98fff9cd2f..15d7e69945 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -6,9 +6,9 @@ # 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 @@ -79,7 +79,7 @@ class Connection: self.FRAME_END = self.spec.constants.byname["frame_end"].id self.write = getattr(self, "write_%s_%s" % (self.spec.major, self.spec.minor)) self.read = getattr(self, "read_%s_%s" % (self.spec.major, self.spec.minor)) - + def flush(self): self.codec.flush() @@ -91,7 +91,7 @@ class Connection: def tini(self): self.codec.unpack(Connection.INIT) - + def write_8_0(self, frame): c = self.codec c.encode_octet(self.spec.constants.byname[frame.type].id) @@ -233,7 +233,7 @@ class Method(Frame): if version == (0, 10): c.encode_octet(self.method.klass.id) c.encode_octet(self.method.id) - else: + else: c.encode_short(self.method.klass.id) c.encode_short(self.method.id) for field, arg in zip(self.method.fields, self.args): @@ -244,7 +244,7 @@ class Method(Frame): if version == (0, 10): klass = spec.classes.byid[c.decode_octet()] meth = klass.methods.byid[c.decode_octet()] - else: + else: klass = spec.classes.byid[c.decode_short()] meth = klass.methods.byid[c.decode_short()] args = tuple([c.decode(f.type) for f in meth.fields]) -- cgit v1.2.1 From e0ba5becd0052cabe0cfa997dd35d7362bf2c472 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Sun, 14 Oct 2007 02:21:59 +0000 Subject: Enabled packed struct encoding in python, cpp, and java. Also fixed computation of required byte credit in Message.cpp. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@584474 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 17 ++++-- qpid/python/qpid/__init__.py | 23 +++++++-- qpid/python/qpid/codec.py | 106 ++++++++++++++++++++++++++++++++------ qpid/python/qpid/connection.py | 7 ++- qpid/python/qpid/peer.py | 4 +- qpid/python/qpid/spec.py | 24 +++++++-- qpid/python/tests/spec.py | 6 +-- qpid/python/tests_0-10/message.py | 28 +++++----- 8 files changed, 170 insertions(+), 45 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index b3170c8e0c..5ba14b0fc1 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -3,14 +3,25 @@ import qpid from qpid.client import Client from qpid.content import Content -client = Client("127.0.0.1", 5672, qpid.spec.load("../specs/amqp.0-10-preview.xml")) +spec = qpid.spec.load("../specs/amqp.0-10-preview.xml") +client = Client("127.0.0.1", 5672, spec) client.start({"LOGIN": "guest", "PASSWORD": "guest"}) ch = client.channel(1) ch.session_open() ch.queue_declare(queue="test") ch.queue_bind(exchange="amq.direct", queue="test", routing_key="test") -print ch.queue_query(queue="test") -ch.message_subscribe(queue="test", destination="test") +#print ch.queue_query(queue="test") +ch.message_subscribe(queue="test", destination="amq.direct") +ch.message_flow("amq.direct", 0, 0xFFFFFFFF) +ch.message_flow("amq.direct", 1, 0xFFFFFFFF) msg = Content("hello world") msg["content_type"] = "text/plain" +msg["routing_key"] = "test" +msg["reply_to"] = spec.struct("reply_to") +msg["reply_to"].exchange_name = "asdf" +msg["reply_to"].routing_key = "fdsa" +msg["application_headers"] = {"x": 1, "y": 2, "z": "zee"} ch.message_transfer(destination="amq.direct", content=msg) +queue = client.queue("amq.direct") +msg = queue.get(timeout=10) +print msg diff --git a/qpid/python/qpid/__init__.py b/qpid/python/qpid/__init__.py index 3f6d82b89e..7afebaf73b 100644 --- a/qpid/python/qpid/__init__.py +++ b/qpid/python/qpid/__init__.py @@ -31,8 +31,12 @@ class Struct: raise AttributeError(attr) return field - def has(self, name): - return self.type.fields.byname.has_key(name) + def exists(self, attr): + return self.type.fields.byname.has_key(attr) + + def has(self, attr): + self._check(attr) + return self._values.has_key(attr) def set(self, attr, value): self._check(attr) @@ -42,17 +46,30 @@ class Struct: field = self._check(attr) return self._values.get(attr, field.default()) + def clear(self, attr): + self._check(attr) + del self._values[attr] + def __setattr__(self, attr, value): self.set(attr, value) def __getattr__(self, attr): return self.get(attr) + def __delattr__(self, attr): + self.clear(attr) + def __setitem__(self, attr, value): self.set(attr, value) def __getitem__(self, attr): return self.get(attr) + def __delitem__(self, attr): + self.clear(attr) + def __str__(self): - return "%s %s" % (self.type.type, self._values) + return "%s %s" % (self.type, self._values) + + def __repr__(self): + return str(self) diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index f6b0f980cb..c54d48df52 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -94,6 +94,7 @@ class Codec: """ reads in 'n' bytes from the stream. Can raise EOF exception """ + self.clearbits() data = self.stream.read(n) if n > 0 and len(data) == 0: raise EOF() @@ -130,6 +131,10 @@ class Codec: for byte in bytes: self.encode_octet(byte) + def clearbits(self): + if self.incoming_bits: + self.incoming_bits = [] + def pack(self, fmt, *args): """ packs the data 'args' as per the format 'fmt' and writes it to the stream @@ -237,6 +242,12 @@ class Codec: """ return self.unpack("!L") + def encode_signed_long(self, o): + self.pack("!q", o) + + def decode_signed_long(self): + return self.unpack("!q") + def encode_longlong(self, o): """ encodes long long (64 bits) data 'o' in network byte order @@ -332,14 +343,8 @@ class Codec: w = width(code) if fixed(code): value = self.read(w) - elif w == 1: - value = self.decode_shortstr() - elif w == 2: - value = self.dec_str("!H") - elif w == 4: - value = self.decode_longstr() else: - raise ValueError("illegal width: " + w) + value = self.read(self.dec_num(w)) result[key] = value return result @@ -409,19 +414,88 @@ class Codec: def decode_uuid(self): return self.unpack("16s") + def enc_num(self, width, n): + if width == 1: + self.encode_octet(n) + elif width == 2: + self.encode_short(n) + elif width == 3: + self.encode_long(n) + else: + raise ValueError("invalid width: %s" % width) + + def dec_num(self, width): + if width == 1: + return self.decode_octet() + elif width == 2: + return self.decode_short() + elif width == 4: + return self.decode_long() + else: + raise ValueError("invalid width: %s" % width) + def encode_struct(self, type, s): + if False and type.size: + enc = StringIO() + codec = Codec(enc, self.spec) + codec.encode_struct_body(type, s) + codec.flush() + body = enc.getvalue() + self.enc_num(type.size, len(body)) + self.write(body) + else: + self.encode_struct_body(type, s) + + def decode_struct(self, type): + if False and type.size: + size = self.dec_num(type.size) + if size == 0: + return None + return self.decode_struct_body(type) + + def encode_struct_body(self, type, s): + reserved = 8*type.pack - len(type.fields) + assert reserved >= 0 + for f in type.fields: if s == None: - val = f.default() + self.encode_bit(False) + elif f.type == "bit": + self.encode_bit(s.get(f.name)) else: - val = s.get(f.name) - self.encode(f.type, val) + self.encode_bit(s.has(f.name)) + + for i in range(reserved): + self.encode_bit(False) + + for f in type.fields: + if f.type != "bit" and s != None and s.has(f.name): + self.encode(f.type, s.get(f.name)) + self.flush() - def decode_struct(self, type): + def decode_struct_body(self, type): + reserved = 8*type.pack - len(type.fields) + assert reserved >= 0 + s = qpid.Struct(type) + for f in type.fields: - s.set(f.name, self.decode(f.type)) + if f.type == "bit": + s.set(f.name, self.decode_bit()) + elif self.decode_bit(): + s.set(f.name, None) + + for i in range(reserved): + if self.decode_bit(): + raise ValueError("expecting reserved flag") + + for f in type.fields: + if f.type != "bit" and s.has(f.name): + s.set(f.name, self.decode(f.type)) + + self.clearbits() + return s def encode_long_struct(self, s): @@ -429,13 +503,13 @@ class Codec: codec = Codec(enc, self.spec) type = s.type codec.encode_short(type.type) - codec.encode_struct(type, s) + codec.encode_struct_body(type, s) self.encode_longstr(enc.getvalue()) def decode_long_struct(self): codec = Codec(StringIO(self.decode_longstr()), self.spec) type = self.spec.structs[codec.decode_short()] - return codec.decode_struct(type) + return codec.decode_struct_body(type) def fixed(code): return (code >> 6) != 2 @@ -454,9 +528,9 @@ def width(code): raise ValueError(code) # variable width elif code < 192 and code >= 128: - lenlen = (self.code >> 4) & 3 + lenlen = (code >> 4) & 3 if lenlen == 3: raise ValueError(code) return 2 ** lenlen # fixed width else: - return (self.code >> 4) & 7 + return (code >> 4) & 7 diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 15d7e69945..1beb60822d 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -163,7 +163,10 @@ class Connection: body_size = frame_size - 12 # TODO: Magic number (frame header size) body = c.read(body_size) dec = codec.Codec(StringIO(body), self.spec) - frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) + try: + frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) + except EOF: + raise "truncated frame body: %r" % body frame.channel = channel frame.subchannel = subchannel end = c.decode_octet() @@ -350,7 +353,7 @@ class Header(Frame): props = self.properties.copy() for k in self.properties: for s in structs: - if s.has(k): + if s.exists(k): s.set(k, props.pop(k)) if props: raise TypeError("no such property: %s" % (", ".join(props))) diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 5c05e71cf1..b734031798 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -130,8 +130,8 @@ class Peer: content = None self.delegate(channel, Message(channel, frame, content)) - except QueueClosed, e: - self.close(e) + except QueueClosed: + self.close("worker closed") except: self.fatal() diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 2542ccc3e1..31c79276aa 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -29,7 +29,7 @@ class so that the generated code can be reused in a variety of situations. """ -import re, textwrap, new, mllib +import re, textwrap, new, mllib, qpid class SpecContainer: @@ -115,6 +115,10 @@ class Spec(Metadata): klass, meth = parts return self.classes.byname[klass].methods.byname[meth] + def struct(self, name): + type = self.domains.byname[name].type + return qpid.Struct(type) + def define_module(self, name, doc = None): module = new.module(name, doc) module.__file__ = self.file @@ -303,14 +307,26 @@ class Field(Metadata): else: return Method.DEFAULTS[self.type] +WIDTHS = { + "octet": 1, + "short": 2, + "long": 4 + } + +def width(st, default=None): + if st in (None, "none", ""): + return default + else: + return WIDTHS[st] + def get_result(nd, spec): result = nd["result"] if not result: return None name = result["@domain"] if name != None: return spec.domains.byname[name] st_nd = result["struct"] - st = Struct(st_nd["@size"], int(result.parent.parent["@index"])*256 + - int(st_nd["@type"]), st_nd["@pack"]) + st = Struct(width(st_nd["@size"]), int(result.parent.parent["@index"])*256 + + int(st_nd["@type"]), width(st_nd["@pack"], 2)) spec.structs[st.type] = st load_fields(st_nd, st.fields, spec.domains.byname) return st @@ -366,7 +382,7 @@ def load(specfile, *errata): code = st_nd["@type"] if code not in (None, "", "none"): code = int(code) - type = Struct(st_nd["@size"], code, st_nd["@pack"]) + type = Struct(width(st_nd["@size"]), code, width(st_nd["@pack"], 2)) if type.type != None: spec.structs[type.type] = type structs.append((type, st_nd)) diff --git a/qpid/python/tests/spec.py b/qpid/python/tests/spec.py index c00faad3ba..ce03640493 100644 --- a/qpid/python/tests/spec.py +++ b/qpid/python/tests/spec.py @@ -32,13 +32,13 @@ class SpecTest(TestCase): if (spec.major == 0 and spec.minor == 10): assert qdecl_ok == None reply_to = spec.domains.byname["reply_to"] - assert reply_to.type.size == "short" - assert reply_to.type.pack == "short" + assert reply_to.type.size == 2 + assert reply_to.type.pack == 2 assert len(reply_to.type.fields) == 2 qq = spec.method("queue_query") assert qq != None - assert qq.result.size == "long" + assert qq.result.size == 4 assert qq.result.type != None args = qq.result.fields.byname["arguments"] assert args.type == "table" diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index c414b15214..8212c7be67 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -437,23 +437,25 @@ class MessageTests(TestBase): channel.message_subscribe(queue = "q", destination = "c") channel.message_flow_mode(mode = 0, destination = "c") #send batch of messages to queue - for i in range(1, 11): + for i in range(10): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) - #each message is currently interpreted as requiring 75 bytes of credit + #each message is currently interpreted as requiring msg_size bytes of credit + msg_size = 40 + #set byte credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 1, value = 75*5, destination = "c") + channel.message_flow(unit = 1, value = msg_size*5, destination = "c") #set infinite message credit channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = self.client.queue("c") - for i in range(1, 6): + for i in range(5): self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) - + #increase credit again and check more are received - for i in range(6, 11): - channel.message_flow(unit = 1, value = 75, destination = "c") + for i in range(5): + channel.message_flow(unit = 1, value = msg_size, destination = "c") self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) @@ -501,25 +503,27 @@ class MessageTests(TestBase): channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) channel.message_flow_mode(mode = 1, destination = "c") #send batch of messages to queue - for i in range(1, 11): + for i in range(10): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) - #each message is currently interpreted as requiring 75 bytes of credit + #each message is currently interpreted as requiring msg_size bytes of credit + msg_size = 40 + #set byte credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 1, value = 75*5, destination = "c") + channel.message_flow(unit = 1, value = msg_size*5, destination = "c") #set infinite message credit channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = self.client.queue("c") msgs = [] - for i in range(1, 6): + for i in range(5): msg = q.get(timeout = 1) msgs.append(msg) self.assertDataEquals(channel, msg, "abcdefgh") self.assertEmpty(q) #ack each message individually and check more are received - for i in range(6, 11): + for i in range(5): msg = msgs.pop() msg.complete(cumulative=False) self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") -- cgit v1.2.1 From b0f78fc025d36d39863049e9c05a6c9926c8eaab Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 15 Oct 2007 09:52:16 +0000 Subject: Remove default queue concept which is no longer applicable in 0-10. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@584719 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/queue.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index d72d12f92d..7b3590d11b 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -126,11 +126,8 @@ class QueueTests(TestBase): #straightforward case, both exchange & queue exist so no errors expected: channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") - #bind the default queue for the channel (i.e. last one declared): - channel.queue_bind(exchange="amq.direct", routing_key="key2") - - #use the queue name where neither routing key nor queue are specified: - channel.queue_bind(exchange="amq.direct") + #use the queue name where the routing key is not specified: + channel.queue_bind(queue="queue-1", exchange="amq.direct") #try and bind to non-existant exchange try: -- cgit v1.2.1 From 2e7a0f670684ccd696316666c6a1d61849f67e80 Mon Sep 17 00:00:00 2001 From: Andrew Stitcher Date: Tue, 16 Oct 2007 10:21:20 +0000 Subject: Implementation of 0-10 field tables git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@585097 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 3 +++ qpid/python/qpid/codec.py | 9 ++++----- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 8a8a3e1b20..80e3d44935 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,3 +1,6 @@ +tests.codec.FieldTableTestCase.test_field_table_decode +tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair +tests.codec.FieldTableTestCase.test_field_table_name_value_pair tests_0-10.alternate-exchange.AlternateExchangeTests.test_immediate tests_0-10.broker.BrokerTests.test_closed_channel diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index c54d48df52..18928698a5 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -68,11 +68,10 @@ class Codec: dict: "table" } - if False: - for constant in self.spec.constants: - if constant.klass == "field-table-type": - type = constant.name.replace("field_table_", "") - self.typecode(constant.id, TYPE_ALIASES.get(type, type)) + for constant in self.spec.constants: + if constant.klass == "field-table-type": + type = constant.name.replace("field_table_", "") + self.typecode(constant.id, TYPE_ALIASES.get(type, type)) if not self.types: self.typecode(ord('S'), "longstr") -- cgit v1.2.1 From 2b382c2ab784f310d0ed36825668a6368f22a668 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 17 Oct 2007 13:45:27 +0000 Subject: Fix to headers exchanges bind: need to check the match value is present before dereferencing Added tests for this. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@585503 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/exchange.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 4d8b254df7..86c39b7736 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -325,3 +325,11 @@ class MiscellaneousErrorsTests(TestBase): c2.session_open() c2.exchange_delete(exchange="test_different_declared_type_exchange") +class ExchangeTests(TestBase): + def testHeadersBindNoMatchArg(self): + self.channel.queue_declare(queue="q", exclusive=True, auto_delete=True) + try: + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={"name":"fred" , "age":3} ) + self.fail("Expected failure for missing x-match arg.") + except Closed, e: + self.assertConnectionException(541, e.args[0]) -- cgit v1.2.1 From b5e7a9557c025f9ed8dc599ebf7a19890875a4c6 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 18 Oct 2007 12:53:46 +0000 Subject: Fixed return of acquired message ranges (and added tests) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@585952 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 8212c7be67..0fcc38caa0 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -571,11 +571,19 @@ class MessageTests(TestBase): self.subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) msg = self.client.queue("a").get(timeout = 1) - channel.message_acquire([msg.command_id, msg.command_id]) - msg.complete() + #message should still be on the queue: + self.assertEquals(1, channel.queue_query(queue = "q").message_count) + channel.message_acquire([msg.command_id, msg.command_id]) + #check that we get notification (i.e. message_acquired) + response = channel.control_queue.get(timeout=1) + self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) #message should have been removed from the queue: self.assertEquals(0, channel.queue_query(queue = "q").message_count) + msg.complete() + + + def test_release(self): """ -- cgit v1.2.1 From 5b1a47bd60fc759c571b9268baa193e04f06ae7f Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 25 Oct 2007 17:37:01 +0000 Subject: Additional testing of subscriber in not-acquired mode git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@588287 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 0fcc38caa0..cdd0969473 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -533,11 +533,6 @@ class MessageTests(TestBase): """ Test the not-acquired modes works as expected for a simple case """ - #NOTE: I'm using not-acquired == 1 and pre-acquired == 0 as - #that keeps the default behaviour as expected. This was - #discussed by the SIG, and I'd rather not change all the - #existing tests twice. - channel = self.channel channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 6): @@ -648,6 +643,40 @@ class MessageTests(TestBase): self.assertEquals("message 3", queue.get(timeout = 1).content.body) self.assertEmpty(queue) + def test_subscribe_not_acquired_2(self): + channel = self.channel + + #publish some messages + self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + + #consume some of them + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + channel.message_flow_mode(mode = 0, destination = "a") + channel.message_flow(unit = 0, value = 5, destination = "a") + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + + queue = self.client.queue("a") + for i in range(1, 6): + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + msg.complete() + self.assertEmpty(queue) + + #now create a not-acquired subscriber + channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + + #check it gets those not consumed + queue = self.client.queue("b") + channel.message_flow(unit = 0, value = 1, destination = "b") + for i in range(6, 11): + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + msg.complete() + channel.message_flow(unit = 0, value = 1, destination = "b") + self.assertEmpty(queue) def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) -- cgit v1.2.1 From ddee33a795a6a1aa01650666303a02bfead04c26 Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Mon, 29 Oct 2007 16:13:21 +0000 Subject: Added empty stub in client.py for handling session acks git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@589711 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/client.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index f0d7935283..37df8f272a 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -138,6 +138,9 @@ class ClientDelegate(Delegate): def channel_close(self, ch, msg): ch.close(msg) + def session_ack(self, ch, msg): + pass + def session_closed(self, ch, msg): ch.close(msg) -- cgit v1.2.1 From 4cf74e8632e3492badb745673cb7557264f9d434 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 29 Oct 2007 22:23:33 +0000 Subject: applied patch from tross posted in QPID-666 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@589889 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 225 ++++++++++++++++++++++++++++++++++++++ qpid/python/tests_0-10/example.py | 4 +- 2 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 qpid/python/qpid/management.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py new file mode 100644 index 0000000000..24c4700c7f --- /dev/null +++ b/qpid/python/qpid/management.py @@ -0,0 +1,225 @@ +# +# 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. +# + +""" +Management classes for AMQP +""" + +import qpid +import base64 +import socket +from threading import Thread +from message import Message +from time import sleep +from qpid.client import Client +from qpid.content import Content +from cStringIO import StringIO +from codec import Codec, EOF + +#=================================================================== +# ManagementMetadata +# +# One instance of this class is created for each ManagedBroker. It +# is used to store metadata from the broker which is needed for the +# proper interpretation of recevied management content. +# +#=================================================================== +class ManagementMetadata: + + def parseSchema (self, cls, oid, len, codec): + #print "Schema Record: objId=", oid + + config = [] + inst = [] + while 1: + flags = codec.decode_octet () + if flags == 0x80: + break + + tc = codec.decode_octet () + name = codec.decode_shortstr () + desc = codec.decode_shortstr () + + if flags & 1: # TODO: Define constants for these + config.append ((tc, name, desc)) + if (flags & 1) == 0 or (flags & 2) == 2: + inst.append ((tc, name, desc)) + + # TODO: Handle notification of schema change outbound + self.schema[(oid,'C')] = config + self.schema[(oid,'I')] = inst + + def parseContent (self, cls, oid, len, codec): + #print "Content Record: Class=", cls, ", objId=", oid + + if cls == 'C' and self.broker.config_cb == None: + return + if cls == 'I' and self.broker.inst_cb == None: + return + + if (oid,cls) not in self.schema: + return + + row = [] + timestamps = [] + + timestamps.append (codec.decode_longlong ()); # Current Time + timestamps.append (codec.decode_longlong ()); # Create Time + timestamps.append (codec.decode_longlong ()); # Delete Time + + for element in self.schema[(oid,cls)][:]: + tc = element[0] + name = element[1] + if tc == 1: # TODO: Define constants for these + data = codec.decode_octet () + elif tc == 2: + data = codec.decode_short () + elif tc == 3: + data = codec.decode_long () + elif tc == 4: + data = codec.decode_longlong () + elif tc == 5: + data = codec.decode_octet () + elif tc == 6: + data = codec.decode_shortstr () + row.append ((name, data)) + + if cls == 'C': + self.broker.config_cb[1] (self.broker.config_cb[0], oid, row, timestamps) + if cls == 'I': + self.broker.inst_cb[1] (self.broker.inst_cb[0], oid, row, timestamps) + + def parse (self, codec): + try: + opcode = chr (codec.decode_octet ()) + except EOF: + return 0 + + cls = chr (codec.decode_octet ()) + oid = codec.decode_short () + len = codec.decode_long () + + if len < 8: + raise ValueError ("parse error: value of length field too small") + + if opcode == 'S': + self.parseSchema (cls, oid, len, codec) + + if opcode == 'C': + self.parseContent (cls, oid, len, codec) + + return 1 + + def __init__ (self, broker): + self.broker = broker + self.schema = {} + + +#=================================================================== +# ManagedBroker +# +# An object of this class represents a connection (over AMQP) to a +# single managed broker. +# +#=================================================================== +class ManagedBroker: + + exchange = "qpid.management" + + def checkHeader (self, codec): + octet = chr (codec.decode_octet ()) + if octet != 'A': + return 0 + octet = chr (codec.decode_octet ()) + if octet != 'M': + return 0 + octet = chr (codec.decode_octet ()) + if octet != '0': + return 0 + octet = chr (codec.decode_octet ()) + if octet != '1': + return 0 + return 1 + + def receive_cb (self, msg): + codec = Codec (StringIO (msg.content.body), self.spec) + + if self.checkHeader (codec) == 0: + raise ValueError ("outer header invalid"); + + while self.metadata.parse (codec): + pass + + msg.complete () + + def __init__ (self, host = "localhost", port = 5672, + username = "guest", password = "guest"): + + self.spec = qpid.spec.load ("../specs/amqp.0-10-preview.xml") + self.client = None + self.channel = None + self.queue = None + self.qname = None + self.metadata = ManagementMetadata (self) + + # Initialize the callback records + self.schema_cb = None + self.config_cb = None + self.inst_cb = None + + self.host = host + self.port = port + self.username = username + self.password = password + + def schemaListener (self, context, callback): + self.schema_cb = (context, callback) + + def configListener (self, context, callback): + self.config_cb = (context, callback) + + def instrumentationListener (self, context, callback): + self.inst_cb = (context, callback) + + def start (self): + print "Connecting to broker", self.host + + try: + self.client = Client (self.host, self.port, self.spec) + self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) + self.channel = self.client.channel (1) + response = self.channel.session_open (detached_lifetime=300) + self.qname = "mgmt-" + base64.urlsafe_b64encode(response.session_id) + + self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) + self.channel.queue_bind (exchange=ManagedBroker.exchange, queue=self.qname, + routing_key="mgmt") + self.channel.message_subscribe (queue=self.qname, destination="dest") + self.queue = self.client.queue ("dest") + self.queue.listen (self.receive_cb) + + self.channel.message_flow_mode (destination="dest", mode=1) + self.channel.message_flow (destination="dest", unit=0, value=0xFFFFFFFF) + self.channel.message_flow (destination="dest", unit=1, value=0xFFFFFFFF) + + except socket.error, e: + print "Socket Error Detected:", e[1] + raise + except: + raise diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index 9dbe73e3cb..da5ee2441f 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -22,8 +22,8 @@ from qpid.testlib import testrunner, TestBase class ExampleTest (TestBase): """ - An example Qpid test, illustrating the unittest frameowkr and the - python Qpid client. The test class must inherit TestCase. The + An example Qpid test, illustrating the unittest framework and the + python Qpid client. The test class must inherit TestBase. The test code uses the Qpid client to interact with a qpid broker and verify it behaves as expected. """ -- cgit v1.2.1 From 6ee11959bff03826083c85943f14e6b4ceeaacff Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 6 Nov 2007 17:27:27 +0000 Subject: Add support for array type to c++ (and python, decode only for now) Change the type of the in-doubt field in dtx-coordination.recover to an array (to bring in line with amqp spec) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@592494 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 17 +++++++++++++ qpid/python/qpid/spec.py | 1 + qpid/python/tests_0-10/dtx.py | 55 +++++++++++++++++++++++++++++++++---------- 3 files changed, 61 insertions(+), 12 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 18928698a5..6399ad2a5d 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -510,6 +510,23 @@ class Codec: type = self.spec.structs[codec.decode_short()] return codec.decode_struct_body(type) + def decode_array(self): + size = self.decode_long() + code = self.decode_octet() + count = self.decode_long() + result = [] + for i in range(0, count): + if self.types.has_key(code): + value = self.decode(self.types[code]) + else: + w = width(code) + if fixed(code): + value = self.read(w) + else: + value = self.read(self.dec_num(w)) + result.append(value) + return result + def fixed(code): return (code >> 6) != 2 diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 31c79276aa..4d0497bced 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -255,6 +255,7 @@ class Method(Metadata): "shortstr": "", "longstr": "", "table": {}, + "array": [], "octet": 0, "short": 0, "long": 0, diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 8fdd32c2f5..5ee4dd4c16 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -304,6 +304,38 @@ class DtxTests(TestBase): self.assertMessageId("a", "two") self.assertMessageId("b", "one") + def test_suspend_start_end_resume(self): + """ + Test suspension and resumption of an association with work + done on another transaction when the first transaction is + suspended + """ + channel = self.channel + channel.dtx_demarcation_select() + + #setup + channel.queue_declare(queue="one", exclusive=True, auto_delete=True) + channel.queue_declare(queue="two", exclusive=True, auto_delete=True) + channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) + channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + + tx = self.xid("dummy") + + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' + channel.dtx_demarcation_end(xid=tx, suspend=True) + + channel.dtx_demarcation_start(xid=tx, resume=True) + self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' + channel.dtx_demarcation_end(xid=tx) + + #commit and check + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + def test_end_suspend_and_fail(self): """ Verify that the correct error is signalled if the suspend and @@ -538,18 +570,7 @@ class DtxTests(TestBase): else: channel.dtx_coordination_rollback(xid=tx) - indoubt = channel.dtx_coordination_recover().in_doubt - #convert indoubt table to a list of xids (note: this will change for 0-10) - data = indoubt["xids"] - xids = [] - pos = 0 - while pos < len(data): - size = unpack("!B", data[pos])[0] - start = pos + 1 - end = start + size - xid = data[start:end] - xids.append(xid) - pos = end + xids = channel.dtx_coordination_recover().in_doubt #rollback the prepared transactions returned by recover for x in xids: @@ -567,6 +588,16 @@ class DtxTests(TestBase): channel.dtx_coordination_rollback(xid=x) self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) + def test_bad_resume(self): + """ + Test that a resume on a session not selected for use with dtx fails + """ + channel = self.channel + try: + channel.dtx_demarcation_start(resume=True) + except Closed, e: + self.assertConnectionException(503, e.args[0]) + def xid(self, txid): DtxTests.tx_counter += 1 branchqual = "v%s" % DtxTests.tx_counter -- cgit v1.2.1 From 338a887844bb11d2c3a02696f06d01bef3c3c51a Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Tue, 6 Nov 2007 21:16:03 +0000 Subject: updated pack param, however nothing was broken because first param is 0 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@592556 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/dtx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 5ee4dd4c16..f84f91c75a 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -601,7 +601,7 @@ class DtxTests(TestBase): def xid(self, txid): DtxTests.tx_counter += 1 branchqual = "v%s" % DtxTests.tx_counter - return pack('LBB', 0, len(txid), len(branchqual)) + txid + branchqual + return pack('!LBB', 0, len(txid), len(branchqual)) + txid + branchqual def txswap(self, tx, id): channel = self.channel -- cgit v1.2.1 From 941af7148965f32e7a660800b38882a55216cbf4 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 7 Nov 2007 14:08:13 +0000 Subject: Fix to QPID-640. The fields actually set/encoded are now tracked explicitly. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@592753 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index cdd0969473..5d251a9c8e 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -441,7 +441,7 @@ class MessageTests(TestBase): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 40 + msg_size = 34 #set byte credit to finite amount (less than enough for all messages) channel.message_flow(unit = 1, value = msg_size*5, destination = "c") -- cgit v1.2.1 From 2e82ef59003f607e7ceea3442c423b4fc70001b9 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 7 Nov 2007 20:46:36 +0000 Subject: added factory for structs, and made default spec loading based on AMQP_SPEC environment variable git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@592888 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 7 ++----- qpid/python/qpid/__init__.py | 11 ++++++++++- qpid/python/qpid/client.py | 33 ++++++++++++++++++++++++++++++--- qpid/python/qpid/spec.py | 4 ++-- 4 files changed, 44 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index 5ba14b0fc1..d419fd988b 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -3,8 +3,7 @@ import qpid from qpid.client import Client from qpid.content import Content -spec = qpid.spec.load("../specs/amqp.0-10-preview.xml") -client = Client("127.0.0.1", 5672, spec) +client = Client("127.0.0.1", 5672) client.start({"LOGIN": "guest", "PASSWORD": "guest"}) ch = client.channel(1) ch.session_open() @@ -17,9 +16,7 @@ ch.message_flow("amq.direct", 1, 0xFFFFFFFF) msg = Content("hello world") msg["content_type"] = "text/plain" msg["routing_key"] = "test" -msg["reply_to"] = spec.struct("reply_to") -msg["reply_to"].exchange_name = "asdf" -msg["reply_to"].routing_key = "fdsa" +msg["reply_to"] = client.structs.reply_to("asdf", "fdsa") msg["application_headers"] = {"x": 1, "y": 2, "z": "zee"} ch.message_transfer(destination="amq.direct", content=msg) queue = client.queue("amq.direct") diff --git a/qpid/python/qpid/__init__.py b/qpid/python/qpid/__init__.py index 7afebaf73b..ff9cc04df8 100644 --- a/qpid/python/qpid/__init__.py +++ b/qpid/python/qpid/__init__.py @@ -21,10 +21,19 @@ import spec, codec, connection, content, peer, delegate, client class Struct: - def __init__(self, type): + def __init__(self, type, *args, **kwargs): self.__dict__["type"] = type self.__dict__["_values"] = {} + if len(args) > len(self.type.fields): + raise TypeError("too many args") + + for a, f in zip(args, self.type.fields): + self.set(f.name, a) + + for k, a in kwargs.items(): + self.set(k, a) + def _check(self, attr): field = self.type.fields.byname.get(attr) if field == None: diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 37df8f272a..44fd6c053f 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -22,7 +22,7 @@ An AQMP client implementation that uses a custom delegate for interacting with the server. """ -import threading +import os, threading from peer import Peer, Closed from delegate import Delegate from connection import Connection, Frame, connect @@ -33,10 +33,18 @@ from reference import ReferenceId, References class Client: - def __init__(self, host, port, spec, vhost = None): + def __init__(self, host, port, spec = None, vhost = None): self.host = host self.port = port - self.spec = spec + if spec: + self.spec = spec + else: + try: + name = os.environ["AMQP_SPEC"] + except KeyError: + raise EnvironmentError("environment variable AMQP_SPEC must be set") + self.spec = load(name) + self.structs = StructFactory(self.spec) self.mechanism = None self.response = None @@ -158,3 +166,22 @@ class ClientDelegate(Delegate): self.client.closed = True self.client.reason = reason self.client.started.set() + +class StructFactory: + + def __init__(self, spec): + self.spec = spec + self.factories = {} + + def __getattr__(self, name): + if self.factories.has_key(name): + return self.factories[name] + elif self.spec.domains.byname.has_key(name): + f = lambda *args, **kwargs: self.struct(name, *args, **kwargs) + self.factories[name] = f + return f + else: + raise AttributeError(name) + + def struct(self, name, *args, **kwargs): + return self.spec.struct(name, *args, **kwargs) diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 4d0497bced..537ced113b 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -115,9 +115,9 @@ class Spec(Metadata): klass, meth = parts return self.classes.byname[klass].methods.byname[meth] - def struct(self, name): + def struct(self, name, *args, **kwargs): type = self.domains.byname[name].type - return qpid.Struct(type) + return qpid.Struct(type, *args, **kwargs) def define_module(self, name, doc = None): module = new.module(name, doc) -- cgit v1.2.1 From 1d31f66fcc9601db06f5fbdd158c16bc3180f0fd Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 7 Nov 2007 20:55:35 +0000 Subject: Fix for QPID-639: c++ now includes sizes for all structs (enabled the same in python & java) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@592895 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 6399ad2a5d..a3663773a9 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -434,7 +434,7 @@ class Codec: raise ValueError("invalid width: %s" % width) def encode_struct(self, type, s): - if False and type.size: + if type.size: enc = StringIO() codec = Codec(enc, self.spec) codec.encode_struct_body(type, s) @@ -446,7 +446,7 @@ class Codec: self.encode_struct_body(type, s) def decode_struct(self, type): - if False and type.size: + if type.size: size = self.dec_num(type.size) if size == 0: return None -- cgit v1.2.1 From 4f6843f7c22313aa00c3aa9211f41e0762b6fcbd Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 7 Nov 2007 22:30:40 +0000 Subject: python API updates git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@592927 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 19 ++++++++------- qpid/python/qpid/client.py | 56 +++++++++++++++++++++++++++++++++++++------- qpid/python/qpid/delegate.py | 2 +- qpid/python/qpid/peer.py | 53 +++++++++++++++++++++-------------------- 4 files changed, 85 insertions(+), 45 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index d419fd988b..518992409e 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -5,20 +5,21 @@ from qpid.content import Content client = Client("127.0.0.1", 5672) client.start({"LOGIN": "guest", "PASSWORD": "guest"}) -ch = client.channel(1) -ch.session_open() -ch.queue_declare(queue="test") -ch.queue_bind(exchange="amq.direct", queue="test", routing_key="test") -#print ch.queue_query(queue="test") -ch.message_subscribe(queue="test", destination="amq.direct") -ch.message_flow("amq.direct", 0, 0xFFFFFFFF) -ch.message_flow("amq.direct", 1, 0xFFFFFFFF) +ssn = client.session() +ssn.open() +ssn.queue_declare(queue="test") +ssn.queue_bind(exchange="amq.direct", queue="test", routing_key="test") +#print ssn.queue_query(queue="test") +ssn.message_subscribe(queue="test", destination="amq.direct") +ssn.message_flow("amq.direct", 0, 0xFFFFFFFF) +ssn.message_flow("amq.direct", 1, 0xFFFFFFFF) msg = Content("hello world") msg["content_type"] = "text/plain" msg["routing_key"] = "test" msg["reply_to"] = client.structs.reply_to("asdf", "fdsa") msg["application_headers"] = {"x": 1, "y": 2, "z": "zee"} -ch.message_transfer(destination="amq.direct", content=msg) +ssn.message_transfer(destination="amq.direct", content=msg) queue = client.queue("amq.direct") msg = queue.get(timeout=10) print msg +ssn.close() diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 44fd6c053f..13c3f13fe5 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -23,7 +23,7 @@ interacting with the server. """ import os, threading -from peer import Peer, Closed +from peer import Peer, Channel, Closed from delegate import Delegate from connection import Connection, Frame, connect from spec import load @@ -45,6 +45,7 @@ class Client: raise EnvironmentError("environment variable AMQP_SPEC must be set") self.spec = load(name) self.structs = StructFactory(self.spec) + self.sessions = {} self.mechanism = None self.response = None @@ -86,7 +87,7 @@ class Client: self.socket = connect(self.host, self.port) self.conn = Connection(self.socket, self.spec) - self.peer = Peer(self.conn, ClientDelegate(self), self.opened) + self.peer = Peer(self.conn, ClientDelegate(self), Session) self.conn.init() self.peer.start() @@ -94,10 +95,29 @@ class Client: self.channel(0).connection_open(self.vhost) def channel(self, id): - return self.peer.channel(id) + self.lock.acquire() + try: + ssn = self.peer.channel(id) + ssn.client = self + self.sessions[id] = ssn + finally: + self.lock.release() + return ssn - def opened(self, ch): - ch.references = References() + def session(self): + self.lock.acquire() + try: + id = None + for i in xrange(1, 64*1024): + if not self.sessions.has_key(id): + id = i + break + finally: + self.lock.release() + if id == None: + raise RuntimeError("out of channels") + else: + return self.channel(id) def close(self): self.socket.close() @@ -144,16 +164,16 @@ class ClientDelegate(Delegate): msg.ok() def channel_close(self, ch, msg): - ch.close(msg) + ch.closed(msg) def session_ack(self, ch, msg): pass def session_closed(self, ch, msg): - ch.close(msg) + ch.closed(msg) def connection_close(self, ch, msg): - self.client.peer.close(msg) + self.client.peer.closed(msg) def execution_complete(self, ch, msg): ch.completion.complete(msg.cumulative_execution_mark) @@ -162,7 +182,7 @@ class ClientDelegate(Delegate): future = ch.futures[msg.command_id] future.put_response(ch, msg.data) - def close(self, reason): + def closed(self, reason): self.client.closed = True self.client.reason = reason self.client.started.set() @@ -185,3 +205,21 @@ class StructFactory: def struct(self, name, *args, **kwargs): return self.spec.struct(name, *args, **kwargs) + +class Session(Channel): + + def __init__(self, *args): + Channel.__init__(self, *args) + self.references = References() + self.client = None + + def open(self): + self.session_open() + + def close(self): + self.session_close() + self.client.lock.acquire() + try: + del self.client.sessions[self.id] + finally: + self.client.lock.release() diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py index 8f5033e485..c4c47592b4 100644 --- a/qpid/python/qpid/delegate.py +++ b/qpid/python/qpid/delegate.py @@ -49,5 +49,5 @@ class Delegate: print >> sys.stderr, "Error in handler: %s\n\n%s" % \ (_handler_name(method), traceback.format_exc()) - def close(self, reason): + def closed(self, reason): print "Connection closed: %s" % reason diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index b734031798..376d81e184 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -51,14 +51,17 @@ class Sequence: class Peer: - def __init__(self, conn, delegate, channel_callback=None): + def __init__(self, conn, delegate, channel_factory=None): self.conn = conn self.delegate = delegate self.outgoing = Queue(0) self.work = Queue(0) self.channels = {} self.lock = thread.allocate_lock() - self.channel_callback = channel_callback #notified when channels are created + if channel_factory: + self.channel_factory = channel_factory + else: + self.channel_factory = Channel def channel(self, id): self.lock.acquire() @@ -66,10 +69,8 @@ class Peer: try: ch = self.channels[id] except KeyError: - ch = Channel(id, self.outgoing, self.conn.spec) + ch = self.channel_factory(id, self.outgoing, self.conn.spec) self.channels[id] = ch - if self.channel_callback: - self.channel_callback(ch) finally: self.lock.release() return ch @@ -82,7 +83,7 @@ class Peer: def fatal(self, message=None): """Call when an unexpected exception occurs that will kill a thread.""" if message: print >> sys.stderr, message - self.close("Fatal error: %s\n%s" % (message or "", traceback.format_exc())) + self.closed("Fatal error: %s\n%s" % (message or "", traceback.format_exc())) def reader(self): try: @@ -97,13 +98,13 @@ class Peer: except: self.fatal() - def close(self, reason): + def closed(self, reason): # We must close the delegate first because closing channels # may wake up waiting threads and we don't want them to see # the delegate as open. - self.delegate.close(reason) + self.delegate.closed(reason) for ch in self.channels.values(): - ch.close(reason) + ch.closed(reason) def writer(self): try: @@ -112,7 +113,7 @@ class Peer: message = self.outgoing.get() self.conn.write(message) except socket.error, e: - self.close(e) + self.closed(e) break self.conn.flush() except: @@ -131,7 +132,7 @@ class Peer: self.delegate(channel, Message(channel, frame, content)) except QueueClosed: - self.close("worker closed") + self.closed("worker closed") except: self.fatal() @@ -181,7 +182,7 @@ class Channel: self.incoming = Queue(0) self.responses = Queue(0) self.queue = None - self.closed = False + self._closed = False self.reason = None self.requester = Requester(self.write) @@ -200,10 +201,10 @@ class Channel: self.use_execution_layer = (spec.major == 0 and spec.minor == 10) self.synchronous = True - def close(self, reason): - if self.closed: + def closed(self, reason): + if self._closed: return - self.closed = True + self._closed = True self.reason = reason self.incoming.close() self.responses.close() @@ -213,7 +214,7 @@ class Channel: f.put_response(self, reason) def write(self, frame, content = None): - if self.closed: + if self._closed: raise Closed(self.reason) frame.channel = self.id self.outgoing.put(frame) @@ -283,7 +284,7 @@ class Channel: if self.use_execution_layer and frame.method_type.is_l4_command(): self.execution_sync() self.completion.wait() - if self.closed: + if self._closed: raise Closed(self.reason) return None try: @@ -293,7 +294,7 @@ class Channel: else: return Message(self, resp) except QueueClosed, e: - if self.closed: + if self._closed: raise Closed(self.reason) else: raise e @@ -328,7 +329,7 @@ class Channel: elif frame.method.result: if self.synchronous: fr = future.get_response(timeout=10) - if self.closed: + if self._closed: raise Closed(self.reason) return fr else: @@ -337,12 +338,12 @@ class Channel: and self.use_execution_layer and frame.method.is_l4_command(): self.execution_sync() completed = self.completion.wait(timeout=10) - if self.closed: + if self._closed: raise Closed(self.reason) if not completed: - self.close("Timed-out waiting for completion of %s" % frame) + self.closed("Timed-out waiting for completion of %s" % frame) except QueueClosed, e: - if self.closed: + if self._closed: raise Closed(self.reason) else: raise e @@ -399,7 +400,7 @@ class OutgoingCompletion: self.sequence = Sequence(0) #issues ids for outgoing commands self.command_id = -1 #last issued id self.mark = -1 #commands up to this mark are known to be complete - self.closed = False + self._closed = False def next_command(self, method): #the following test is a hack until the track/sub-channel is available @@ -413,7 +414,7 @@ class OutgoingCompletion: self.reset() self.condition.acquire() try: - self.closed = True + self._closed = True self.condition.notifyAll() finally: self.condition.release() @@ -433,10 +434,10 @@ class OutgoingCompletion: remaining = timeout self.condition.acquire() try: - while not self.closed and point_of_interest > self.mark: + while not self._closed and point_of_interest > self.mark: #print "waiting for %s, mark = %s [%s]" % (point_of_interest, self.mark, self) self.condition.wait(remaining) - if not self.closed and point_of_interest > self.mark and timeout: + if not self._closed and point_of_interest > self.mark and timeout: if (start_time + timeout) < time(): break else: remaining = timeout - (time() - start_time) finally: -- cgit v1.2.1 From 4e8a91d7704fe666f61ff2c216157d1828de4ed8 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 9 Nov 2007 23:32:10 +0000 Subject: Get rid of basic_publish, no longer supported by c++ broker. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@593693 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/execution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/execution.py b/qpid/python/tests_0-10/execution.py index 950ff59d97..3ff6d8ea65 100644 --- a/qpid/python/tests_0-10/execution.py +++ b/qpid/python/tests_0-10/execution.py @@ -24,6 +24,6 @@ class ExecutionTests (TestBase): def test_flush(self): channel = self.channel for i in [1, 2, 3]: - channel.basic_publish(routing_key=str(i)) - #channel.execution_flush() + channel.message_transfer( + content=Content(properties={'routing_key':str(i)})) assert(channel.completion.wait(channel.completion.command_id, timeout=1)) -- cgit v1.2.1 From eb1561a655940a5d32da698187ff05e845e6f093 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 15 Nov 2007 08:54:29 +0000 Subject: Fix to auto-rollback which didn't do what it was supposed to (i.e. rollback a session ended with an open transaction) Fix to rollback to stop flow for subscriptions before the rollback, and restart after. This prevents any out of order delivery as rollback requeues the messages and will be redundant once QPID-686 is fixed. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@595244 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/tx.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 6a682e9ae7..3fd1065af3 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -77,8 +77,9 @@ class TxTests(TestBase): """ Test that a channel closed with an open transaction is effectively rolled back """ - channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + channel2 = self.client.channel(2) + channel2.session_open() + queue_a, queue_b, queue_c = self.perform_txn_work(channel2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") for q in [queue_a, queue_b, queue_c]: try: @@ -86,7 +87,18 @@ class TxTests(TestBase): self.fail("Got unexpected message: " + extra.content.body) except Empty: None - channel.tx_rollback() + channel2.session_close() + channel = self.channel + channel.tx_select() + + self.subscribe(channel, queue="tx-autorollback-a", destination="qa", confirm_mode=1) + queue_a = self.client.queue("qa") + + self.subscribe(channel, queue="tx-autorollback-b", destination="qb", confirm_mode=1) + queue_b = self.client.queue("qb") + + self.subscribe(channel, queue="tx-autorollback-c", destination="qc", confirm_mode=1) + queue_c = self.client.queue("qc") #check results for i in range(1, 5): @@ -124,8 +136,17 @@ class TxTests(TestBase): self.fail("Got unexpected message: " + extra.content.body) except Empty: None + #stop subscriptions (ensures no delivery occurs during rollback as messages are requeued) + for d in ["sub_a", "sub_b", "sub_c"]: + channel.message_stop(destination=d) + channel.tx_rollback() + #restart susbcriptions + for d in ["sub_a", "sub_b", "sub_c"]: + channel.message_flow(destination=d, unit=0, value=0xFFFFFFFF) + channel.message_flow(destination=d, unit=1, value=0xFFFFFFFF) + #check results for i in range(1, 5): msg = queue_a.get(timeout=1) -- cgit v1.2.1 From 1fe5478eebfc242339be974160817e9709c6cb8b Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Thu, 15 Nov 2007 21:36:00 +0000 Subject: QPID-687: comitted qpid-patch7-python.diff for real this time. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@595465 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec.py | 6 + qpid/python/qpid/management.py | 258 +++++++++++++++++++++++++++++------------ 2 files changed, 190 insertions(+), 74 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index a3663773a9..b25de11f11 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -247,6 +247,12 @@ class Codec: def decode_signed_long(self): return self.unpack("!q") + def encode_signed_int(self, o): + self.pack("!l", o) + + def decode_signed_int(self): + return self.unpack("!l") + def encode_longlong(self, o): """ encodes long long (64 bits) data 'o' in network byte order diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 24c4700c7f..a5ad997a24 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -18,7 +18,7 @@ # """ -Management classes for AMQP +Management API for Qpid """ import qpid @@ -42,50 +42,91 @@ from codec import Codec, EOF #=================================================================== class ManagementMetadata: - def parseSchema (self, cls, oid, len, codec): - #print "Schema Record: objId=", oid - - config = [] - inst = [] - while 1: - flags = codec.decode_octet () - if flags == 0x80: - break - - tc = codec.decode_octet () - name = codec.decode_shortstr () - desc = codec.decode_shortstr () - - if flags & 1: # TODO: Define constants for these - config.append ((tc, name, desc)) - if (flags & 1) == 0 or (flags & 2) == 2: - inst.append ((tc, name, desc)) + def parseSchema (self, cls, codec): + className = codec.decode_shortstr () + configCount = codec.decode_short () + instCount = codec.decode_short () + methodCount = codec.decode_short () + eventCount = codec.decode_short () + + configs = [] + insts = [] + methods = [] + events = [] + + configs.append (("id", 4, "", "", 1, 1, None, None, None, None, None)) + insts.append (("id", 4, None, None)) + + for idx in range (configCount): + ft = codec.decode_table () + name = ft["name"] + type = ft["type"] + access = ft["access"] + index = ft["index"] + unit = None + min = None + max = None + maxlen = None + desc = None + + for key, value in ft.items (): + if key == "unit": + unit = value + elif key == "min": + min = value + elif key == "max": + max = value + elif key == "maxlen": + maxlen = value + elif key == "desc": + desc = value + + config = (name, type, unit, desc, access, index, min, max, maxlen) + configs.append (config) + + for idx in range (instCount): + ft = codec.decode_table () + name = ft["name"] + type = ft["type"] + unit = None + desc = None + + for key, value in ft.items (): + if key == "unit": + unit = value + elif key == "desc": + desc = value + + inst = (name, type, unit, desc) + insts.append (inst) # TODO: Handle notification of schema change outbound - self.schema[(oid,'C')] = config - self.schema[(oid,'I')] = inst - - def parseContent (self, cls, oid, len, codec): - #print "Content Record: Class=", cls, ", objId=", oid + self.schema[(className,'C')] = configs + self.schema[(className,'I')] = insts + self.schema[(className,'M')] = methods + self.schema[(className,'E')] = events + def parseContent (self, cls, codec): if cls == 'C' and self.broker.config_cb == None: return if cls == 'I' and self.broker.inst_cb == None: return - if (oid,cls) not in self.schema: + className = codec.decode_shortstr () + + if (className,cls) not in self.schema: return row = [] timestamps = [] - timestamps.append (codec.decode_longlong ()); # Current Time - timestamps.append (codec.decode_longlong ()); # Create Time - timestamps.append (codec.decode_longlong ()); # Delete Time + timestamps.append (codec.decode_longlong ()) # Current Time + timestamps.append (codec.decode_longlong ()) # Create Time + timestamps.append (codec.decode_longlong ()) # Delete Time - for element in self.schema[(oid,cls)][:]: - tc = element[0] - name = element[1] + for element in self.schema[(className,cls)][:]: + tc = element[1] + name = element[0] if tc == 1: # TODO: Define constants for these data = codec.decode_octet () elif tc == 2: @@ -98,33 +139,29 @@ class ManagementMetadata: data = codec.decode_octet () elif tc == 6: data = codec.decode_shortstr () + elif tc == 7: + data = codec.decode_longstr () + else: + raise ValueError ("Invalid type code: %d" % tc) row.append ((name, data)) if cls == 'C': - self.broker.config_cb[1] (self.broker.config_cb[0], oid, row, timestamps) - if cls == 'I': - self.broker.inst_cb[1] (self.broker.inst_cb[0], oid, row, timestamps) + self.broker.config_cb[1] (self.broker.config_cb[0], className, row, timestamps) + elif cls == 'I': + self.broker.inst_cb[1] (self.broker.inst_cb[0], className, row, timestamps) def parse (self, codec): - try: - opcode = chr (codec.decode_octet ()) - except EOF: - return 0 - - cls = chr (codec.decode_octet ()) - oid = codec.decode_short () - len = codec.decode_long () - - if len < 8: - raise ValueError ("parse error: value of length field too small") + opcode = chr (codec.decode_octet ()) + cls = chr (codec.decode_octet ()) if opcode == 'S': - self.parseSchema (cls, oid, len, codec) + self.parseSchema (cls, codec) - if opcode == 'C': - self.parseContent (cls, oid, len, codec) + elif opcode == 'C': + self.parseContent (cls, codec) - return 1 + else: + raise ValueError ("Unknown opcode: %c" % opcode); def __init__ (self, broker): self.broker = broker @@ -140,7 +177,8 @@ class ManagementMetadata: #=================================================================== class ManagedBroker: - exchange = "qpid.management" + mExchange = "qpid.management" + dExchange = "amq.direct" def checkHeader (self, codec): octet = chr (codec.decode_octet ()) @@ -157,69 +195,141 @@ class ManagedBroker: return 0 return 1 - def receive_cb (self, msg): + def publish_cb (self, msg): codec = Codec (StringIO (msg.content.body), self.spec) if self.checkHeader (codec) == 0: raise ValueError ("outer header invalid"); - while self.metadata.parse (codec): - pass - + self.metadata.parse (codec) msg.complete () - def __init__ (self, host = "localhost", port = 5672, - username = "guest", password = "guest"): + def reply_cb (self, msg): + codec = Codec (StringIO (msg.content.body), self.spec) + methodId = codec.decode_long () + status = codec.decode_long () + sText = codec.decode_shortstr () + + args = {} + if status == 0: + args["sequence"] = codec.decode_long () + args["body"] = codec.decode_longstr () + + if self.method_cb != None: + self.method_cb[1] (self.method_cb[0], methodId, status, sText, args) - self.spec = qpid.spec.load ("../specs/amqp.0-10-preview.xml") - self.client = None - self.channel = None - self.queue = None - self.qname = None - self.metadata = ManagementMetadata (self) + msg.complete () + + def __init__ (self, + host = "localhost", + port = 5672, + username = "guest", + password = "guest", + specfile = "../specs/amqp.0-10-preview.xml"): + + self.spec = qpid.spec.load (specfile) + self.client = None + self.channel = None + self.queue = None + self.rqueue = None + self.qname = None + self.rqname = None + self.metadata = ManagementMetadata (self) + self.connected = 0 + self.lastConnectError = None # Initialize the callback records + self.status_cb = None self.schema_cb = None self.config_cb = None self.inst_cb = None + self.method_cb = None self.host = host self.port = port self.username = username self.password = password + def statusListener (self, context, callback): + self.status_cb = (context, callback) + def schemaListener (self, context, callback): self.schema_cb = (context, callback) def configListener (self, context, callback): self.config_cb = (context, callback) + def methodListener (self, context, callback): + self.method_cb = (context, callback) + def instrumentationListener (self, context, callback): self.inst_cb = (context, callback) + def method (self, methodId, objId, className, + methodName, args=None, packageName="qpid"): + codec = Codec (StringIO (), self.spec); + codec.encode_long (methodId) + codec.encode_longlong (objId) + codec.encode_shortstr (self.rqname) + + # TODO: Encode args according to schema + if methodName == "echo": + codec.encode_long (args["sequence"]) + codec.encode_longstr (args["body"]) + + msg = Content (codec.stream.getvalue ()) + msg["content_type"] = "application/octet-stream" + msg["routing_key"] = "method." + packageName + "." + className + "." + methodName + msg["reply_to"] = self.spec.struct ("reply_to") + self.channel.message_transfer (destination="qpid.management", content=msg) + + def isConnected (self): + return connected + def start (self): - print "Connecting to broker", self.host + print "Connecting to broker %s:%d" % (self.host, self.port) try: self.client = Client (self.host, self.port, self.spec) self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) self.channel = self.client.channel (1) - response = self.channel.session_open (detached_lifetime=300) - self.qname = "mgmt-" + base64.urlsafe_b64encode(response.session_id) + response = self.channel.session_open (detached_lifetime=10) + self.qname = "mgmt-" + base64.urlsafe_b64encode (response.session_id) + self.rqname = "reply-" + base64.urlsafe_b64encode (response.session_id) + + self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) + self.channel.queue_declare (queue=self.rqname, exclusive=1, auto_delete=1) + + self.channel.queue_bind (exchange=ManagedBroker.mExchange, queue=self.qname, + routing_key="mgmt.#") + self.channel.queue_bind (exchange=ManagedBroker.dExchange, queue=self.rqname, + routing_key=self.rqname) + + self.channel.message_subscribe (queue=self.qname, destination="mdest") + self.channel.message_subscribe (queue=self.rqname, destination="rdest") + + self.queue = self.client.queue ("mdest") + self.queue.listen (self.publish_cb) - self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) - self.channel.queue_bind (exchange=ManagedBroker.exchange, queue=self.qname, - routing_key="mgmt") - self.channel.message_subscribe (queue=self.qname, destination="dest") - self.queue = self.client.queue ("dest") - self.queue.listen (self.receive_cb) + self.channel.message_flow_mode (destination="mdest", mode=1) + self.channel.message_flow (destination="mdest", unit=0, value=0xFFFFFFFF) + self.channel.message_flow (destination="mdest", unit=1, value=0xFFFFFFFF) - self.channel.message_flow_mode (destination="dest", mode=1) - self.channel.message_flow (destination="dest", unit=0, value=0xFFFFFFFF) - self.channel.message_flow (destination="dest", unit=1, value=0xFFFFFFFF) + self.rqueue = self.client.queue ("rdest") + self.rqueue.listen (self.reply_cb) + + self.channel.message_flow_mode (destination="rdest", mode=1) + self.channel.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) + self.channel.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) + + self.connected = 1 except socket.error, e: print "Socket Error Detected:", e[1] + self.lastConnectError = e raise except: raise + + def stop (self): + pass -- cgit v1.2.1 From c3fff86dc51cae6bd40bb741f339584a386e9dcd Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 16 Nov 2007 12:11:09 +0000 Subject: Additional test covering the current hack for no-local used on exclusive queues Additional check to second not-acquired test, asserting that messages aren't consumed git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@595636 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 5d251a9c8e..fbd3d255de 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -51,6 +51,45 @@ class MessageTests(TestBase): self.fail("Received locally published message though no_local=true") except Empty: None + def test_consume_no_local_awkward(self): + + """ + If an exclusive queue gets a no-local delivered to it, that + message could 'block' delivery of subsequent messages or it + could be left on the queue, possibly never being consumed + (this is the case for example in the qpid JMS mapping of + topics). This test excercises a Qpid C++ broker hack that + deletes such messages. + """ + + channel = self.channel + #setup: + channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + #establish consumer which excludes delivery of locally sent messages + self.subscribe(destination="local_excluded", queue="test-queue", no_local=True) + + #send a 'local' message + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="local")) + + #send a non local message + other = self.connect() + channel2 = other.channel(1) + channel2.session_open() + channel2.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="foreign")) + channel2.session_close() + other.close() + + #check that the second message only is delivered + excluded = self.client.queue("local_excluded") + msg = excluded.get(timeout=1) + self.assertEqual("foreign", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received extra message") + except Empty: None + #check queue is empty + self.assertEqual(0, channel.queue_query(queue="test-queue").message_count) + def test_consume_exclusive(self): """ @@ -678,6 +717,9 @@ class MessageTests(TestBase): channel.message_flow(unit = 0, value = 1, destination = "b") self.assertEmpty(queue) + #check all 'browsed' messages are still on the queue + self.assertEqual(5, channel.queue_query(queue="q").message_count) + def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) -- cgit v1.2.1 From ad14550394b17e15724c2680dc902925461ceb0e Mon Sep 17 00:00:00 2001 From: Arnaud Simon Date: Tue, 27 Nov 2007 16:34:54 +0000 Subject: new file git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@598684 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/perftest | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100755 qpid/python/perftest (limited to 'qpid/python') diff --git a/qpid/python/perftest b/qpid/python/perftest new file mode 100755 index 0000000000..2e9148ce50 --- /dev/null +++ b/qpid/python/perftest @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +def publisher(n): + import qpid + import sys + from qpid.client import Client + from qpid.content import Content + if len(sys.argv) >= 3: + n = int(sys.argv[2]) + client = Client("127.0.0.1", 5672) + client.start({"LOGIN": "guest", "PASSWORD": "guest"}) + channel = client.channel(1) + channel.session_open() + message = Content("message") + message["routing_key"] = "message_queue" + print "producing ", n, " messages" + for i in range(n): + channel.message_transfer(destination="amq.direct", content=message) + + print "producing final message" + message = Content("That's done") + message["routing_key"] = "message_queue" + channel.message_transfer(destination="amq.direct", content=message) + + print "consuming sync message" + consumer = "consumer" + queue = client.queue(consumer) + channel.message_subscribe(queue="sync_queue", destination=consumer) + channel.message_flow(consumer, 0, 0xFFFFFFFF) + channel.message_flow(consumer, 1, 0xFFFFFFFF) + queue.get(block = True) + print "done" + channel.session_close() + +def consumer(): + import sys + import qpid + from qpid.client import Client + from qpid.content import Content + client = Client("127.0.0.1", 5672) + client.start({"LOGIN": "guest", "PASSWORD": "guest"}) + channel = client.channel(1) + channel.session_open() + consumer = "consumer" + queue = client.queue(consumer) + channel.message_subscribe(queue="message_queue", destination=consumer) + channel.message_flow(consumer, 0, 0xFFFFFFFF) + channel.message_flow(consumer, 1, 0xFFFFFFFF) + final = "That's done" + content = "" + message = None + print "getting messages" + while content != final: + message = queue.get(block = True) + content = message.content.body + message.complete(cumulative=True) + + print "consumed all messages" + message = Content("message") + message["routing_key"] = "sync_queue" + channel.message_transfer(destination="amq.direct", content=message) + print "done" + channel.session_close() + +if __name__=='__main__': + import sys + import qpid + from timeit import Timer + from qpid.client import Client + from qpid.content import Content + client = Client("127.0.0.1", 5672) + client.start({"LOGIN": "guest", "PASSWORD": "guest"}) + channel = client.channel(1) + channel.session_open() + channel.queue_declare(queue="message_queue") + channel.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="message_queue") + channel.queue_declare(queue="sync_queue") + channel.queue_bind(exchange="amq.direct", queue="sync_queue", routing_key="sync_queue") + channel.session_close() + + numMess = 100 + if len(sys.argv) >= 3: + numMess = int(sys.argv[2]) + if len(sys.argv) == 1: + print "error: please specify prod or cons" + elif sys.argv[1] == 'prod': + tprod = Timer("publisher(100)", "from __main__ import publisher") + tp = tprod.timeit(1) + print "produced and consumed" , numMess + 2 ,"messages in: ", tp + elif sys.argv[1] == 'cons': + tcons = Timer("consumer()", "from __main__ import consumer") + tc = tcons.timeit(1) + print "consumed " , numMess ," in: ", tc + else: + print "please specify prod or cons" -- cgit v1.2.1 From cbe6690289988a6e5fda4d562352394e9b1a72c7 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 30 Nov 2007 17:11:47 +0000 Subject: Python examples git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@599876 13f79535-47bb-0310-9956-ffa450edef68 --- .../examples/direct/config_direct_exchange.py | 57 ++++++++++ qpid/python/examples/direct/direct_consumer.py | 75 +++++++++++++ qpid/python/examples/direct/direct_producer.py | 51 +++++++++ .../examples/fanout/config_fanout_exchange.py | 54 ++++++++++ qpid/python/examples/fanout/fanout_consumer.py | 75 +++++++++++++ qpid/python/examples/fanout/fanout_producer.py | 49 +++++++++ qpid/python/examples/pubsub/topic_consumer.py | 120 +++++++++++++++++++++ qpid/python/examples/pubsub/topic_producer.py | 100 +++++++++++++++++ qpid/python/examples/request-response/client.py | 112 +++++++++++++++++++ qpid/python/examples/request-response/server.py | 89 +++++++++++++++ 10 files changed, 782 insertions(+) create mode 100644 qpid/python/examples/direct/config_direct_exchange.py create mode 100644 qpid/python/examples/direct/direct_consumer.py create mode 100644 qpid/python/examples/direct/direct_producer.py create mode 100644 qpid/python/examples/fanout/config_fanout_exchange.py create mode 100644 qpid/python/examples/fanout/fanout_consumer.py create mode 100644 qpid/python/examples/fanout/fanout_producer.py create mode 100644 qpid/python/examples/pubsub/topic_consumer.py create mode 100644 qpid/python/examples/pubsub/topic_producer.py create mode 100644 qpid/python/examples/request-response/client.py create mode 100644 qpid/python/examples/request-response/server.py (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/config_direct_exchange.py b/qpid/python/examples/direct/config_direct_exchange.py new file mode 100644 index 0000000000..45af6f63b9 --- /dev/null +++ b/qpid/python/examples/direct/config_direct_exchange.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.direct" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") + +#----- Cleanup --------------------------------------------- + +# Clean up before exiting so there are no open threads. +# +# Close Channel 1. +# Close the connection using Channel 0, which is used for all connection methods. +session.session_close() + diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py new file mode 100644 index 0000000000..4e50dfbc2a --- /dev/null +++ b/qpid/python/examples/direct/direct_consumer.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? + +# Initialize 'final' and 'content', variables used to identify the last message. + +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read + +message = None +while content != final: + message = queue.get(timeout=10) + content = message.content.body + print content + +# Messages are not removed from the queue until they are +# acknowledged. Using cumulative=True, all messages from the session +# up to and including the one identified by the delivery tag are +# acknowledged. This is more efficient, because there are fewer +# network round-trips. + +message.complete(cumulative=True) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +# session.session_close() diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py new file mode 100644 index 0000000000..6770e56803 --- /dev/null +++ b/qpid/python/examples/direct/direct_producer.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" + direct_producer.py + + Publishes messages to an AMQP direct exchange, using + the routing key "routing_key" +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +for i in range(10): + message = Content("message " + str(i)) + message["routing_key"] = "routing_key" + session.message_transfer(destination="amq.direct", content=message) + +final="That's all, folks!" +message = Content(final) +message["routing_key"] = "routing_key" +session.message_transfer(destination="amq.direct", content=message) + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() + diff --git a/qpid/python/examples/fanout/config_fanout_exchange.py b/qpid/python/examples/fanout/config_fanout_exchange.py new file mode 100644 index 0000000000..6eef1b94e3 --- /dev/null +++ b/qpid/python/examples/fanout/config_fanout_exchange.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.fanout" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.fanout", queue="message_queue") + +#----- Cleanup --------------------------------------------- + +# Clean up before exiting so there are no open threads. +# channel.session_close() + diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py new file mode 100644 index 0000000000..e9a2291f4c --- /dev/null +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + +# Initialize 'final' and 'content', variables used to identify the last message. + +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read + +message = None +while content != final: + message = queue.get(timeout=10) + content = message.content.body + print content + +# Messages are not removed from the queue until they are +# acknowledged. Using cumulative=True, all messages from the session +# up to and including the one identified by the delivery tag are +# acknowledged. This is more efficient, because there are fewer +# network round-trips. + +message.complete(cumulative=True) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py new file mode 100644 index 0000000000..42570ed510 --- /dev/null +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +""" + direct_producer.py + + Publishes messages to an AMQP direct exchange, using + the routing key "routing_key" +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +for i in range(10): + message = Content(body="message " + str(i)) + session.message_transfer(destination="amq.fanout", content=message) + +final="That's all, folks!" +message=Content(final) +session.message_transfer(destination="amq.fanout", content=message) + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() + diff --git a/qpid/python/examples/pubsub/topic_consumer.py b/qpid/python/examples/pubsub/topic_consumer.py new file mode 100644 index 0000000000..5db04573f8 --- /dev/null +++ b/qpid/python/examples/pubsub/topic_consumer.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +""" + topic_consumer.py + + This AMQP client reads all messages from the + "news", "weather", "usa", and "europe" queues + created and bound by config_topic_exchange.py. +""" + +import base64 + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Functions ------------------------------------------- + +def dump_queue(client, queue_name): + + print "Messages queue: " + queue_name + + consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag + queue = client.queue(consumer_tag) + + # Call basic_consume() to tell the broker to deliver messages + # from the AMQP queue to a local client queue. The broker will + # start delivering messages as soon as basic_consume() is called. + + session.message_subscribe(queue=queue_name, destination=consumer_tag) + session.message_flow(consumer_tag, 0, 0xFFFFFFFF) + session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + + content = "" # Content of the last message read + final = "That's all, folks!" # In a message body, signals the last message + message = 0 + + while content != final: + try: + message = queue.get() + content = message.content.body + print content + except Empty: + if message != 0: + message.complete(cumulative=True) + print "No more messages!" + return + + + # Messages are not removed from the queue until they + # are acknowledged. Using multiple=True, all messages + # in the channel up to and including the one identified + # by the delivery tag are acknowledged. This is more efficient, + # because there are fewer network round-trips. + + if message != 0: + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session = session.session_open() # keep the session object, we'll need the session id + +#----- Main Body -- ---------------------------------------- + + +news = "news" + base64.urlsafe_b64encode(session.session_id) +weather = "weather" + base64.urlsafe_b64encode(session.session_id) +usa = "usa" + base64.urlsafe_b64encode(session.session_id) +europe = "europe" + base64.urlsafe_b64encode(session.session_id) + +session.queue_declare(queue=news, exclusive=True) +session.queue_declare(queue=weather, exclusive=True) +session.queue_declare(queue=usa, exclusive=True) +session.queue_declare(queue=europe, exclusive=True) + +# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". + +# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches +# "europe.news" or "usa.news". + +session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") +session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") +session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") +session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") + +# Remind the user to start the topic producer + +print "Queues create - please start the topic producer" + +# Call dump_queue to print messages from each queue + +dump_queue(client, news) +dump_queue(client, weather) +dump_queue(client, usa) +dump_queue(client, europe) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# +# Close Channel 1. + +session.session_close() + diff --git a/qpid/python/examples/pubsub/topic_producer.py b/qpid/python/examples/pubsub/topic_producer.py new file mode 100644 index 0000000000..5f8372e7ba --- /dev/null +++ b/qpid/python/examples/pubsub/topic_producer.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +""" + topic_producer.py + + This is a simple AMQP publisher application that uses a + Topic exchange. The publisher specifies the routing key + and the exchange for each message. +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login. + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. Use the +# topic exchange. The routing keys are "usa.news", "usa.weather", +# "europe.news", and "europe.weather". + +final = "That's all, folks!" + +# We'll use the same routing key for all messages in the loop, and +# also for the terminating message. + +# usa.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.news" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "usa.news" +session.message_transfer(destination="amq.topic", content=message) + +# usa.weather + + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.weather" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "usa.weather" +session.message_transfer(destination="amq.topic", content=message) + +# europe.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.news" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "europe.news" +session.message_transfer(destination="amq.topic", content=message) + + +# europe.weather + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.weather" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "europe.weather" +session.message_transfer(destination="amq.topic", content=message) + + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. +# +# Close Channel 1. + + +session.session_close() + diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py new file mode 100644 index 0000000000..23e9310509 --- /dev/null +++ b/qpid/python/examples/request-response/client.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +""" + client.py + + Client for a client/server example + +""" + +import base64 + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Functions ------------------------------------------- + +def dump_queue(client, queue_name): + + print "Messages queue: " + queue_name + + consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag + queue = client.queue(consumer_tag) + + # Call basic_consume() to tell the broker to deliver messages + # from the AMQP queue to a local client queue. The broker will + # start delivering messages as soon as basic_consume() is called. + + session.message_subscribe(queue=queue_name, destination=consumer_tag) + session.message_flow(consumer_tag, 0, 0xFFFFFFFF) + session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + + message = 0 + + while True: + try: + message = queue.get(timeout=10) + content = message.content.body + print "Response: " + content + except Empty: + print "No more messages!" + break + except: + print "Unexpected exception!" + break + + + # Messages are not removed from the queue until they + # are acknowledged. Using multiple=True, all messages + # in the channel up to and including the one identified + # by the delivery tag are acknowledged. This is more efficient, + # because there are fewer network round-trips. + + if message != 0: + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Main Body -- ---------------------------------------- + +# Create a response queue for the server to send responses to. Use the +# same string as the name of the queue and the name of the routing +# key. + +replyTo = "ReplyTo:" # + base64.urlsafe_b64encode(session.session_id) +session.queue_declare(queue=replyTo, exclusive=True) +session.queue_bind(exchange="amq.direct", queue=replyTo, routing_key=replyTo) + +# Send some messages to the server's request queue + +lines = ["Twas brilling, and the slithy toves", + "Did gyre and gimble in the wabe.", + "All mimsy were the borogroves,", + "And the mome raths outgrabe."] + +for l in lines: + print "Request: " + l + request=Content(l) + request["routing_key"] = "request" + request["reply_to"] = client.spec.struct("reply_to") + request["reply_to"]["exchange_name"] = "amq.direct" + request["reply_to"]["routing_key"] = replyTo + session.message_transfer(destination="amq.direct", content=request) + +# Now see what messages the server sent to our replyTo queue + +dump_queue(client, replyTo) + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py new file mode 100644 index 0000000000..dd81b419e8 --- /dev/null +++ b/qpid/python/examples/request-response/server.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" + server.py + + Server for a client/server example +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Functions ------------------------------------------- + +def respond(session, request): + + # The routing key for the response is the request's reply-to + # property. The body for the response is the request's body, + # converted to upper case. + + response=Content(request.body.upper()) + response["routing_key"] = request["reply_to"]["routing_key"] + + session.message_transfer(destination=request["reply_to"]["exchange_name"], content=response) + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +# Open Channel 1 so we can use it to manage our queue. + +session = client.session() +session.session_open() # keep the session object, we'll need the session id + +#----- Main Body -- ---------------------------------------- + +# Create a request queue and subscribe to it + +session.queue_declare(queue="request", exclusive=True) +session.queue_bind(exchange="amq.direct", queue="request", routing_key="request") + +dest = "request_destination" + +session.message_subscribe(queue="request", destination=dest) +session.message_flow(dest, 0, 0xFFFFFFFF) +session.message_flow(dest, 1, 0xFFFFFFFF) + + +# Remind the user to start the client program + +print "Request server running - run your client now." +print "(Times out after 100 seconds ...)" + +# Respond to each request + +queue = client.queue(dest) + +# If we get a message, send it back to the user (as indicated in the +# ReplyTo property) + +while True: + try: + request = queue.get(timeout=100) + respond(session, request.content) + request.complete() + except Empty: + print "No more messages!" + break; + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# +# Close Channel 1. + +session.session_close() + -- cgit v1.2.1 From a5d215809f5b5425715189b5c929c496046d1ae4 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 4 Dec 2007 22:18:38 +0000 Subject: removed warnings on duplicate constants, the issue will go away with 0-10 final git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@601112 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 537ced113b..623b2e9e9f 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -372,7 +372,8 @@ def load(specfile, *errata): try: spec.constants.add(const) except ValueError, e: - print "Warning:", e + pass + #print "Warning:", e # domains are typedefs structs = [] -- cgit v1.2.1 From 387a53a7dd18dd0bd3dd4a36b2a600cfbd60f249 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 5 Dec 2007 15:19:45 +0000 Subject: Updates to examples from jonathan.robie@redhat.com git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@601358 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/config_direct_exchange.py | 4 ---- qpid/python/examples/direct/direct_consumer.py | 2 +- qpid/python/examples/fanout/config_fanout_exchange.py | 2 +- qpid/python/examples/fanout/fanout_producer.py | 1 - qpid/python/examples/pubsub/topic_consumer.py | 14 ++++++-------- qpid/python/examples/pubsub/topic_producer.py | 4 ---- qpid/python/examples/request-response/client.py | 15 +++++++++------ qpid/python/examples/request-response/server.py | 5 +---- 8 files changed, 18 insertions(+), 29 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/config_direct_exchange.py b/qpid/python/examples/direct/config_direct_exchange.py index 45af6f63b9..e64ad678b8 100644 --- a/qpid/python/examples/direct/config_direct_exchange.py +++ b/qpid/python/examples/direct/config_direct_exchange.py @@ -49,9 +49,5 @@ session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="ro #----- Cleanup --------------------------------------------- -# Clean up before exiting so there are no open threads. -# -# Close Channel 1. -# Close the connection using Channel 0, which is used for all connection methods. session.session_close() diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index 4e50dfbc2a..38b1ba30a0 100644 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -72,4 +72,4 @@ message.complete(cumulative=True) # Clean up before exiting so there are no open threads. # -# session.session_close() +session.session_close() diff --git a/qpid/python/examples/fanout/config_fanout_exchange.py b/qpid/python/examples/fanout/config_fanout_exchange.py index 6eef1b94e3..3315f5bc14 100644 --- a/qpid/python/examples/fanout/config_fanout_exchange.py +++ b/qpid/python/examples/fanout/config_fanout_exchange.py @@ -50,5 +50,5 @@ session.queue_bind(exchange="amq.fanout", queue="message_queue") #----- Cleanup --------------------------------------------- # Clean up before exiting so there are no open threads. -# channel.session_close() +session.session_close() diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 42570ed510..92ca7b7ec0 100644 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -46,4 +46,3 @@ session.message_transfer(destination="amq.fanout", content=message) # Clean up before exiting so there are no open threads. session.session_close() - diff --git a/qpid/python/examples/pubsub/topic_consumer.py b/qpid/python/examples/pubsub/topic_consumer.py index 5db04573f8..afe8bba91e 100644 --- a/qpid/python/examples/pubsub/topic_consumer.py +++ b/qpid/python/examples/pubsub/topic_consumer.py @@ -74,15 +74,16 @@ client = Client(host, port, spec) client.start({"LOGIN": user, "PASSWORD": password}) session = client.session() -session = session.session_open() # keep the session object, we'll need the session id +session_info = session.session_open() +session_id = session_info.session_id #----- Main Body -- ---------------------------------------- -news = "news" + base64.urlsafe_b64encode(session.session_id) -weather = "weather" + base64.urlsafe_b64encode(session.session_id) -usa = "usa" + base64.urlsafe_b64encode(session.session_id) -europe = "europe" + base64.urlsafe_b64encode(session.session_id) +news = "news" + base64.urlsafe_b64encode(session_id) +weather = "weather" + base64.urlsafe_b64encode(session_id) +usa = "usa" + base64.urlsafe_b64encode(session_id) +europe = "europe" + base64.urlsafe_b64encode(session_id) session.queue_declare(queue=news, exclusive=True) session.queue_declare(queue=weather, exclusive=True) @@ -113,8 +114,5 @@ dump_queue(client, europe) #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. -# -# Close Channel 1. session.session_close() - diff --git a/qpid/python/examples/pubsub/topic_producer.py b/qpid/python/examples/pubsub/topic_producer.py index 5f8372e7ba..c3b13cd82c 100644 --- a/qpid/python/examples/pubsub/topic_producer.py +++ b/qpid/python/examples/pubsub/topic_producer.py @@ -92,9 +92,5 @@ session.message_transfer(destination="amq.topic", content=message) #----- Cleanup -------------------------------------------- # Clean up before exiting so there are no open threads. -# -# Close Channel 1. - session.session_close() - diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 23e9310509..bc0cf8a55a 100644 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -22,7 +22,7 @@ def dump_queue(client, queue_name): consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag queue = client.queue(consumer_tag) - # Call basic_consume() to tell the broker to deliver messages + # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to a local client queue. The broker will # start delivering messages as soon as basic_consume() is called. @@ -46,8 +46,8 @@ def dump_queue(client, queue_name): # Messages are not removed from the queue until they - # are acknowledged. Using multiple=True, all messages - # in the channel up to and including the one identified + # are acknowledged. Using cumulative=True, all messages + # in the session up to and including the one identified # by the delivery tag are acknowledged. This is more efficient, # because there are fewer network round-trips. @@ -71,8 +71,11 @@ spec = qpid.spec.load(amqp_spec) client = Client(host, port, spec) client.start({"LOGIN": user, "PASSWORD": password}) +# Open the session. Save the session id. + session = client.session() -session.session_open() +session_info = session.session_open() +session_id = session_info.session_id #----- Main Body -- ---------------------------------------- @@ -80,7 +83,7 @@ session.session_open() # same string as the name of the queue and the name of the routing # key. -replyTo = "ReplyTo:" # + base64.urlsafe_b64encode(session.session_id) +replyTo = "ReplyTo:" + base64.urlsafe_b64encode(session_id) session.queue_declare(queue=replyTo, exclusive=True) session.queue_bind(exchange="amq.direct", queue=replyTo, routing_key=replyTo) @@ -109,4 +112,4 @@ dump_queue(client, replyTo) # Clean up before exiting so there are no open threads. -session.session_close() +session.session_close() diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index dd81b419e8..28bfd5ee4a 100644 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -41,7 +41,7 @@ client.start({"LOGIN": user, "PASSWORD": password}) # Open Channel 1 so we can use it to manage our queue. session = client.session() -session.session_open() # keep the session object, we'll need the session id +session.session_open() #----- Main Body -- ---------------------------------------- @@ -82,8 +82,5 @@ while True: #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. -# -# Close Channel 1. session.session_close() - -- cgit v1.2.1 From 46989a4ce0c90b73dbcb76cb344c95b0de27d360 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 5 Dec 2007 16:22:50 +0000 Subject: Renamed for consistency with c++ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@601394 13f79535-47bb-0310-9956-ffa450edef68 --- .../examples/direct/config_direct_exchange.py | 53 --------- .../python/examples/direct/direct_config_queues.py | 53 +++++++++ qpid/python/examples/direct/direct_consumer.py | 75 ------------- qpid/python/examples/direct/direct_producer.py | 51 --------- qpid/python/examples/direct/direct_publisher.py | 51 +++++++++ qpid/python/examples/direct/listener.py | 75 +++++++++++++ .../examples/fanout/config_fanout_exchange.py | 54 ---------- .../python/examples/fanout/fanout_config_queues.py | 54 ++++++++++ qpid/python/examples/fanout/fanout_consumer.py | 75 ------------- qpid/python/examples/fanout/fanout_producer.py | 48 --------- qpid/python/examples/fanout/fanout_publisher.py | 48 +++++++++ qpid/python/examples/fanout/listener.py | 75 +++++++++++++ qpid/python/examples/pubsub/topic_consumer.py | 118 --------------------- qpid/python/examples/pubsub/topic_listener.py | 118 +++++++++++++++++++++ qpid/python/examples/pubsub/topic_producer.py | 96 ----------------- qpid/python/examples/pubsub/topic_publisher.py | 96 +++++++++++++++++ 16 files changed, 570 insertions(+), 570 deletions(-) delete mode 100644 qpid/python/examples/direct/config_direct_exchange.py create mode 100644 qpid/python/examples/direct/direct_config_queues.py delete mode 100644 qpid/python/examples/direct/direct_consumer.py delete mode 100644 qpid/python/examples/direct/direct_producer.py create mode 100644 qpid/python/examples/direct/direct_publisher.py create mode 100644 qpid/python/examples/direct/listener.py delete mode 100644 qpid/python/examples/fanout/config_fanout_exchange.py create mode 100644 qpid/python/examples/fanout/fanout_config_queues.py delete mode 100644 qpid/python/examples/fanout/fanout_consumer.py delete mode 100644 qpid/python/examples/fanout/fanout_producer.py create mode 100644 qpid/python/examples/fanout/fanout_publisher.py create mode 100644 qpid/python/examples/fanout/listener.py delete mode 100644 qpid/python/examples/pubsub/topic_consumer.py create mode 100644 qpid/python/examples/pubsub/topic_listener.py delete mode 100644 qpid/python/examples/pubsub/topic_producer.py create mode 100644 qpid/python/examples/pubsub/topic_publisher.py (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/config_direct_exchange.py b/qpid/python/examples/direct/config_direct_exchange.py deleted file mode 100644 index e64ad678b8..0000000000 --- a/qpid/python/examples/direct/config_direct_exchange.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -""" - config_direct_exchange.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.direct" exchange. -# -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") - -#----- Cleanup --------------------------------------------- - -session.session_close() - diff --git a/qpid/python/examples/direct/direct_config_queues.py b/qpid/python/examples/direct/direct_config_queues.py new file mode 100644 index 0000000000..e64ad678b8 --- /dev/null +++ b/qpid/python/examples/direct/direct_config_queues.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.direct" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") + +#----- Cleanup --------------------------------------------- + +session.session_close() + diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py deleted file mode 100644 index 38b1ba30a0..0000000000 --- a/qpid/python/examples/direct/direct_consumer.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -""" - direct_consumer.py - - This AMQP client reads messages from a message - queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Read from queue -------------------------------------------- - -# Now let's create a local client queue and tell it to read -# incoming messages. - -# The consumer tag identifies the client-side queue. - -consumer_tag = "consumer1" -queue = client.queue(consumer_tag) - -# Call message_consume() to tell the broker to deliver messages -# from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. - -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? - -# Initialize 'final' and 'content', variables used to identify the last message. - -final = "That's all, folks!" # In a message body, signals the last message -content = "" # Content of the last message read - -message = None -while content != final: - message = queue.get(timeout=10) - content = message.content.body - print content - -# Messages are not removed from the queue until they are -# acknowledged. Using cumulative=True, all messages from the session -# up to and including the one identified by the delivery tag are -# acknowledged. This is more efficient, because there are fewer -# network round-trips. - -message.complete(cumulative=True) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. -# - -session.session_close() diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py deleted file mode 100644 index 6770e56803..0000000000 --- a/qpid/python/examples/direct/direct_producer.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -""" - direct_producer.py - - Publishes messages to an AMQP direct exchange, using - the routing key "routing_key" -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. - -for i in range(10): - message = Content("message " + str(i)) - message["routing_key"] = "routing_key" - session.message_transfer(destination="amq.direct", content=message) - -final="That's all, folks!" -message = Content(final) -message["routing_key"] = "routing_key" -session.message_transfer(destination="amq.direct", content=message) - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() - diff --git a/qpid/python/examples/direct/direct_publisher.py b/qpid/python/examples/direct/direct_publisher.py new file mode 100644 index 0000000000..6770e56803 --- /dev/null +++ b/qpid/python/examples/direct/direct_publisher.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" + direct_producer.py + + Publishes messages to an AMQP direct exchange, using + the routing key "routing_key" +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +for i in range(10): + message = Content("message " + str(i)) + message["routing_key"] = "routing_key" + session.message_transfer(destination="amq.direct", content=message) + +final="That's all, folks!" +message = Content(final) +message["routing_key"] = "routing_key" +session.message_transfer(destination="amq.direct", content=message) + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() + diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py new file mode 100644 index 0000000000..38b1ba30a0 --- /dev/null +++ b/qpid/python/examples/direct/listener.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? + +# Initialize 'final' and 'content', variables used to identify the last message. + +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read + +message = None +while content != final: + message = queue.get(timeout=10) + content = message.content.body + print content + +# Messages are not removed from the queue until they are +# acknowledged. Using cumulative=True, all messages from the session +# up to and including the one identified by the delivery tag are +# acknowledged. This is more efficient, because there are fewer +# network round-trips. + +message.complete(cumulative=True) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() diff --git a/qpid/python/examples/fanout/config_fanout_exchange.py b/qpid/python/examples/fanout/config_fanout_exchange.py deleted file mode 100644 index 3315f5bc14..0000000000 --- a/qpid/python/examples/fanout/config_fanout_exchange.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -""" - config_direct_exchange.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.fanout" exchange. -# -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.fanout", queue="message_queue") - -#----- Cleanup --------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/fanout/fanout_config_queues.py b/qpid/python/examples/fanout/fanout_config_queues.py new file mode 100644 index 0000000000..3315f5bc14 --- /dev/null +++ b/qpid/python/examples/fanout/fanout_config_queues.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.fanout" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.fanout", queue="message_queue") + +#----- Cleanup --------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py deleted file mode 100644 index e9a2291f4c..0000000000 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -""" - direct_consumer.py - - This AMQP client reads messages from a message - queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Read from queue -------------------------------------------- - -# Now let's create a local client queue and tell it to read -# incoming messages. - -# The consumer tag identifies the client-side queue. - -consumer_tag = "consumer1" -queue = client.queue(consumer_tag) - -# Call message_consume() to tell the broker to deliver messages -# from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. - -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - -# Initialize 'final' and 'content', variables used to identify the last message. - -final = "That's all, folks!" # In a message body, signals the last message -content = "" # Content of the last message read - -message = None -while content != final: - message = queue.get(timeout=10) - content = message.content.body - print content - -# Messages are not removed from the queue until they are -# acknowledged. Using cumulative=True, all messages from the session -# up to and including the one identified by the delivery tag are -# acknowledged. This is more efficient, because there are fewer -# network round-trips. - -message.complete(cumulative=True) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. -# - -session.session_close() diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py deleted file mode 100644 index 92ca7b7ec0..0000000000 --- a/qpid/python/examples/fanout/fanout_producer.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -""" - direct_producer.py - - Publishes messages to an AMQP direct exchange, using - the routing key "routing_key" -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. - -for i in range(10): - message = Content(body="message " + str(i)) - session.message_transfer(destination="amq.fanout", content=message) - -final="That's all, folks!" -message=Content(final) -session.message_transfer(destination="amq.fanout", content=message) - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/fanout/fanout_publisher.py b/qpid/python/examples/fanout/fanout_publisher.py new file mode 100644 index 0000000000..92ca7b7ec0 --- /dev/null +++ b/qpid/python/examples/fanout/fanout_publisher.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +""" + direct_producer.py + + Publishes messages to an AMQP direct exchange, using + the routing key "routing_key" +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +for i in range(10): + message = Content(body="message " + str(i)) + session.message_transfer(destination="amq.fanout", content=message) + +final="That's all, folks!" +message=Content(final) +session.message_transfer(destination="amq.fanout", content=message) + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py new file mode 100644 index 0000000000..e9a2291f4c --- /dev/null +++ b/qpid/python/examples/fanout/listener.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + +# Initialize 'final' and 'content', variables used to identify the last message. + +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read + +message = None +while content != final: + message = queue.get(timeout=10) + content = message.content.body + print content + +# Messages are not removed from the queue until they are +# acknowledged. Using cumulative=True, all messages from the session +# up to and including the one identified by the delivery tag are +# acknowledged. This is more efficient, because there are fewer +# network round-trips. + +message.complete(cumulative=True) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() diff --git a/qpid/python/examples/pubsub/topic_consumer.py b/qpid/python/examples/pubsub/topic_consumer.py deleted file mode 100644 index afe8bba91e..0000000000 --- a/qpid/python/examples/pubsub/topic_consumer.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python -""" - topic_consumer.py - - This AMQP client reads all messages from the - "news", "weather", "usa", and "europe" queues - created and bound by config_topic_exchange.py. -""" - -import base64 - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Functions ------------------------------------------- - -def dump_queue(client, queue_name): - - print "Messages queue: " + queue_name - - consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = client.queue(consumer_tag) - - # Call basic_consume() to tell the broker to deliver messages - # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as basic_consume() is called. - - session.message_subscribe(queue=queue_name, destination=consumer_tag) - session.message_flow(consumer_tag, 0, 0xFFFFFFFF) - session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - - content = "" # Content of the last message read - final = "That's all, folks!" # In a message body, signals the last message - message = 0 - - while content != final: - try: - message = queue.get() - content = message.content.body - print content - except Empty: - if message != 0: - message.complete(cumulative=True) - print "No more messages!" - return - - - # Messages are not removed from the queue until they - # are acknowledged. Using multiple=True, all messages - # in the channel up to and including the one identified - # by the delivery tag are acknowledged. This is more efficient, - # because there are fewer network round-trips. - - if message != 0: - message.complete(cumulative=True) - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session_info = session.session_open() -session_id = session_info.session_id - -#----- Main Body -- ---------------------------------------- - - -news = "news" + base64.urlsafe_b64encode(session_id) -weather = "weather" + base64.urlsafe_b64encode(session_id) -usa = "usa" + base64.urlsafe_b64encode(session_id) -europe = "europe" + base64.urlsafe_b64encode(session_id) - -session.queue_declare(queue=news, exclusive=True) -session.queue_declare(queue=weather, exclusive=True) -session.queue_declare(queue=usa, exclusive=True) -session.queue_declare(queue=europe, exclusive=True) - -# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". - -# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches -# "europe.news" or "usa.news". - -session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") -session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") -session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") -session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") - -# Remind the user to start the topic producer - -print "Queues create - please start the topic producer" - -# Call dump_queue to print messages from each queue - -dump_queue(client, news) -dump_queue(client, weather) -dump_queue(client, usa) -dump_queue(client, europe) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/pubsub/topic_listener.py b/qpid/python/examples/pubsub/topic_listener.py new file mode 100644 index 0000000000..afe8bba91e --- /dev/null +++ b/qpid/python/examples/pubsub/topic_listener.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +""" + topic_consumer.py + + This AMQP client reads all messages from the + "news", "weather", "usa", and "europe" queues + created and bound by config_topic_exchange.py. +""" + +import base64 + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Functions ------------------------------------------- + +def dump_queue(client, queue_name): + + print "Messages queue: " + queue_name + + consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag + queue = client.queue(consumer_tag) + + # Call basic_consume() to tell the broker to deliver messages + # from the AMQP queue to a local client queue. The broker will + # start delivering messages as soon as basic_consume() is called. + + session.message_subscribe(queue=queue_name, destination=consumer_tag) + session.message_flow(consumer_tag, 0, 0xFFFFFFFF) + session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + + content = "" # Content of the last message read + final = "That's all, folks!" # In a message body, signals the last message + message = 0 + + while content != final: + try: + message = queue.get() + content = message.content.body + print content + except Empty: + if message != 0: + message.complete(cumulative=True) + print "No more messages!" + return + + + # Messages are not removed from the queue until they + # are acknowledged. Using multiple=True, all messages + # in the channel up to and including the one identified + # by the delivery tag are acknowledged. This is more efficient, + # because there are fewer network round-trips. + + if message != 0: + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session_info = session.session_open() +session_id = session_info.session_id + +#----- Main Body -- ---------------------------------------- + + +news = "news" + base64.urlsafe_b64encode(session_id) +weather = "weather" + base64.urlsafe_b64encode(session_id) +usa = "usa" + base64.urlsafe_b64encode(session_id) +europe = "europe" + base64.urlsafe_b64encode(session_id) + +session.queue_declare(queue=news, exclusive=True) +session.queue_declare(queue=weather, exclusive=True) +session.queue_declare(queue=usa, exclusive=True) +session.queue_declare(queue=europe, exclusive=True) + +# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". + +# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches +# "europe.news" or "usa.news". + +session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") +session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") +session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") +session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") + +# Remind the user to start the topic producer + +print "Queues create - please start the topic producer" + +# Call dump_queue to print messages from each queue + +dump_queue(client, news) +dump_queue(client, weather) +dump_queue(client, usa) +dump_queue(client, europe) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/pubsub/topic_producer.py b/qpid/python/examples/pubsub/topic_producer.py deleted file mode 100644 index c3b13cd82c..0000000000 --- a/qpid/python/examples/pubsub/topic_producer.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -""" - topic_producer.py - - This is a simple AMQP publisher application that uses a - Topic exchange. The publisher specifies the routing key - and the exchange for each message. -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login. - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. Use the -# topic exchange. The routing keys are "usa.news", "usa.weather", -# "europe.news", and "europe.weather". - -final = "That's all, folks!" - -# We'll use the same routing key for all messages in the loop, and -# also for the terminating message. - -# usa.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.news" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "usa.news" -session.message_transfer(destination="amq.topic", content=message) - -# usa.weather - - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.weather" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "usa.weather" -session.message_transfer(destination="amq.topic", content=message) - -# europe.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.news" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "europe.news" -session.message_transfer(destination="amq.topic", content=message) - - -# europe.weather - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.weather" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "europe.weather" -session.message_transfer(destination="amq.topic", content=message) - - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py new file mode 100644 index 0000000000..c3b13cd82c --- /dev/null +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +""" + topic_producer.py + + This is a simple AMQP publisher application that uses a + Topic exchange. The publisher specifies the routing key + and the exchange for each message. +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login. + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. Use the +# topic exchange. The routing keys are "usa.news", "usa.weather", +# "europe.news", and "europe.weather". + +final = "That's all, folks!" + +# We'll use the same routing key for all messages in the loop, and +# also for the terminating message. + +# usa.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.news" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "usa.news" +session.message_transfer(destination="amq.topic", content=message) + +# usa.weather + + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.weather" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "usa.weather" +session.message_transfer(destination="amq.topic", content=message) + +# europe.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.news" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "europe.news" +session.message_transfer(destination="amq.topic", content=message) + + +# europe.weather + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.weather" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "europe.weather" +session.message_transfer(destination="amq.topic", content=message) + + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() -- cgit v1.2.1 From 8896a1a5ce01157036cc3096fdb33e8be50d4a02 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 5 Dec 2007 18:53:36 +0000 Subject: Reversed renaming git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@601467 13f79535-47bb-0310-9956-ffa450edef68 --- .../examples/direct/config_direct_exchange.py | 53 +++++++++ .../python/examples/direct/direct_config_queues.py | 53 --------- qpid/python/examples/direct/direct_consumer.py | 75 +++++++++++++ qpid/python/examples/direct/direct_producer.py | 51 +++++++++ qpid/python/examples/direct/direct_publisher.py | 51 --------- qpid/python/examples/direct/listener.py | 75 ------------- .../examples/fanout/config_fanout_exchange.py | 54 ++++++++++ .../python/examples/fanout/fanout_config_queues.py | 54 ---------- qpid/python/examples/fanout/fanout_consumer.py | 75 +++++++++++++ qpid/python/examples/fanout/fanout_producer.py | 48 +++++++++ qpid/python/examples/fanout/fanout_publisher.py | 48 --------- qpid/python/examples/fanout/listener.py | 75 ------------- qpid/python/examples/pubsub/topic_consumer.py | 118 +++++++++++++++++++++ qpid/python/examples/pubsub/topic_listener.py | 118 --------------------- qpid/python/examples/pubsub/topic_producer.py | 96 +++++++++++++++++ qpid/python/examples/pubsub/topic_publisher.py | 96 ----------------- 16 files changed, 570 insertions(+), 570 deletions(-) create mode 100644 qpid/python/examples/direct/config_direct_exchange.py delete mode 100644 qpid/python/examples/direct/direct_config_queues.py create mode 100644 qpid/python/examples/direct/direct_consumer.py create mode 100644 qpid/python/examples/direct/direct_producer.py delete mode 100644 qpid/python/examples/direct/direct_publisher.py delete mode 100644 qpid/python/examples/direct/listener.py create mode 100644 qpid/python/examples/fanout/config_fanout_exchange.py delete mode 100644 qpid/python/examples/fanout/fanout_config_queues.py create mode 100644 qpid/python/examples/fanout/fanout_consumer.py create mode 100644 qpid/python/examples/fanout/fanout_producer.py delete mode 100644 qpid/python/examples/fanout/fanout_publisher.py delete mode 100644 qpid/python/examples/fanout/listener.py create mode 100644 qpid/python/examples/pubsub/topic_consumer.py delete mode 100644 qpid/python/examples/pubsub/topic_listener.py create mode 100644 qpid/python/examples/pubsub/topic_producer.py delete mode 100644 qpid/python/examples/pubsub/topic_publisher.py (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/config_direct_exchange.py b/qpid/python/examples/direct/config_direct_exchange.py new file mode 100644 index 0000000000..e64ad678b8 --- /dev/null +++ b/qpid/python/examples/direct/config_direct_exchange.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.direct" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") + +#----- Cleanup --------------------------------------------- + +session.session_close() + diff --git a/qpid/python/examples/direct/direct_config_queues.py b/qpid/python/examples/direct/direct_config_queues.py deleted file mode 100644 index e64ad678b8..0000000000 --- a/qpid/python/examples/direct/direct_config_queues.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -""" - config_direct_exchange.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.direct" exchange. -# -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") - -#----- Cleanup --------------------------------------------- - -session.session_close() - diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py new file mode 100644 index 0000000000..38b1ba30a0 --- /dev/null +++ b/qpid/python/examples/direct/direct_consumer.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? + +# Initialize 'final' and 'content', variables used to identify the last message. + +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read + +message = None +while content != final: + message = queue.get(timeout=10) + content = message.content.body + print content + +# Messages are not removed from the queue until they are +# acknowledged. Using cumulative=True, all messages from the session +# up to and including the one identified by the delivery tag are +# acknowledged. This is more efficient, because there are fewer +# network round-trips. + +message.complete(cumulative=True) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py new file mode 100644 index 0000000000..6770e56803 --- /dev/null +++ b/qpid/python/examples/direct/direct_producer.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" + direct_producer.py + + Publishes messages to an AMQP direct exchange, using + the routing key "routing_key" +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +for i in range(10): + message = Content("message " + str(i)) + message["routing_key"] = "routing_key" + session.message_transfer(destination="amq.direct", content=message) + +final="That's all, folks!" +message = Content(final) +message["routing_key"] = "routing_key" +session.message_transfer(destination="amq.direct", content=message) + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() + diff --git a/qpid/python/examples/direct/direct_publisher.py b/qpid/python/examples/direct/direct_publisher.py deleted file mode 100644 index 6770e56803..0000000000 --- a/qpid/python/examples/direct/direct_publisher.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -""" - direct_producer.py - - Publishes messages to an AMQP direct exchange, using - the routing key "routing_key" -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. - -for i in range(10): - message = Content("message " + str(i)) - message["routing_key"] = "routing_key" - session.message_transfer(destination="amq.direct", content=message) - -final="That's all, folks!" -message = Content(final) -message["routing_key"] = "routing_key" -session.message_transfer(destination="amq.direct", content=message) - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() - diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py deleted file mode 100644 index 38b1ba30a0..0000000000 --- a/qpid/python/examples/direct/listener.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -""" - direct_consumer.py - - This AMQP client reads messages from a message - queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Read from queue -------------------------------------------- - -# Now let's create a local client queue and tell it to read -# incoming messages. - -# The consumer tag identifies the client-side queue. - -consumer_tag = "consumer1" -queue = client.queue(consumer_tag) - -# Call message_consume() to tell the broker to deliver messages -# from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. - -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? - -# Initialize 'final' and 'content', variables used to identify the last message. - -final = "That's all, folks!" # In a message body, signals the last message -content = "" # Content of the last message read - -message = None -while content != final: - message = queue.get(timeout=10) - content = message.content.body - print content - -# Messages are not removed from the queue until they are -# acknowledged. Using cumulative=True, all messages from the session -# up to and including the one identified by the delivery tag are -# acknowledged. This is more efficient, because there are fewer -# network round-trips. - -message.complete(cumulative=True) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. -# - -session.session_close() diff --git a/qpid/python/examples/fanout/config_fanout_exchange.py b/qpid/python/examples/fanout/config_fanout_exchange.py new file mode 100644 index 0000000000..3315f5bc14 --- /dev/null +++ b/qpid/python/examples/fanout/config_fanout_exchange.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.fanout" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.fanout", queue="message_queue") + +#----- Cleanup --------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/fanout/fanout_config_queues.py b/qpid/python/examples/fanout/fanout_config_queues.py deleted file mode 100644 index 3315f5bc14..0000000000 --- a/qpid/python/examples/fanout/fanout_config_queues.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -""" - config_direct_exchange.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.fanout" exchange. -# -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.fanout", queue="message_queue") - -#----- Cleanup --------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py new file mode 100644 index 0000000000..e9a2291f4c --- /dev/null +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + +# Initialize 'final' and 'content', variables used to identify the last message. + +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read + +message = None +while content != final: + message = queue.get(timeout=10) + content = message.content.body + print content + +# Messages are not removed from the queue until they are +# acknowledged. Using cumulative=True, all messages from the session +# up to and including the one identified by the delivery tag are +# acknowledged. This is more efficient, because there are fewer +# network round-trips. + +message.complete(cumulative=True) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py new file mode 100644 index 0000000000..92ca7b7ec0 --- /dev/null +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +""" + direct_producer.py + + Publishes messages to an AMQP direct exchange, using + the routing key "routing_key" +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +for i in range(10): + message = Content(body="message " + str(i)) + session.message_transfer(destination="amq.fanout", content=message) + +final="That's all, folks!" +message=Content(final) +session.message_transfer(destination="amq.fanout", content=message) + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/fanout/fanout_publisher.py b/qpid/python/examples/fanout/fanout_publisher.py deleted file mode 100644 index 92ca7b7ec0..0000000000 --- a/qpid/python/examples/fanout/fanout_publisher.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -""" - direct_producer.py - - Publishes messages to an AMQP direct exchange, using - the routing key "routing_key" -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. - -for i in range(10): - message = Content(body="message " + str(i)) - session.message_transfer(destination="amq.fanout", content=message) - -final="That's all, folks!" -message=Content(final) -session.message_transfer(destination="amq.fanout", content=message) - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py deleted file mode 100644 index e9a2291f4c..0000000000 --- a/qpid/python/examples/fanout/listener.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -""" - direct_consumer.py - - This AMQP client reads messages from a message - queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Read from queue -------------------------------------------- - -# Now let's create a local client queue and tell it to read -# incoming messages. - -# The consumer tag identifies the client-side queue. - -consumer_tag = "consumer1" -queue = client.queue(consumer_tag) - -# Call message_consume() to tell the broker to deliver messages -# from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. - -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - -# Initialize 'final' and 'content', variables used to identify the last message. - -final = "That's all, folks!" # In a message body, signals the last message -content = "" # Content of the last message read - -message = None -while content != final: - message = queue.get(timeout=10) - content = message.content.body - print content - -# Messages are not removed from the queue until they are -# acknowledged. Using cumulative=True, all messages from the session -# up to and including the one identified by the delivery tag are -# acknowledged. This is more efficient, because there are fewer -# network round-trips. - -message.complete(cumulative=True) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. -# - -session.session_close() diff --git a/qpid/python/examples/pubsub/topic_consumer.py b/qpid/python/examples/pubsub/topic_consumer.py new file mode 100644 index 0000000000..afe8bba91e --- /dev/null +++ b/qpid/python/examples/pubsub/topic_consumer.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +""" + topic_consumer.py + + This AMQP client reads all messages from the + "news", "weather", "usa", and "europe" queues + created and bound by config_topic_exchange.py. +""" + +import base64 + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Functions ------------------------------------------- + +def dump_queue(client, queue_name): + + print "Messages queue: " + queue_name + + consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag + queue = client.queue(consumer_tag) + + # Call basic_consume() to tell the broker to deliver messages + # from the AMQP queue to a local client queue. The broker will + # start delivering messages as soon as basic_consume() is called. + + session.message_subscribe(queue=queue_name, destination=consumer_tag) + session.message_flow(consumer_tag, 0, 0xFFFFFFFF) + session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + + content = "" # Content of the last message read + final = "That's all, folks!" # In a message body, signals the last message + message = 0 + + while content != final: + try: + message = queue.get() + content = message.content.body + print content + except Empty: + if message != 0: + message.complete(cumulative=True) + print "No more messages!" + return + + + # Messages are not removed from the queue until they + # are acknowledged. Using multiple=True, all messages + # in the channel up to and including the one identified + # by the delivery tag are acknowledged. This is more efficient, + # because there are fewer network round-trips. + + if message != 0: + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session_info = session.session_open() +session_id = session_info.session_id + +#----- Main Body -- ---------------------------------------- + + +news = "news" + base64.urlsafe_b64encode(session_id) +weather = "weather" + base64.urlsafe_b64encode(session_id) +usa = "usa" + base64.urlsafe_b64encode(session_id) +europe = "europe" + base64.urlsafe_b64encode(session_id) + +session.queue_declare(queue=news, exclusive=True) +session.queue_declare(queue=weather, exclusive=True) +session.queue_declare(queue=usa, exclusive=True) +session.queue_declare(queue=europe, exclusive=True) + +# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". + +# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches +# "europe.news" or "usa.news". + +session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") +session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") +session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") +session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") + +# Remind the user to start the topic producer + +print "Queues create - please start the topic producer" + +# Call dump_queue to print messages from each queue + +dump_queue(client, news) +dump_queue(client, weather) +dump_queue(client, usa) +dump_queue(client, europe) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/pubsub/topic_listener.py b/qpid/python/examples/pubsub/topic_listener.py deleted file mode 100644 index afe8bba91e..0000000000 --- a/qpid/python/examples/pubsub/topic_listener.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python -""" - topic_consumer.py - - This AMQP client reads all messages from the - "news", "weather", "usa", and "europe" queues - created and bound by config_topic_exchange.py. -""" - -import base64 - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Functions ------------------------------------------- - -def dump_queue(client, queue_name): - - print "Messages queue: " + queue_name - - consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = client.queue(consumer_tag) - - # Call basic_consume() to tell the broker to deliver messages - # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as basic_consume() is called. - - session.message_subscribe(queue=queue_name, destination=consumer_tag) - session.message_flow(consumer_tag, 0, 0xFFFFFFFF) - session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - - content = "" # Content of the last message read - final = "That's all, folks!" # In a message body, signals the last message - message = 0 - - while content != final: - try: - message = queue.get() - content = message.content.body - print content - except Empty: - if message != 0: - message.complete(cumulative=True) - print "No more messages!" - return - - - # Messages are not removed from the queue until they - # are acknowledged. Using multiple=True, all messages - # in the channel up to and including the one identified - # by the delivery tag are acknowledged. This is more efficient, - # because there are fewer network round-trips. - - if message != 0: - message.complete(cumulative=True) - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session_info = session.session_open() -session_id = session_info.session_id - -#----- Main Body -- ---------------------------------------- - - -news = "news" + base64.urlsafe_b64encode(session_id) -weather = "weather" + base64.urlsafe_b64encode(session_id) -usa = "usa" + base64.urlsafe_b64encode(session_id) -europe = "europe" + base64.urlsafe_b64encode(session_id) - -session.queue_declare(queue=news, exclusive=True) -session.queue_declare(queue=weather, exclusive=True) -session.queue_declare(queue=usa, exclusive=True) -session.queue_declare(queue=europe, exclusive=True) - -# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". - -# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches -# "europe.news" or "usa.news". - -session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") -session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") -session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") -session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") - -# Remind the user to start the topic producer - -print "Queues create - please start the topic producer" - -# Call dump_queue to print messages from each queue - -dump_queue(client, news) -dump_queue(client, weather) -dump_queue(client, usa) -dump_queue(client, europe) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/pubsub/topic_producer.py b/qpid/python/examples/pubsub/topic_producer.py new file mode 100644 index 0000000000..c3b13cd82c --- /dev/null +++ b/qpid/python/examples/pubsub/topic_producer.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +""" + topic_producer.py + + This is a simple AMQP publisher application that uses a + Topic exchange. The publisher specifies the routing key + and the exchange for each message. +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login. + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. Use the +# topic exchange. The routing keys are "usa.news", "usa.weather", +# "europe.news", and "europe.weather". + +final = "That's all, folks!" + +# We'll use the same routing key for all messages in the loop, and +# also for the terminating message. + +# usa.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.news" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "usa.news" +session.message_transfer(destination="amq.topic", content=message) + +# usa.weather + + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.weather" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "usa.weather" +session.message_transfer(destination="amq.topic", content=message) + +# europe.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.news" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "europe.news" +session.message_transfer(destination="amq.topic", content=message) + + +# europe.weather + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.weather" + session.message_transfer(destination="amq.topic", content=message) + +message = Content(final) +message["routing_key"] = "europe.weather" +session.message_transfer(destination="amq.topic", content=message) + + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py deleted file mode 100644 index c3b13cd82c..0000000000 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -""" - topic_producer.py - - This is a simple AMQP publisher application that uses a - Topic exchange. The publisher specifies the routing key - and the exchange for each message. -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login. - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. Use the -# topic exchange. The routing keys are "usa.news", "usa.weather", -# "europe.news", and "europe.weather". - -final = "That's all, folks!" - -# We'll use the same routing key for all messages in the loop, and -# also for the terminating message. - -# usa.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.news" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "usa.news" -session.message_transfer(destination="amq.topic", content=message) - -# usa.weather - - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.weather" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "usa.weather" -session.message_transfer(destination="amq.topic", content=message) - -# europe.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.news" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "europe.news" -session.message_transfer(destination="amq.topic", content=message) - - -# europe.weather - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.weather" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "europe.weather" -session.message_transfer(destination="amq.topic", content=message) - - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() -- cgit v1.2.1 From 340eccfc41a61b5cea01184f0da40b2f78c20a3f Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 10 Dec 2007 20:22:23 +0000 Subject: Patches from Ted Ross QPID-697 Fixed access-rights constants for management schema. Added mutex to fix problems associated with concurrent invocation of accessors for queue statistics. Removed queue schema content that is not relevant to QPID. QPID-698 This patch creates a new subdirectory in python called "mgmt-cli". python/mgmt-cli/main.py can be executed from the shell. If no arguments are supplied, it attempts to connect to the broker at localhost:5672. The first argument is the hostname for the target broker and the second (optional) argument is the TCP port (defaults to 5672). It is assumed that the AMQP spec file is in the following location: /usr/share/amqp/amqp.0-10-preview.xml It is also required that the qpid/python directory be in the PYTHONPATH environment variable. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@603034 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/disp.py | 77 ++++++ qpid/python/mgmt-cli/main.py | 164 ++++++++++++ qpid/python/mgmt-cli/managementdata.py | 417 ++++++++++++++++++++++++++++++ qpid/python/qpid/management.py | 239 ++++++++++++----- qpid/python/qpid/management.py.rej | 457 +++++++++++++++++++++++++++++++++ 5 files changed, 1294 insertions(+), 60 deletions(-) create mode 100644 qpid/python/mgmt-cli/disp.py create mode 100644 qpid/python/mgmt-cli/main.py create mode 100644 qpid/python/mgmt-cli/managementdata.py create mode 100644 qpid/python/qpid/management.py.rej (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/disp.py b/qpid/python/mgmt-cli/disp.py new file mode 100644 index 0000000000..5746a26e51 --- /dev/null +++ b/qpid/python/mgmt-cli/disp.py @@ -0,0 +1,77 @@ +#!/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. +# + +from time import strftime, gmtime + +class Display: + """ Display formatting for QPID Management CLI """ + + def __init__ (self): + self.tableSpacing = 2 + self.tablePrefix = " " + self.timestampFormat = "%X" + + def table (self, title, heads, rows): + """ Print a formatted table with autosized columns """ + print title + if len (rows) == 0: + return + colWidth = [] + col = 0 + line = self.tablePrefix + for head in heads: + width = len (head) + for row in rows: + cellWidth = len (str (row[col])) + if cellWidth > width: + width = cellWidth + colWidth.append (width + self.tableSpacing) + line = line + head + for i in range (colWidth[col] - len (head)): + line = line + " " + col = col + 1 + print line + line = self.tablePrefix + for width in colWidth: + for i in range (width): + line = line + "=" + print line + + for row in rows: + line = self.tablePrefix + col = 0 + for width in colWidth: + line = line + str (row[col]) + for i in range (width - len (str (row[col]))): + line = line + " " + col = col + 1 + print line + + def do_setTimeFormat (self, fmt): + """ Select timestamp format """ + if fmt == "long": + self.timestampFormat = "%c" + elif fmt == "short": + self.timestampFormat = "%X" + + def timestamp (self, nsec): + """ Format a nanosecond-since-the-epoch timestamp for printing """ + return strftime (self.timestampFormat, gmtime (nsec / 1000000000)) diff --git a/qpid/python/mgmt-cli/main.py b/qpid/python/mgmt-cli/main.py new file mode 100644 index 0000000000..2990e25437 --- /dev/null +++ b/qpid/python/mgmt-cli/main.py @@ -0,0 +1,164 @@ +#!/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 os +import getopt +import sys +import socket +from cmd import Cmd +from managementdata import ManagementData +from shlex import split +from disp import Display +from qpid.peer import Closed + +class Mcli (Cmd): + """ Management Command Interpreter """ + prompt = "qpid: " + + def __init__ (self, dataObject, dispObject): + Cmd.__init__ (self) + self.dataObject = dataObject + self.dispObject = dispObject + + def emptyline (self): + pass + + def do_help (self, data): + print "Management Tool for QPID" + print + print "Commands:" + print " list - Print summary of existing objects by class" + print " list - Print list of objects of the specified class" + print " list all - Print contents of all objects of specified class" + print " list - Print contents of one or more objects" + print " list is space-separated, ranges may be specified (i.e. 1004-1010)" + print " call [] - Invoke a method on an object" + print " schema - Print summary of object classes seen on the target" + print " schema [className] - Print details of an object class" + print " set time-format short - Select short timestamp format (default)" + print " set time-format long - Select long timestamp format" + print " quit or ^D - Exit the program" + print + + def complete_set (self, text, line, begidx, endidx): + """ Command completion for the 'set' command """ + tokens = split (line) + if len (tokens) < 2: + return ["time-format "] + elif tokens[1] == "time-format": + if len (tokens) == 2: + return ["long", "short"] + elif len (tokens) == 3: + if "long".find (text) == 0: + return ["long"] + elif "short".find (text) == 0: + return ["short"] + elif "time-format".find (text) == 0: + return ["time-format "] + return [] + + def do_set (self, data): + tokens = split (data) + try: + if tokens[0] == "time-format": + self.dispObject.do_setTimeFormat (tokens[1]) + except: + pass + + def complete_schema (self, text, line, begidx, endidx): + tokens = split (line) + if len (tokens) > 2: + return [] + return self.dataObject.classCompletions (text) + + def do_schema (self, data): + self.dataObject.do_schema (data) + + def complete_list (self, text, line, begidx, endidx): + tokens = split (line) + if len (tokens) > 2: + return [] + return self.dataObject.classCompletions (text) + + def do_list (self, data): + self.dataObject.do_list (data) + + def do_call (self, data): + self.dataObject.do_call (data) + + def do_EOF (self, data): + print "quit" + return True + + def do_quit (self, data): + return True + + def postcmd (self, stop, line): + return stop + + def postloop (self): + print "Exiting..." + self.dataObject.close () + +def Usage (): + print sys.argv[0], "[ []]" + print + sys.exit (1) + +#========================================================= +# Main Program +#========================================================= + +# Get host name and port if specified on the command line +try: + (optlist, cargs) = getopt.getopt (sys.argv[1:], 's:') +except: + Usage () + +specpath = "/usr/share/amqp/amqp.0-10-preview.xml" +host = "localhost" +port = 5672 + +if "s" in optlist: + specpath = optlist["s"] + +if len (cargs) > 0: + host = cargs[0] + +if len (cargs) > 1: + port = int (cargs[1]) + +print ("Management Tool for QPID") +disp = Display () + +# Attempt to make a connection to the target broker +try: + data = ManagementData (disp, host, port, spec=specpath) +except socket.error, e: + sys.exit (0) +except Closed, e: + if str(e).find ("Exchange not found") != -1: + print "Management not enabled on broker: Use '-m yes' option on broker startup." + sys.exit (0) + +# Instantiate the CLI interpreter and launch it. +cli = Mcli (data, disp) +cli.cmdloop () diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py new file mode 100644 index 0000000000..b770677825 --- /dev/null +++ b/qpid/python/mgmt-cli/managementdata.py @@ -0,0 +1,417 @@ +#!/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. +# + +from qpid.management import ManagedBroker +from threading import Lock +from disp import Display +from shlex import split + +class ManagementData: + + # + # Data Structure: + # + # Please note that this data structure holds only the most recent + # configuration and instrumentation data for each object. It does + # not hold the detailed historical data that is sent from the broker. + # The only historical data it keeps are the high and low watermarks + # for hi-lo statistics. + # + # tables :== {} + # {} + # (timestamp, config-record, inst-record) + # timestamp :== (, , ) + # config-record :== [element] + # inst-record :== [element] + # element :== (, ) + # + + def dataHandler (self, context, className, list, timestamps): + """ Callback for configuration and instrumentation data updates """ + self.lock.acquire () + try: + # If this class has not been seen before, create an empty dictionary to + # hold objects of this class + if className not in self.tables: + self.tables[className] = {} + + # Calculate a base-id so displayed IDs are reasonable 4-digit numbers + id = long (list[0][1]) + if self.baseId == 0: + self.baseId = id - 1000 + + # If this object hasn't been seen before, create a new object record with + # the timestamps and empty lists for configuration and instrumentation data. + if id not in self.tables[className]: + self.tables[className][id] = (timestamps, [], []) + + (unused, oldConf, oldInst) = self.tables[className][id] + + # For config updates, simply replace old config list with the new one. + if context == 0: #config + self.tables[className][id] = (timestamps, list, oldInst) + + # For instrumentation updates, carry the minimum and maximum values for + # "hi-lo" stats forward. + elif context == 1: #inst + if len (oldInst) == 0: + newInst = list + else: + newInst = [] + for idx in range (len (list)): + (key, value) = list[idx] + if key.find ("High") == len (key) - 4: + if oldInst[idx][1] > value: + value = oldInst[idx][1] + if key.find ("Low") == len (key) - 3: + if oldInst[idx][1] < value: + value = oldInst[idx][1] + newInst.append ((key, value)) + self.tables[className][id] = (timestamps, oldConf, newInst) + + finally: + self.lock.release () + + def methodReply (self, broker, methodId, status, sText, args): + """ Callback for method-reply messages """ + pass + + def schemaHandler (self, context, className, configs, insts, methods, events): + """ Callback for schema updates """ + if className not in self.schema: + self.schema[className] = (configs, insts, methods, events) + + def __init__ (self, disp, host, port=5672, username="guest", password="guest", + spec="../../specs/amqp.0-10-preview.xml"): + self.broker = ManagedBroker (host, port, username, password, spec) + self.broker.configListener (0, self.dataHandler) + self.broker.instrumentationListener (1, self.dataHandler) + self.broker.methodListener (None, self.methodReply) + self.broker.schemaListener (None, self.schemaHandler) + self.lock = Lock () + self.tables = {} + self.schema = {} + self.baseId = 0 + self.disp = disp + self.broker.start () + + def close (self): + self.broker.stop () + + def getObjIndex (self, className, config): + """ Concatenate the values from index columns to form a unique object name """ + result = "" + schemaConfig = self.schema[className][0] + for item in schemaConfig: + if item[5] == 1 and item[0] != "id": + if result != "": + result = result + "." + for key,val in config: + if key == item[0]: + if key.find ("Ref") != -1: + val = val - self.baseId + result = result + str (val) + return result + + def classCompletions (self, prefix): + """ Provide a list of candidate class names for command completion """ + self.lock.acquire () + complist = [] + try: + for name in self.tables: + if name.find (prefix) == 0: + complist.append (name) + finally: + self.lock.release () + return complist + + def typeName (self, typecode): + """ Convert type-codes to printable strings """ + if typecode == 1: + return "uint8" + elif typecode == 2: + return "uint16" + elif typecode == 3: + return "uint32" + elif typecode == 4: + return "uint64" + elif typecode == 5: + return "bool" + elif typecode == 6: + return "short-string" + elif typecode == 7: + return "long-string" + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def accessName (self, code): + """ Convert element access codes to printable strings """ + if code == 1: + return "ReadCreate" + elif code == 2: + return "ReadWrite" + elif code == 3: + return "ReadOnly" + else: + raise ValueErrir ("Invalid access code: %d" %code) + + def notNone (self, text): + if text == None: + return "" + else: + return text + + def listOfIds (self, className, tokens): + """ Generate a tuple of object ids for a classname based on command tokens. """ + list = [] + if tokens[0] == "all": + for id in self.tables[className]: + list.append (id - self.baseId) + + else: + for token in tokens: + if token.find ("-") != -1: + ids = token.split("-", 2) + for id in range (int (ids[0]), int (ids[1]) + 1): + if self.getClassForId (long (id) + self.baseId) == className: + list.append (id) + else: + list.append (token) + + list.sort () + result = () + for item in list: + result = result + (item,) + return result + + def listClasses (self): + """ Generate a display of the list of classes """ + self.lock.acquire () + try: + rows = [] + sorted = self.tables.keys () + sorted.sort () + for name in sorted: + active = 0 + deleted = 0 + for record in self.tables[name]: + isdel = False + ts = self.tables[name][record][0] + if ts[2] > 0: + isdel = True + if isdel: + deleted = deleted + 1 + else: + active = active + 1 + rows.append ((name, active, deleted)) + self.disp.table ("Management Object Types:", + ("ObjectType", "Active", "Deleted"), rows) + finally: + self.lock.release () + + def listObjects (self, className): + """ Generate a display of a list of objects in a class """ + self.lock.acquire () + try: + if className not in self.tables: + print ("Object type %s not known" % className) + else: + rows = [] + sorted = self.tables[className].keys () + sorted.sort () + for objId in sorted: + (ts, config, inst) = self.tables[className][objId] + createTime = self.disp.timestamp (ts[1]) + destroyTime = "-" + if ts[2] > 0: + destroyTime = self.disp.timestamp (ts[2]) + objIndex = self.getObjIndex (className, config) + row = (objId - self.baseId, createTime, destroyTime, objIndex) + rows.append (row) + self.disp.table ("Objects of type %s" % className, + ("ID", "Created", "Destroyed", "Index"), + rows) + finally: + self.lock.release () + + def showObjects (self, tokens): + """ Generate a display of object data for a particular class """ + self.lock.acquire () + try: + className = tokens[0] + if className not in self.tables: + print "Class not known: %s" % className + raise ValueError () + + userIds = self.listOfIds (className, tokens[1:]) + if len (userIds) == 0: + print "No object IDs supplied" + raise ValueError () + + ids = [] + for id in userIds: + if self.getClassForId (long (id) + self.baseId) == className: + ids.append (long (id) + self.baseId) + + rows = [] + config = self.tables[className][ids[0]][1] + for eIdx in range (len (config)): + key = config[eIdx][0] + if key != "id": + isRef = key.find ("Ref") == len (key) - 3 + row = ("config", key) + for id in ids: + value = self.tables[className][id][1][eIdx][1] + if isRef: + value = value - self.baseId + row = row + (value,) + rows.append (row) + + inst = self.tables[className][ids[0]][2] + for eIdx in range (len (inst)): + key = inst[eIdx][0] + if key != "id": + isRef = key.find ("Ref") == len (key) - 3 + row = ("inst", key) + for id in ids: + value = self.tables[className][id][2][eIdx][1] + if isRef: + value = value - self.baseId + row = row + (value,) + rows.append (row) + + titleRow = ("Type", "Element") + for id in ids: + titleRow = titleRow + (str (id - self.baseId),) + self.disp.table ("Object of type %s:" % className, titleRow, rows) + + except: + pass + self.lock.release () + + def schemaSummary (self): + """ Generate a display of the list of classes in the schema """ + self.lock.acquire () + try: + rows = [] + sorted = self.schema.keys () + sorted.sort () + for className in sorted: + tuple = self.schema[className] + row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) + rows.append (row) + self.disp.table ("Classes in Schema:", + ("Class", "ConfigElements", "InstElements", "Methods", "Events"), + rows) + finally: + self.lock.release () + + def schemaTable (self, className): + """ Generate a display of details of the schema of a particular class """ + self.lock.acquire () + try: + if className not in self.schema: + print ("Class name %s not known" % className) + raise ValueError () + + rows = [] + for config in self.schema[className][0]: + name = config[0] + if name != "id": + typename = self.typeName(config[1]) + unit = self.notNone (config[2]) + desc = self.notNone (config[3]) + access = self.accessName (config[4]) + extra = "" + if config[5] == 1: + extra = extra + "index " + if config[6] != None: + extra = extra + "Min: " + str (config[6]) + if config[7] != None: + extra = extra + "Max: " + str (config[7]) + if config[8] != None: + extra = extra + "MaxLen: " + str (config[8]) + rows.append ((name, typename, unit, access, extra, desc)) + + for config in self.schema[className][1]: + name = config[0] + if name != "id": + typename = self.typeName(config[1]) + unit = self.notNone (config[2]) + desc = self.notNone (config[3]) + rows.append ((name, typename, unit, "", "", desc)) + + titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") + self.disp.table ("Schema for class '%s':" % className, titles, rows) + + for method in self.schema[className][2]: + mname = method[0] + mdesc = method[1] + args = method[2] + caption = "\nMethod '%s' %s" % (mname, self.notNone (mdesc)) + rows = [] + for arg in args: + name = arg[0] + typename = self.typeName (arg[1]) + dir = arg[2] + unit = self.notNone (arg[3]) + desc = self.notNone (arg[4]) + extra = "" + if arg[5] != None: + extra = extra + "Min: " + str (arg[5]) + if arg[6] != None: + extra = extra + "Max: " + str (arg[6]) + if arg[7] != None: + extra = extra + "MaxLen: " + str (arg[7]) + if arg[8] != None: + extra = extra + "Default: " + str (arg[8]) + rows.append ((name, typename, dir, unit, extra, desc)) + titles = ("Argument", "Type", "Direction", "Unit", "Notes", "Description") + self.disp.table (caption, titles, rows) + + except: + pass + self.lock.release () + + def getClassForId (self, objId): + """ Given an object ID, return the class name for the referenced object """ + for className in self.tables: + if objId in self.tables[className]: + return className + return None + + def do_list (self, data): + tokens = data.split () + if len (tokens) == 0: + self.listClasses () + elif len (tokens) == 1: + self.listObjects (data) + else: + self.showObjects (tokens) + + def do_schema (self, data): + if data == "": + self.schemaSummary () + else: + self.schemaTable (data) + + def do_call (self, data): + print "Not yet implemented" diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index a5ad997a24..8373ecceb7 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -31,17 +31,74 @@ from qpid.client import Client from qpid.content import Content from cStringIO import StringIO from codec import Codec, EOF +from threading import Lock + + +class SequenceManager: + def __init__ (self): + self.lock = Lock () + self.sequence = 0 + self.pending = {} + + def reserve (self, data): + self.lock.acquire () + result = self.sequence + self.sequence = self.sequence + 1 + self.pending[result] = data + self.lock.release () + return result + + def release (self, seq): + data = None + self.lock.acquire () + if seq in self.pending: + data = self.pending[seq] + del self.pending[seq] + self.lock.release () + return data -#=================================================================== -# ManagementMetadata -# -# One instance of this class is created for each ManagedBroker. It -# is used to store metadata from the broker which is needed for the -# proper interpretation of recevied management content. -# -#=================================================================== class ManagementMetadata: - + """One instance of this class is created for each ManagedBroker. It + is used to store metadata from the broker which is needed for the + proper interpretation of received management content.""" + + def encodeValue (self, codec, value, typecode): + if typecode == 1: + codec.encode_octet (int (value)) + elif typecode == 2: + codec.encode_short (int (value)) + elif typecode == 3: + codec.encode_long (long (value)) + elif typecode == 4: + codec.encode_longlong (long (value)) + elif typecode == 5: + codec.encode_octet (int (value)) + elif typecode == 6: + codec.encode_shortstr (value) + elif typecode == 7: + codec.encode_longstr (value) + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def decodeValue (self, codec, typecode): + if typecode == 1: + data = codec.decode_octet () + elif typecode == 2: + data = codec.decode_short () + elif typecode == 3: + data = codec.decode_long () + elif typecode == 4: + data = codec.decode_longlong () + elif typecode == 5: + data = codec.decode_octet () + elif typecode == 6: + data = codec.decode_shortstr () + elif typecode == 7: + data = codec.decode_longstr () + else: + raise ValueError ("Invalid type code: %d" % typecode) + return data + def parseSchema (self, cls, codec): className = codec.decode_shortstr () configCount = codec.decode_short () @@ -100,12 +157,56 @@ class ManagementMetadata: inst = (name, type, unit, desc) insts.append (inst) - # TODO: Handle notification of schema change outbound + for idx in range (methodCount): + ft = codec.decode_table () + mname = ft["name"] + argCount = ft["argCount"] + if "desc" in ft: + mdesc = ft["desc"] + else: + mdesc = None + + args = [] + for aidx in range (argCount): + ft = codec.decode_table () + name = ft["name"] + type = ft["type"] + dir = ft["dir"].upper () + unit = None + min = None + max = None + maxlen = None + desc = None + default = None + + for key, value in ft.items (): + if key == "unit": + unit = value + elif key == "min": + min = value + elif key == "max": + max = value + elif key == "maxlen": + maxlen = value + elif key == "desc": + desc = value + elif key == "default": + default = value + + arg = (name, type, dir, unit, desc, min, max, maxlen, default) + args.append (arg) + methods.append ((mname, mdesc, args)) + + self.schema[(className,'C')] = configs self.schema[(className,'I')] = insts self.schema[(className,'M')] = methods self.schema[(className,'E')] = events + if self.broker.schema_cb != None: + self.broker.schema_cb[1] (self.broker.schema_cb[0], className, + configs, insts, methods, events) + def parseContent (self, cls, codec): if cls == 'C' and self.broker.config_cb == None: return @@ -127,22 +228,7 @@ class ManagementMetadata: for element in self.schema[(className,cls)][:]: tc = element[1] name = element[0] - if tc == 1: # TODO: Define constants for these - data = codec.decode_octet () - elif tc == 2: - data = codec.decode_short () - elif tc == 3: - data = codec.decode_long () - elif tc == 4: - data = codec.decode_longlong () - elif tc == 5: - data = codec.decode_octet () - elif tc == 6: - data = codec.decode_shortstr () - elif tc == 7: - data = codec.decode_longstr () - else: - raise ValueError ("Invalid type code: %d" % tc) + data = self.decodeValue (codec, tc) row.append ((name, data)) if cls == 'C': @@ -168,14 +254,9 @@ class ManagementMetadata: self.schema = {} -#=================================================================== -# ManagedBroker -# -# An object of this class represents a connection (over AMQP) to a -# single managed broker. -# -#=================================================================== class ManagedBroker: + """An object of this class represents a connection (over AMQP) to a + single managed broker.""" mExchange = "qpid.management" dExchange = "amq.direct" @@ -205,18 +286,35 @@ class ManagedBroker: msg.complete () def reply_cb (self, msg): - codec = Codec (StringIO (msg.content.body), self.spec) - methodId = codec.decode_long () + codec = Codec (StringIO (msg.content.body), self.spec) + sequence = codec.decode_long () status = codec.decode_long () sText = codec.decode_shortstr () - args = {} + data = self.sequenceManager.release (sequence) + if data == None: + msg.complete () + return + + (userSequence, className, methodName) = data + if status == 0: - args["sequence"] = codec.decode_long () - args["body"] = codec.decode_longstr () + ms = self.metadata.schema[(className,'M')] + arglist = None + for (mname, mdesc, margs) in ms: + if mname == methodName: + arglist = margs + if arglist == None: + msg.complete () + return + + args = {} + for arg in arglist: + if arg[2].find("O") != -1: + args[arg[0]] = self.metadata.decodeValue (codec, arg[1]) if self.method_cb != None: - self.method_cb[1] (self.method_cb[0], methodId, status, sText, args) + self.method_cb[1] (self.method_cb[0], userSequence, status, sText, args) msg.complete () @@ -225,17 +323,18 @@ class ManagedBroker: port = 5672, username = "guest", password = "guest", - specfile = "../specs/amqp.0-10-preview.xml"): - - self.spec = qpid.spec.load (specfile) - self.client = None - self.channel = None - self.queue = None - self.rqueue = None - self.qname = None - self.rqname = None - self.metadata = ManagementMetadata (self) - self.connected = 0 + specfile = "/usr/share/amqp/amqp.0-10-preview.xml"): + + self.spec = qpid.spec.load (specfile) + self.client = None + self.channel = None + self.queue = None + self.rqueue = None + self.qname = None + self.rqname = None + self.metadata = ManagementMetadata (self) + self.sequenceManager = SequenceManager () + self.connected = 0 self.lastConnectError = None # Initialize the callback records @@ -265,17 +364,37 @@ class ManagedBroker: def instrumentationListener (self, context, callback): self.inst_cb = (context, callback) - def method (self, methodId, objId, className, + def method (self, userSequence, objId, className, methodName, args=None, packageName="qpid"): codec = Codec (StringIO (), self.spec); - codec.encode_long (methodId) - codec.encode_longlong (objId) - codec.encode_shortstr (self.rqname) - - # TODO: Encode args according to schema - if methodName == "echo": - codec.encode_long (args["sequence"]) - codec.encode_longstr (args["body"]) + sequence = self.sequenceManager.reserve ((userSequence, className, methodName)) + codec.encode_long (sequence) # Method sequence id + codec.encode_longlong (objId) # ID of object + codec.encode_shortstr (self.rqname) # name of reply queue + + # Encode args according to schema + if (className,'M') not in self.metadata.schema: + self.sequenceManager.release (sequence) + raise ValueError ("Unknown class name: %s" % className) + + ms = self.metadata.schema[(className,'M')] + arglist = None + for (mname, mdesc, margs) in ms: + if mname == methodName: + arglist = margs + if arglist == None: + self.sequenceManager.release (sequence) + raise ValueError ("Unknown method name: %s" % methodName) + + for arg in arglist: + if arg[2].find("I") != -1: + value = arg[8] # default + if arg[0] in args: + value = args[arg[0]] + if value == None: + self.sequenceManager.release (sequence) + raise ValueError ("Missing non-defaulted argument: %s" % arg[0]) + self.metadata.encodeValue (codec, value, arg[1]) msg = Content (codec.stream.getvalue ()) msg["content_type"] = "application/octet-stream" @@ -325,7 +444,7 @@ class ManagedBroker: self.connected = 1 except socket.error, e: - print "Socket Error Detected:", e[1] + print "Socket Error:", e[1] self.lastConnectError = e raise except: diff --git a/qpid/python/qpid/management.py.rej b/qpid/python/qpid/management.py.rej new file mode 100644 index 0000000000..28d172abe5 --- /dev/null +++ b/qpid/python/qpid/management.py.rej @@ -0,0 +1,457 @@ +*************** +*** 18,24 **** + # + + """ +- Management classes for AMQP + """ + + import qpid +--- 18,24 ---- + # + + """ ++ Management API for Qpid + """ + + import qpid +*************** +*** 42,91 **** + #=================================================================== + class ManagementMetadata: + +- def parseSchema (self, cls, oid, len, codec): +- #print "Schema Record: objId=", oid + +- config = [] +- inst = [] +- while 1: +- flags = codec.decode_octet () +- if flags == 0x80: +- break + +- tc = codec.decode_octet () +- name = codec.decode_shortstr () +- desc = codec.decode_shortstr () + +- if flags & 1: # TODO: Define constants for these +- config.append ((tc, name, desc)) +- if (flags & 1) == 0 or (flags & 2) == 2: +- inst.append ((tc, name, desc)) + + # TODO: Handle notification of schema change outbound +- self.schema[(oid,'C')] = config +- self.schema[(oid,'I')] = inst + +- def parseContent (self, cls, oid, len, codec): +- #print "Content Record: Class=", cls, ", objId=", oid +- + if cls == 'C' and self.broker.config_cb == None: + return + if cls == 'I' and self.broker.inst_cb == None: + return + +- if (oid,cls) not in self.schema: + return + + row = [] + timestamps = [] + +- timestamps.append (codec.decode_longlong ()); # Current Time +- timestamps.append (codec.decode_longlong ()); # Create Time +- timestamps.append (codec.decode_longlong ()); # Delete Time + +- for element in self.schema[(oid,cls)][:]: +- tc = element[0] +- name = element[1] + if tc == 1: # TODO: Define constants for these + data = codec.decode_octet () + elif tc == 2: +--- 42,132 ---- + #=================================================================== + class ManagementMetadata: + ++ def parseSchema (self, cls, codec): ++ className = codec.decode_shortstr () ++ configCount = codec.decode_short () ++ instCount = codec.decode_short () ++ methodCount = codec.decode_short () ++ eventCount = codec.decode_short () + ++ configs = [] ++ insts = [] ++ methods = [] ++ events = [] + ++ configs.append (("id", 4, "", "", 1, 1, None, None, None, None, None)) ++ insts.append (("id", 4, None, None)) + ++ for idx in range (configCount): ++ ft = codec.decode_table () ++ name = ft["name"] ++ type = ft["type"] ++ access = ft["access"] ++ index = ft["index"] ++ unit = None ++ min = None ++ max = None ++ maxlen = None ++ desc = None + ++ for key, value in ft.items (): ++ if key == "unit": ++ unit = value ++ elif key == "min": ++ min = value ++ elif key == "max": ++ max = value ++ elif key == "maxlen": ++ maxlen = value ++ elif key == "desc": ++ desc = value ++ ++ config = (name, type, unit, desc, access, index, min, max, maxlen) ++ configs.append (config) ++ ++ for idx in range (instCount): ++ ft = codec.decode_table () ++ name = ft["name"] ++ type = ft["type"] ++ unit = None ++ desc = None ++ ++ for key, value in ft.items (): ++ if key == "unit": ++ unit = value ++ elif key == "desc": ++ desc = value ++ ++ inst = (name, type, unit, desc) ++ insts.append (inst) ++ + # TODO: Handle notification of schema change outbound ++ self.schema[(className,'C')] = configs ++ self.schema[(className,'I')] = insts ++ self.schema[(className,'M')] = methods ++ self.schema[(className,'E')] = events + ++ def parseContent (self, cls, codec): + if cls == 'C' and self.broker.config_cb == None: + return + if cls == 'I' and self.broker.inst_cb == None: + return + ++ className = codec.decode_shortstr () ++ ++ if (className,cls) not in self.schema: + return + + row = [] + timestamps = [] + ++ timestamps.append (codec.decode_longlong ()) # Current Time ++ timestamps.append (codec.decode_longlong ()) # Create Time ++ timestamps.append (codec.decode_longlong ()) # Delete Time + ++ for element in self.schema[(className,cls)][:]: ++ tc = element[1] ++ name = element[0] + if tc == 1: # TODO: Define constants for these + data = codec.decode_octet () + elif tc == 2: +*************** +*** 98,130 **** + data = codec.decode_octet () + elif tc == 6: + data = codec.decode_shortstr () + row.append ((name, data)) + + if cls == 'C': +- self.broker.config_cb[1] (self.broker.config_cb[0], oid, row, timestamps) +- if cls == 'I': +- self.broker.inst_cb[1] (self.broker.inst_cb[0], oid, row, timestamps) + + def parse (self, codec): +- try: +- opcode = chr (codec.decode_octet ()) +- except EOF: +- return 0 + +- cls = chr (codec.decode_octet ()) +- oid = codec.decode_short () +- len = codec.decode_long () +- +- if len < 8: +- raise ValueError ("parse error: value of length field too small") +- + if opcode == 'S': +- self.parseSchema (cls, oid, len, codec) + +- if opcode == 'C': +- self.parseContent (cls, oid, len, codec) + +- return 1 + + def __init__ (self, broker): + self.broker = broker +--- 139,167 ---- + data = codec.decode_octet () + elif tc == 6: + data = codec.decode_shortstr () ++ elif tc == 7: ++ data = codec.decode_longstr () ++ else: ++ raise ValueError ("Invalid type code: %d" % tc) + row.append ((name, data)) + + if cls == 'C': ++ self.broker.config_cb[1] (self.broker.config_cb[0], className, row, timestamps) ++ elif cls == 'I': ++ self.broker.inst_cb[1] (self.broker.inst_cb[0], className, row, timestamps) + + def parse (self, codec): ++ opcode = chr (codec.decode_octet ()) ++ cls = chr (codec.decode_octet ()) + + if opcode == 'S': ++ self.parseSchema (cls, codec) + ++ elif opcode == 'C': ++ self.parseContent (cls, codec) + ++ else: ++ raise ValueError ("Unknown opcode: %c" % opcode); + + def __init__ (self, broker): + self.broker = broker +*************** +*** 140,146 **** + #=================================================================== + class ManagedBroker: + +- exchange = "qpid.management" + + def checkHeader (self, codec): + octet = chr (codec.decode_octet ()) +--- 177,184 ---- + #=================================================================== + class ManagedBroker: + ++ mExchange = "qpid.management" ++ dExchange = "amq.direct" + + def checkHeader (self, codec): + octet = chr (codec.decode_octet ()) +*************** +*** 157,225 **** + return 0 + return 1 + +- def receive_cb (self, msg): + codec = Codec (StringIO (msg.content.body), self.spec) + + if self.checkHeader (codec) == 0: + raise ValueError ("outer header invalid"); + +- while self.metadata.parse (codec): +- pass + + msg.complete () + +- def __init__ (self, host = "localhost", port = 5672, +- username = "guest", password = "guest"): + +- self.spec = qpid.spec.load ("../specs/amqp.0-10-preview.xml") +- self.client = None +- self.channel = None +- self.queue = None +- self.qname = None +- self.metadata = ManagementMetadata (self) + + # Initialize the callback records + self.schema_cb = None + self.config_cb = None + self.inst_cb = None + + self.host = host + self.port = port + self.username = username + self.password = password + + def schemaListener (self, context, callback): + self.schema_cb = (context, callback) + + def configListener (self, context, callback): + self.config_cb = (context, callback) + + def instrumentationListener (self, context, callback): + self.inst_cb = (context, callback) + + def start (self): +- print "Connecting to broker", self.host + + try: + self.client = Client (self.host, self.port, self.spec) + self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) + self.channel = self.client.channel (1) +- response = self.channel.session_open (detached_lifetime=300) +- self.qname = "mgmt-" + base64.urlsafe_b64encode(response.session_id) + +- self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) +- self.channel.queue_bind (exchange=ManagedBroker.exchange, queue=self.qname, +- routing_key="mgmt") +- self.channel.message_subscribe (queue=self.qname, destination="dest") +- self.queue = self.client.queue ("dest") +- self.queue.listen (self.receive_cb) + +- self.channel.message_flow_mode (destination="dest", mode=1) +- self.channel.message_flow (destination="dest", unit=0, value=0xFFFFFFFF) +- self.channel.message_flow (destination="dest", unit=1, value=0xFFFFFFFF) + + except socket.error, e: + print "Socket Error Detected:", e[1] + raise + except: + raise +--- 195,335 ---- + return 0 + return 1 + ++ def publish_cb (self, msg): + codec = Codec (StringIO (msg.content.body), self.spec) + + if self.checkHeader (codec) == 0: + raise ValueError ("outer header invalid"); + ++ self.metadata.parse (codec) ++ msg.complete () + ++ def reply_cb (self, msg): ++ codec = Codec (StringIO (msg.content.body), self.spec) ++ methodId = codec.decode_long () ++ status = codec.decode_long () ++ sText = codec.decode_shortstr () ++ ++ args = {} ++ if status == 0: ++ args["sequence"] = codec.decode_long () ++ args["body"] = codec.decode_longstr () ++ ++ if self.method_cb != None: ++ self.method_cb[1] (self.method_cb[0], methodId, status, sText, args) ++ + msg.complete () + ++ def __init__ (self, ++ host = "localhost", ++ port = 5672, ++ username = "guest", ++ password = "guest", ++ specfile = "../specs/amqp.0-10-preview.xml"): + ++ self.spec = qpid.spec.load (specfile) ++ self.client = None ++ self.channel = None ++ self.queue = None ++ self.rqueue = None ++ self.qname = None ++ self.rqname = None ++ self.metadata = ManagementMetadata (self) ++ self.connected = 0 ++ self.lastConnectError = None + + # Initialize the callback records ++ self.status_cb = None + self.schema_cb = None + self.config_cb = None + self.inst_cb = None ++ self.method_cb = None + + self.host = host + self.port = port + self.username = username + self.password = password + ++ def statusListener (self, context, callback): ++ self.status_cb = (context, callback) ++ + def schemaListener (self, context, callback): + self.schema_cb = (context, callback) + + def configListener (self, context, callback): + self.config_cb = (context, callback) + ++ def methodListener (self, context, callback): ++ self.method_cb = (context, callback) ++ + def instrumentationListener (self, context, callback): + self.inst_cb = (context, callback) + ++ def method (self, methodId, objId, className, ++ methodName, args=None, packageName="qpid"): ++ codec = Codec (StringIO (), self.spec); ++ codec.encode_long (methodId) ++ codec.encode_longlong (objId) ++ codec.encode_shortstr (self.rqname) ++ ++ # TODO: Encode args according to schema ++ if methodName == "echo": ++ codec.encode_long (args["sequence"]) ++ codec.encode_longstr (args["body"]) ++ ++ msg = Content (codec.stream.getvalue ()) ++ msg["content_type"] = "application/octet-stream" ++ msg["routing_key"] = "method." + packageName + "." + className + "." + methodName ++ msg["reply_to"] = self.spec.struct ("reply_to") ++ self.channel.message_transfer (destination="qpid.management", content=msg) ++ ++ def isConnected (self): ++ return connected ++ + def start (self): ++ print "Connecting to broker %s:%d" % (self.host, self.port) + + try: + self.client = Client (self.host, self.port, self.spec) + self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) + self.channel = self.client.channel (1) ++ response = self.channel.session_open (detached_lifetime=10) ++ self.qname = "mgmt-" + base64.urlsafe_b64encode (response.session_id) ++ self.rqname = "reply-" + base64.urlsafe_b64encode (response.session_id) + ++ self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) ++ self.channel.queue_declare (queue=self.rqname, exclusive=1, auto_delete=1) ++ ++ self.channel.queue_bind (exchange=ManagedBroker.mExchange, queue=self.qname, ++ routing_key="mgmt.#") ++ self.channel.queue_bind (exchange=ManagedBroker.dExchange, queue=self.rqname, ++ routing_key=self.rqname) + ++ self.channel.message_subscribe (queue=self.qname, destination="mdest") ++ self.channel.message_subscribe (queue=self.rqname, destination="rdest") + ++ self.queue = self.client.queue ("mdest") ++ self.queue.listen (self.publish_cb) ++ ++ self.channel.message_flow_mode (destination="mdest", mode=1) ++ self.channel.message_flow (destination="mdest", unit=0, value=0xFFFFFFFF) ++ self.channel.message_flow (destination="mdest", unit=1, value=0xFFFFFFFF) ++ ++ self.rqueue = self.client.queue ("rdest") ++ self.rqueue.listen (self.reply_cb) ++ ++ self.channel.message_flow_mode (destination="rdest", mode=1) ++ self.channel.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) ++ self.channel.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) ++ ++ self.connected = 1 ++ + except socket.error, e: + print "Socket Error Detected:", e[1] ++ self.lastConnectError = e + raise + except: + raise ++ ++ def stop (self): ++ pass -- cgit v1.2.1 From d4881839ec7780a125331167e943c3069938e43b Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Tue, 11 Dec 2007 13:48:30 +0000 Subject: set executable property git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@603242 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/main.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 qpid/python/mgmt-cli/main.py (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/main.py b/qpid/python/mgmt-cli/main.py old mode 100644 new mode 100755 -- cgit v1.2.1 From fc68a4fae5468bdd800e4fecbd4d81853940b222 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Fri, 14 Dec 2007 20:22:30 +0000 Subject: patch from tross QPID-706 Added implementation for the "Call" command to invoke methods on management objects. Fixed a bug in qpid/management.py caused by replies to methods with no arguments. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@604286 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/main.py | 3 +- qpid/python/mgmt-cli/managementdata.py | 71 ++++++++++++++++++++++++++++------ qpid/python/qpid/management.py | 12 +++--- 3 files changed, 68 insertions(+), 18 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/main.py b/qpid/python/mgmt-cli/main.py index 2990e25437..4d7c03d1f2 100755 --- a/qpid/python/mgmt-cli/main.py +++ b/qpid/python/mgmt-cli/main.py @@ -48,11 +48,12 @@ class Mcli (Cmd): print " list - Print summary of existing objects by class" print " list - Print list of objects of the specified class" print " list all - Print contents of all objects of specified class" + print " list active - Print contents of all non-deleted objects of specified class" print " list - Print contents of one or more objects" print " list is space-separated, ranges may be specified (i.e. 1004-1010)" print " call [] - Invoke a method on an object" print " schema - Print summary of object classes seen on the target" - print " schema [className] - Print details of an object class" + print " schema - Print details of an object class" print " set time-format short - Select short timestamp format (default)" print " set time-format long - Select long timestamp format" print " quit or ^D - Exit the program" diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index b770677825..2adb962b54 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -90,9 +90,16 @@ class ManagementData: finally: self.lock.release () - def methodReply (self, broker, methodId, status, sText, args): + def methodReply (self, broker, sequence, status, sText, args): """ Callback for method-reply messages """ - pass + self.lock.acquire () + try: + line = "Call Result: " + self.methodsPending[sequence] + \ + " " + str (status) + " (" + sText + ")" + print line, args + del self.methodsPending[sequence] + finally: + self.lock.release () def schemaHandler (self, context, className, configs, insts, methods, events): """ Callback for schema updates """ @@ -106,11 +113,13 @@ class ManagementData: self.broker.instrumentationListener (1, self.dataHandler) self.broker.methodListener (None, self.methodReply) self.broker.schemaListener (None, self.schemaHandler) - self.lock = Lock () - self.tables = {} - self.schema = {} - self.baseId = 0 - self.disp = disp + self.lock = Lock () + self.tables = {} + self.schema = {} + self.baseId = 0 + self.disp = disp + self.methodSeq = 1 + self.methodsPending = {} self.broker.start () def close (self): @@ -186,6 +195,11 @@ class ManagementData: for id in self.tables[className]: list.append (id - self.baseId) + elif tokens[0] == "active": + for id in self.tables[className]: + if self.tables[className][id][0][2] == 0: + list.append (id - self.baseId) + else: for token in tokens: if token.find ("-") != -1: @@ -362,10 +376,8 @@ class ManagementData: titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") self.disp.table ("Schema for class '%s':" % className, titles, rows) - for method in self.schema[className][2]: - mname = method[0] - mdesc = method[1] - args = method[2] + for mname in self.schema[className][2]: + (mdesc, args) = self.schema[className][2][mname] caption = "\nMethod '%s' %s" % (mname, self.notNone (mdesc)) rows = [] for arg in args: @@ -398,6 +410,33 @@ class ManagementData: return className return None + def callMethod (self, userOid, methodName, args): + self.lock.acquire () + methodOk = True + try: + className = self.getClassForId (userOid + self.baseId) + if className == None: + raise ValueError () + + schemaMethod = self.schema[className][2][methodName] + if len (args) != len (schemaMethod[1]): + print "Wrong number of method args: Need %d, Got %d" % (len (schemaMethod[1]), len (args)) + raise ValueError () + + namedArgs = {} + for idx in range (len (args)): + namedArgs[schemaMethod[1][idx][0]] = args[idx] + + self.methodSeq = self.methodSeq + 1 + self.methodsPending[self.methodSeq] = methodName + except: + methodOk = False + print "Error in call syntax" + self.lock.release () + if methodOk: + self.broker.method (self.methodSeq, userOid + self.baseId, className, + methodName, namedArgs) + def do_list (self, data): tokens = data.split () if len (tokens) == 0: @@ -414,4 +453,12 @@ class ManagementData: self.schemaTable (data) def do_call (self, data): - print "Not yet implemented" + tokens = data.split () + if len (tokens) < 2: + print "Not enough arguments supplied" + return + + userOid = long (tokens[0]) + methodName = tokens[1] + args = tokens[2:] + self.callMethod (userOid, methodName, args) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 8373ecceb7..1c8b3cd840 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -108,7 +108,7 @@ class ManagementMetadata: configs = [] insts = [] - methods = [] + methods = {} events = [] configs.append (("id", 4, "", "", 1, 1, None, None, None, None, None)) @@ -195,7 +195,7 @@ class ManagementMetadata: arg = (name, type, dir, unit, desc, min, max, maxlen, default) args.append (arg) - methods.append ((mname, mdesc, args)) + methods[mname] = (mdesc, args) self.schema[(className,'C')] = configs @@ -297,18 +297,19 @@ class ManagedBroker: return (userSequence, className, methodName) = data + args = {} if status == 0: ms = self.metadata.schema[(className,'M')] arglist = None - for (mname, mdesc, margs) in ms: + for mname in ms: + (mdesc, margs) = ms[mname] if mname == methodName: arglist = margs if arglist == None: msg.complete () return - args = {} for arg in arglist: if arg[2].find("O") != -1: args[arg[0]] = self.metadata.decodeValue (codec, arg[1]) @@ -379,7 +380,8 @@ class ManagedBroker: ms = self.metadata.schema[(className,'M')] arglist = None - for (mname, mdesc, margs) in ms: + for mname in ms: + (mdesc, margs) = ms[mname] if mname == methodName: arglist = margs if arglist == None: -- cgit v1.2.1 From a8cfd6ce0dd80f4e2980f48282d074aef53f8132 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 19 Dec 2007 16:01:28 +0000 Subject: File rename to better fit the pubsub nomenclature (from jrobie@redhat.com) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@605599 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/pubsub/topic_consumer.py | 118 ---------------------- qpid/python/examples/pubsub/topic_producer.py | 96 ------------------ qpid/python/examples/pubsub/topic_publisher.py | 80 +++++++++++++++ qpid/python/examples/pubsub/topic_subscriber.py | 124 ++++++++++++++++++++++++ 4 files changed, 204 insertions(+), 214 deletions(-) delete mode 100644 qpid/python/examples/pubsub/topic_consumer.py delete mode 100644 qpid/python/examples/pubsub/topic_producer.py create mode 100644 qpid/python/examples/pubsub/topic_publisher.py create mode 100644 qpid/python/examples/pubsub/topic_subscriber.py (limited to 'qpid/python') diff --git a/qpid/python/examples/pubsub/topic_consumer.py b/qpid/python/examples/pubsub/topic_consumer.py deleted file mode 100644 index afe8bba91e..0000000000 --- a/qpid/python/examples/pubsub/topic_consumer.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python -""" - topic_consumer.py - - This AMQP client reads all messages from the - "news", "weather", "usa", and "europe" queues - created and bound by config_topic_exchange.py. -""" - -import base64 - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Functions ------------------------------------------- - -def dump_queue(client, queue_name): - - print "Messages queue: " + queue_name - - consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = client.queue(consumer_tag) - - # Call basic_consume() to tell the broker to deliver messages - # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as basic_consume() is called. - - session.message_subscribe(queue=queue_name, destination=consumer_tag) - session.message_flow(consumer_tag, 0, 0xFFFFFFFF) - session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - - content = "" # Content of the last message read - final = "That's all, folks!" # In a message body, signals the last message - message = 0 - - while content != final: - try: - message = queue.get() - content = message.content.body - print content - except Empty: - if message != 0: - message.complete(cumulative=True) - print "No more messages!" - return - - - # Messages are not removed from the queue until they - # are acknowledged. Using multiple=True, all messages - # in the channel up to and including the one identified - # by the delivery tag are acknowledged. This is more efficient, - # because there are fewer network round-trips. - - if message != 0: - message.complete(cumulative=True) - - -#----- Initialization -------------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session_info = session.session_open() -session_id = session_info.session_id - -#----- Main Body -- ---------------------------------------- - - -news = "news" + base64.urlsafe_b64encode(session_id) -weather = "weather" + base64.urlsafe_b64encode(session_id) -usa = "usa" + base64.urlsafe_b64encode(session_id) -europe = "europe" + base64.urlsafe_b64encode(session_id) - -session.queue_declare(queue=news, exclusive=True) -session.queue_declare(queue=weather, exclusive=True) -session.queue_declare(queue=usa, exclusive=True) -session.queue_declare(queue=europe, exclusive=True) - -# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". - -# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches -# "europe.news" or "usa.news". - -session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") -session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") -session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") -session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") - -# Remind the user to start the topic producer - -print "Queues create - please start the topic producer" - -# Call dump_queue to print messages from each queue - -dump_queue(client, news) -dump_queue(client, weather) -dump_queue(client, usa) -dump_queue(client, europe) - -#----- Cleanup ------------------------------------------------ - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/pubsub/topic_producer.py b/qpid/python/examples/pubsub/topic_producer.py deleted file mode 100644 index c3b13cd82c..0000000000 --- a/qpid/python/examples/pubsub/topic_producer.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -""" - topic_producer.py - - This is a simple AMQP publisher application that uses a - Topic exchange. The publisher specifies the routing key - and the exchange for each message. -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login. - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Publish some messages ------------------------------ - -# Create some messages and put them on the broker. Use the -# topic exchange. The routing keys are "usa.news", "usa.weather", -# "europe.news", and "europe.weather". - -final = "That's all, folks!" - -# We'll use the same routing key for all messages in the loop, and -# also for the terminating message. - -# usa.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.news" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "usa.news" -session.message_transfer(destination="amq.topic", content=message) - -# usa.weather - - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.weather" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "usa.weather" -session.message_transfer(destination="amq.topic", content=message) - -# europe.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.news" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "europe.news" -session.message_transfer(destination="amq.topic", content=message) - - -# europe.weather - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.weather" - session.message_transfer(destination="amq.topic", content=message) - -message = Content(final) -message["routing_key"] = "europe.weather" -session.message_transfer(destination="amq.topic", content=message) - - -#----- Cleanup -------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py new file mode 100644 index 0000000000..1ff983b315 --- /dev/null +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" + topic_publisher.py + + This is a simple AMQP publisher application that uses a + Topic exchange. The publisher specifies the routing key + and the exchange for each message. +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login. + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. Use the +# topic exchange. The routing keys are "usa.news", "usa.weather", +# "europe.news", and "europe.weather". + + +# usa.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.news" + session.message_transfer(destination="amq.topic", content=message) + +# usa.weather + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "usa.weather" + session.message_transfer(destination="amq.topic", content=message) + +# europe.news + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.news" + session.message_transfer(destination="amq.topic", content=message) + +# europe.weather + +for i in range(5): + message = Content("message " + str(i)) + message["routing_key"] = "europe.weather" + session.message_transfer(destination="amq.topic", content=message) + +# Signal termination + +message = Content("That's all, folks!") +message["routing_key"] = "control" +session.message_transfer(destination="amq.topic", content=message) + + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py new file mode 100644 index 0000000000..08682f0674 --- /dev/null +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +""" + topic_subscriber.py + + This subscriber creates private queues and binds them + to the topics "usa.#", "europe.#", "#.news", and "#.weather". +""" + +import base64 + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Functions ------------------------------------------- + +def dump_queue(client, queue_name): + + print "Messages queue: " + queue_name + + consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag + queue = client.queue(consumer_tag) + + # Call basic_consume() to tell the broker to deliver messages + # from the AMQP queue to a local client queue. The broker will + # start delivering messages as soon as basic_consume() is called. + + session.message_subscribe(queue=queue_name, destination=consumer_tag) + session.message_flow(consumer_tag, 0, 0xFFFFFFFF) + session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + + content = "" # Content of the last message read + final = "That's all, folks!" # In a message body, signals the last message + message = 0 + + while content != final: + try: + message = queue.get() + content = message.content.body + print content + except Empty: + if message != 0: + message.complete(cumulative=True) + print "No more messages!" + return + + + # Messages are not removed from the queue until they + # are acknowledged. Using multiple=True, all messages + # in the channel up to and including the one identified + # by the delivery tag are acknowledged. This is more efficient, + # because there are fewer network round-trips. + + if message != 0: + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +spec = qpid.spec.load(amqp_spec) +client = Client(host, port, spec) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session_info = session.session_open() +session_id = session_info.session_id + +#----- Main Body -- ---------------------------------------- + + +news = "news" + base64.urlsafe_b64encode(session_id) +weather = "weather" + base64.urlsafe_b64encode(session_id) +usa = "usa" + base64.urlsafe_b64encode(session_id) +europe = "europe" + base64.urlsafe_b64encode(session_id) + +session.queue_declare(queue=news, exclusive=True) +session.queue_declare(queue=weather, exclusive=True) +session.queue_declare(queue=usa, exclusive=True) +session.queue_declare(queue=europe, exclusive=True) + +# Routing keys may be "usa.news", "usa.weather", "europe.news", or "europe.weather". + +# The '#' symbol matches one component of a multipart name, e.g. "#.news" matches +# "europe.news" or "usa.news". + +session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") +session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") +session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") +session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") + +# Bind each queue to the control queue so we know when to stop + +session.queue_bind(exchange="amq.topic", queue=news, routing_key="control") +session.queue_bind(exchange="amq.topic", queue=weather, routing_key="control") +session.queue_bind(exchange="amq.topic", queue=usa, routing_key="control") +session.queue_bind(exchange="amq.topic", queue=europe, routing_key="control") + +# Remind the user to start the topic producer + +print "Queues create - please start the topic producer" + +# Call dump_queue to print messages from each queue + +dump_queue(client, news) +dump_queue(client, weather) +dump_queue(client, usa) +dump_queue(client, europe) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. + +session.session_close() -- cgit v1.2.1 From a3f994526194bda58a1e160b3191eb944ec1f6dc Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 20 Dec 2007 16:40:12 +0000 Subject: Further renames as suggested by jrobie@redhat.com git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@605975 13f79535-47bb-0310-9956-ffa450edef68 --- .../examples/direct/config_direct_exchange.py | 53 ------------- qpid/python/examples/direct/declare_queues.py | 53 +++++++++++++ qpid/python/examples/direct/direct_producer.py | 1 - qpid/python/examples/direct/listener.py | 87 ++++++++++++++++++++++ .../examples/fanout/config_fanout_exchange.py | 54 -------------- qpid/python/examples/fanout/declare_queues.py | 54 ++++++++++++++ qpid/python/examples/fanout/listener.py | 86 +++++++++++++++++++++ 7 files changed, 280 insertions(+), 108 deletions(-) delete mode 100644 qpid/python/examples/direct/config_direct_exchange.py create mode 100644 qpid/python/examples/direct/declare_queues.py create mode 100644 qpid/python/examples/direct/listener.py delete mode 100644 qpid/python/examples/fanout/config_fanout_exchange.py create mode 100644 qpid/python/examples/fanout/declare_queues.py create mode 100644 qpid/python/examples/fanout/listener.py (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/config_direct_exchange.py b/qpid/python/examples/direct/config_direct_exchange.py deleted file mode 100644 index e64ad678b8..0000000000 --- a/qpid/python/examples/direct/config_direct_exchange.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -""" - config_direct_exchange.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.direct" exchange. -# -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") - -#----- Cleanup --------------------------------------------- - -session.session_close() - diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py new file mode 100644 index 0000000000..e64ad678b8 --- /dev/null +++ b/qpid/python/examples/direct/declare_queues.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.direct" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") + +#----- Cleanup --------------------------------------------- + +session.session_close() + diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index 6770e56803..ff662477ce 100644 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -48,4 +48,3 @@ session.message_transfer(destination="amq.direct", content=message) # Clean up before exiting so there are no open threads. session.session_close() - diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py new file mode 100644 index 0000000000..e5eee72637 --- /dev/null +++ b/qpid/python/examples/direct/listener.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +""" + listener.py + + This AMQP client reads messages from a message + queue named "message_queue". It is implemented + as a message listener. +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty +from time import sleep + + +#----- Message Receive Handler ----------------------------- +class Receiver: + def __init__ (self): + self.finalReceived = False + + def isFinal (self): + return self.finalReceived + + def Handler (self, message): + content = message.content.body + print content + if content == "That's all, folks!": + self.finalReceived = True + + # Messages are not removed from the queue until they are + # acknowledged. Using cumulative=True, all messages from the session + # up to and including the one identified by the delivery tag are + # acknowledged. This is more efficient, because there are fewer + # network round-trips. + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? + +receiver = Receiver () +queue.listen (receiver.Handler) + +while not receiver.isFinal (): + sleep (1) + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() diff --git a/qpid/python/examples/fanout/config_fanout_exchange.py b/qpid/python/examples/fanout/config_fanout_exchange.py deleted file mode 100644 index 3315f5bc14..0000000000 --- a/qpid/python/examples/fanout/config_fanout_exchange.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -""" - config_direct_exchange.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host="127.0.0.1" -port=5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.fanout" exchange. -# -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.fanout", queue="message_queue") - -#----- Cleanup --------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/fanout/declare_queues.py b/qpid/python/examples/fanout/declare_queues.py new file mode 100644 index 0000000000..3315f5bc14 --- /dev/null +++ b/qpid/python/examples/fanout/declare_queues.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" + config_direct_exchange.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Create a queue ------------------------------------- + +# Create a queue named "listener" on channel 1, and bind it +# to the "amq.fanout" exchange. +# +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.queue_declare(queue="message_queue") +session.queue_bind(exchange="amq.fanout", queue="message_queue") + +#----- Cleanup --------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.session_close() diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py new file mode 100644 index 0000000000..4779466fd9 --- /dev/null +++ b/qpid/python/examples/fanout/listener.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +from qpid.client import Client +from qpid.content import Content +from qpid.queue import Empty +from time import sleep + + +#----- Message Receive Handler ----------------------------- +class Receiver: + def __init__ (self): + self.finalReceived = False + + def isFinal (self): + return self.finalReceived + + def Handler (self, message): + content = message.content.body + print content + if content == "That's all, folks!": + self.finalReceived = True + + # Messages are not removed from the queue until they are + # acknowledged. Using cumulative=True, all messages from the session + # up to and including the one identified by the delivery tag are + # acknowledged. This is more efficient, because there are fewer + # network round-trips. + message.complete(cumulative=True) + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +user="guest" +password="guest" + +# Create a client and log in to it. + +client = Client(host, port, qpid.spec.load(amqp_spec)) +client.start({"LOGIN": user, "PASSWORD": password}) + +session = client.session() +session.session_open() + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +consumer_tag = "consumer1" +queue = client.queue(consumer_tag) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=consumer_tag) +session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? +session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? + +receiver = Receiver () +queue.listen (receiver.Handler) + +while not receiver.isFinal (): + sleep (1) + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.session_close() -- cgit v1.2.1 From e336559c82f22ecd0a013b8ea787bb4946ab2fdc Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Wed, 2 Jan 2008 15:56:20 +0000 Subject: patch-715 (tross) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@608135 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/main.py | 1 + qpid/python/mgmt-cli/managementdata.py | 141 +++++++++++++++++++++++++-------- qpid/python/qpid/management.py | 65 +++++++++++---- 3 files changed, 158 insertions(+), 49 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/main.py b/qpid/python/mgmt-cli/main.py index 4d7c03d1f2..76e1f25c14 100755 --- a/qpid/python/mgmt-cli/main.py +++ b/qpid/python/mgmt-cli/main.py @@ -49,6 +49,7 @@ class Mcli (Cmd): print " list - Print list of objects of the specified class" print " list all - Print contents of all objects of specified class" print " list active - Print contents of all non-deleted objects of specified class" + print " list - Print contents of one or more objects (infer className)" print " list - Print contents of one or more objects" print " list is space-separated, ranges may be specified (i.e. 1004-1010)" print " call [] - Invoke a method on an object" diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index 2adb962b54..419cbd13c0 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -118,6 +118,7 @@ class ManagementData: self.schema = {} self.baseId = 0 self.disp = disp + self.lastUnit = None self.methodSeq = 1 self.methodsPending = {} self.broker.start () @@ -125,6 +126,44 @@ class ManagementData: def close (self): self.broker.stop () + def refName (self, oid): + if oid == 0: + return "NULL" + return str (oid - self.baseId) + + def valueDisplay (self, className, key, value): + for kind in range (2): + schema = self.schema[className][kind] + for item in schema: + if item[0] == key: + typecode = item[1] + unit = item[2] + if typecode >= 1 and typecode <= 5: # numerics + if unit == None or unit == self.lastUnit: + return str (value) + else: + self.lastUnit = unit + suffix = "" + if value != 1: + suffix = "s" + return str (value) + " " + unit + suffix + elif typecode == 6 or typecode == 7: # strings + return value + elif typecode == 8: + if value == 0: + return "--" + return self.disp.timestamp (value) + elif typecode == 9: + return str (value) + elif typecode == 10: + return self.refName (value) + elif typecode == 11: + if value == 0: + return "False" + else: + return "True" + return "*type-error*" + def getObjIndex (self, className, config): """ Concatenate the values from index columns to form a unique object name """ result = "" @@ -135,9 +174,7 @@ class ManagementData: result = result + "." for key,val in config: if key == item[0]: - if key.find ("Ref") != -1: - val = val - self.baseId - result = result + str (val) + result = result + self.valueDisplay (className, key, val) return result def classCompletions (self, prefix): @@ -168,6 +205,14 @@ class ManagementData: return "short-string" elif typecode == 7: return "long-string" + elif typecode == 8: + return "abs-time" + elif typecode == 9: + return "delta-time" + elif typecode == 10: + return "reference" + elif typecode == 11: + return "boolean" else: raise ValueError ("Invalid type code: %d" % typecode) @@ -180,7 +225,7 @@ class ManagementData: elif code == 3: return "ReadOnly" else: - raise ValueErrir ("Invalid access code: %d" %code) + raise ValueError ("Invalid access code: %d" %code) def notNone (self, text): if text == None: @@ -188,6 +233,12 @@ class ManagementData: else: return text + def isOid (self, id): + for char in str (id): + if not char.isdigit () and not char == '-': + return False + return True + def listOfIds (self, className, tokens): """ Generate a tuple of object ids for a classname based on command tokens. """ list = [] @@ -202,13 +253,14 @@ class ManagementData: else: for token in tokens: - if token.find ("-") != -1: - ids = token.split("-", 2) - for id in range (int (ids[0]), int (ids[1]) + 1): - if self.getClassForId (long (id) + self.baseId) == className: - list.append (id) - else: - list.append (token) + if self.isOid (token): + if token.find ("-") != -1: + ids = token.split("-", 2) + for id in range (int (ids[0]), int (ids[1]) + 1): + if self.getClassForId (long (id) + self.baseId) == className: + list.append (id) + else: + list.append (token) list.sort () result = () @@ -258,7 +310,7 @@ class ManagementData: if ts[2] > 0: destroyTime = self.disp.timestamp (ts[2]) objIndex = self.getObjIndex (className, config) - row = (objId - self.baseId, createTime, destroyTime, objIndex) + row = (self.refName (objId), createTime, destroyTime, objIndex) rows.append (row) self.disp.table ("Objects of type %s" % className, ("ID", "Created", "Destroyed", "Index"), @@ -270,12 +322,26 @@ class ManagementData: """ Generate a display of object data for a particular class """ self.lock.acquire () try: - className = tokens[0] - if className not in self.tables: - print "Class not known: %s" % className - raise ValueError () - - userIds = self.listOfIds (className, tokens[1:]) + self.lastUnit = None + if self.isOid (tokens[0]): + if tokens[0].find ("-") != -1: + rootId = int (tokens[0][0:tokens[0].find ("-")]) + else: + rootId = int (tokens[0]) + + className = self.getClassForId (rootId + self.baseId) + remaining = tokens + if className == None: + print "Id not known: %d" % int (tokens[0]) + raise ValueError () + else: + className = tokens[0] + remaining = tokens[1:] + if className not in self.tables: + print "Class not known: %s" % className + raise ValueError () + + userIds = self.listOfIds (className, remaining) if len (userIds) == 0: print "No object IDs supplied" raise ValueError () @@ -286,36 +352,37 @@ class ManagementData: ids.append (long (id) + self.baseId) rows = [] + timestamp = None config = self.tables[className][ids[0]][1] for eIdx in range (len (config)): key = config[eIdx][0] if key != "id": - isRef = key.find ("Ref") == len (key) - 3 row = ("config", key) for id in ids: - value = self.tables[className][id][1][eIdx][1] - if isRef: - value = value - self.baseId - row = row + (value,) + if timestamp == None or \ + timestamp < self.tables[className][id][0][0]: + timestamp = self.tables[className][id][0][0] + (key, value) = self.tables[className][id][1][eIdx] + row = row + (self.valueDisplay (className, key, value),) rows.append (row) inst = self.tables[className][ids[0]][2] for eIdx in range (len (inst)): key = inst[eIdx][0] if key != "id": - isRef = key.find ("Ref") == len (key) - 3 row = ("inst", key) for id in ids: - value = self.tables[className][id][2][eIdx][1] - if isRef: - value = value - self.baseId - row = row + (value,) + (key, value) = self.tables[className][id][2][eIdx] + row = row + (self.valueDisplay (className, key, value),) rows.append (row) titleRow = ("Type", "Element") for id in ids: - titleRow = titleRow + (str (id - self.baseId),) - self.disp.table ("Object of type %s:" % className, titleRow, rows) + titleRow = titleRow + (self.refName (id),) + caption = "Object of type %s:" % className + if timestamp != None: + caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" + self.disp.table (caption, titleRow, rows) except: pass @@ -418,6 +485,10 @@ class ManagementData: if className == None: raise ValueError () + if methodName not in self.schema[className][2]: + print "Method '%s' not valid for class '%s'" % (methodName, className) + raise ValueError () + schemaMethod = self.schema[className][2][methodName] if len (args) != len (schemaMethod[1]): print "Wrong number of method args: Need %d, Got %d" % (len (schemaMethod[1]), len (args)) @@ -431,17 +502,19 @@ class ManagementData: self.methodsPending[self.methodSeq] = methodName except: methodOk = False - print "Error in call syntax" self.lock.release () if methodOk: - self.broker.method (self.methodSeq, userOid + self.baseId, className, - methodName, namedArgs) +# try: + self.broker.method (self.methodSeq, userOid + self.baseId, className, + methodName, namedArgs) +# except ValueError, e: +# print "Error invoking method:", e def do_list (self, data): tokens = data.split () if len (tokens) == 0: self.listClasses () - elif len (tokens) == 1: + elif len (tokens) == 1 and not self.isOid (tokens[0]): self.listObjects (data) else: self.showObjects (tokens) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 1c8b3cd840..40de2a5298 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -77,6 +77,14 @@ class ManagementMetadata: codec.encode_shortstr (value) elif typecode == 7: codec.encode_longstr (value) + elif typecode == 8: # ABSTIME + codec.encode_longlong (long (value)) + elif typecode == 9: # DELTATIME + codec.encode_longlong (long (value)) + elif typecode == 10: # REF + codec.encode_longlong (long (value)) + elif typecode == 11: # BOOL + codec.encode_octet (int (value)) else: raise ValueError ("Invalid type code: %d" % typecode) @@ -95,6 +103,14 @@ class ManagementMetadata: data = codec.decode_shortstr () elif typecode == 7: data = codec.decode_longstr () + elif typecode == 8: # ABSTIME + data = codec.decode_longlong () + elif typecode == 9: # DELTATIME + data = codec.decode_longlong () + elif typecode == 10: # REF + data = codec.decode_longlong () + elif typecode == 11: # BOOL + data = codec.decode_octet () else: raise ValueError ("Invalid type code: %d" % typecode) return data @@ -236,10 +252,7 @@ class ManagementMetadata: elif cls == 'I': self.broker.inst_cb[1] (self.broker.inst_cb[0], className, row, timestamps) - def parse (self, codec): - opcode = chr (codec.decode_octet ()) - cls = chr (codec.decode_octet ()) - + def parse (self, codec, opcode, cls): if opcode == 'S': self.parseSchema (cls, codec) @@ -261,34 +274,53 @@ class ManagedBroker: mExchange = "qpid.management" dExchange = "amq.direct" + def setHeader (self, codec, opcode, cls = 0): + codec.encode_octet (ord ('A')) + codec.encode_octet (ord ('M')) + codec.encode_octet (ord ('0')) + codec.encode_octet (ord ('1')) + codec.encode_octet (opcode) + codec.encode_octet (cls) + def checkHeader (self, codec): octet = chr (codec.decode_octet ()) if octet != 'A': - return 0 + return None octet = chr (codec.decode_octet ()) if octet != 'M': - return 0 + return None octet = chr (codec.decode_octet ()) if octet != '0': - return 0 + return None octet = chr (codec.decode_octet ()) if octet != '1': - return 0 - return 1 + return None + opcode = chr (codec.decode_octet ()) + cls = chr (codec.decode_octet ()) + return (opcode, cls) def publish_cb (self, msg): codec = Codec (StringIO (msg.content.body), self.spec) - if self.checkHeader (codec) == 0: + hdr = self.checkHeader (codec) + if hdr == None: raise ValueError ("outer header invalid"); - self.metadata.parse (codec) + self.metadata.parse (codec, hdr[0], hdr[1]) msg.complete () def reply_cb (self, msg): codec = Codec (StringIO (msg.content.body), self.spec) - sequence = codec.decode_long () - status = codec.decode_long () + hdr = self.checkHeader (codec) + if hdr == None: + msg.complete () + return + if hdr[0] != 'R': + msg.complete () + return + + sequence = codec.decode_long () + status = codec.decode_long () sText = codec.decode_shortstr () data = self.sequenceManager.release (sequence) @@ -369,9 +401,10 @@ class ManagedBroker: methodName, args=None, packageName="qpid"): codec = Codec (StringIO (), self.spec); sequence = self.sequenceManager.reserve ((userSequence, className, methodName)) + self.setHeader (codec, ord ('M')) codec.encode_long (sequence) # Method sequence id codec.encode_longlong (objId) # ID of object - codec.encode_shortstr (self.rqname) # name of reply queue + #codec.encode_shortstr (self.rqname) # name of reply queue # Encode args according to schema if (className,'M') not in self.metadata.schema: @@ -402,6 +435,8 @@ class ManagedBroker: msg["content_type"] = "application/octet-stream" msg["routing_key"] = "method." + packageName + "." + className + "." + methodName msg["reply_to"] = self.spec.struct ("reply_to") + msg["reply_to"]["exchange_name"] = "amq.direct" + msg["reply_to"]["routing_key"] = self.rqname self.channel.message_transfer (destination="qpid.management", content=msg) def isConnected (self): @@ -414,7 +449,7 @@ class ManagedBroker: self.client = Client (self.host, self.port, self.spec) self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) self.channel = self.client.channel (1) - response = self.channel.session_open (detached_lifetime=10) + response = self.channel.session_open (detached_lifetime=300) self.qname = "mgmt-" + base64.urlsafe_b64encode (response.session_id) self.rqname = "reply-" + base64.urlsafe_b64encode (response.session_id) -- cgit v1.2.1 From 168f40831104bc954cadba17d08544c08c858b77 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 7 Jan 2008 16:28:07 +0000 Subject: Patch from https://issues.apache.org/jira/browse/QPID-722 by Ted Ross: Two changes in this patch: 1) Management object IDs are now persistent for persistent (durable) objects. This is required to provide continuity of historical management data across broker restarts. The format of object IDs now indicates whether they are transient or persistent. The upper bit (bit 63) is 0 for transient IDs and 1 for persistent IDs. 2) Changes have been made to the management code generator in preparation for allowing it to be used by outside projects that wish to use the broker Plugin API for management access. File-by-file notes: M python/mgmt-cli/managementdata.py Enhanced user-friendly display of 64-bit object IDs to differentiate between persistent IDs and non-persistent IDs. M cpp/src/Makefile.am Changed command line format for call to the management code generator. M cpp/src/qpid/broker/Broker.cpp M cpp/src/qpid/broker/Vhost.cpp M cpp/src/qpid/broker/Queue.cpp Updated calls to ManagementAgent::addObject to use the new support for persistent IDs, ensuring that the management object IDs for persistent objects are themselves persistent. M cpp/src/qpid/management/ManagementAgent.h M cpp/src/qpid/management/ManagementAgent.cpp Added support (using defaulted arguments) to ManagementAgent::addObject for persistent object IDs M cpp/managementgen/generate.py M cpp/managementgen/schema.py M cpp/managementgen/main.py Added the ability for templates to set variables to be used during code generation. Makefile fragment is now generated using a template rather than hard-code. This was done to help non-qpid code to use the code generator for management-via-qpid support. M cpp/managementgen/templates/Args.h M cpp/managementgen/templates/Class.cpp M cpp/managementgen/templates/Class.h Use a generator variable to define the comment prefix. A cpp/managementgen/templates/Makefile.mk New template for the qpid makefile fragment. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@609672 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/managementdata.py | 38 +++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index 419cbd13c0..e7233c98ae 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -44,6 +44,21 @@ class ManagementData: # element :== (, ) # + def registerObjId (self, objId): + if self.baseId == 0: + if objId & 0x8000000000000000L == 0: + self.baseId = objId - 1000 + + def displayObjId (self, objId): + if objId & 0x8000000000000000L == 0: + return objId - self.baseId + return (objId & 0x7fffffffffffffffL) + 5000 + + def rawObjId (self, displayId): + if displayId < 5000: + return displayId + self.baseId + return displayId - 5000 + 0x8000000000000000L + def dataHandler (self, context, className, list, timestamps): """ Callback for configuration and instrumentation data updates """ self.lock.acquire () @@ -53,10 +68,9 @@ class ManagementData: if className not in self.tables: self.tables[className] = {} - # Calculate a base-id so displayed IDs are reasonable 4-digit numbers + # Register the ID so a more friendly presentation can be displayed id = long (list[0][1]) - if self.baseId == 0: - self.baseId = id - 1000 + self.registerObjId (id) # If this object hasn't been seen before, create a new object record with # the timestamps and empty lists for configuration and instrumentation data. @@ -129,7 +143,7 @@ class ManagementData: def refName (self, oid): if oid == 0: return "NULL" - return str (oid - self.baseId) + return str (self.displayObjId (oid)) def valueDisplay (self, className, key, value): for kind in range (2): @@ -244,12 +258,12 @@ class ManagementData: list = [] if tokens[0] == "all": for id in self.tables[className]: - list.append (id - self.baseId) + list.append (self.displayObjId (id)) elif tokens[0] == "active": for id in self.tables[className]: if self.tables[className][id][0][2] == 0: - list.append (id - self.baseId) + list.append (self.displayObjId (id)) else: for token in tokens: @@ -257,7 +271,7 @@ class ManagementData: if token.find ("-") != -1: ids = token.split("-", 2) for id in range (int (ids[0]), int (ids[1]) + 1): - if self.getClassForId (long (id) + self.baseId) == className: + if self.getClassForId (self.rawObjId (long (id))) == className: list.append (id) else: list.append (token) @@ -329,7 +343,7 @@ class ManagementData: else: rootId = int (tokens[0]) - className = self.getClassForId (rootId + self.baseId) + className = self.getClassForId (self.rawObjId (rootId)) remaining = tokens if className == None: print "Id not known: %d" % int (tokens[0]) @@ -348,8 +362,8 @@ class ManagementData: ids = [] for id in userIds: - if self.getClassForId (long (id) + self.baseId) == className: - ids.append (long (id) + self.baseId) + if self.getClassForId (self.rawObjId (long (id))) == className: + ids.append (self.rawObjId (long (id))) rows = [] timestamp = None @@ -481,7 +495,7 @@ class ManagementData: self.lock.acquire () methodOk = True try: - className = self.getClassForId (userOid + self.baseId) + className = self.getClassForId (self.rawObjId (userOid)) if className == None: raise ValueError () @@ -505,7 +519,7 @@ class ManagementData: self.lock.release () if methodOk: # try: - self.broker.method (self.methodSeq, userOid + self.baseId, className, + self.broker.method (self.methodSeq, self.rawObjId (userOid), className, methodName, namedArgs) # except ValueError, e: # print "Error invoking method:", e -- cgit v1.2.1 From adab8d6fc99210863b6234fe74d85af900c0035c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 17 Jan 2008 13:04:56 +0000 Subject: Set the exchange field in delivery properties on the broker. (required an adjustment to the size of messages in the bytes based credit test) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@612805 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index fbd3d255de..1daaad9ba1 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -480,7 +480,7 @@ class MessageTests(TestBase): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 34 + msg_size = 35 #set byte credit to finite amount (less than enough for all messages) channel.message_flow(unit = 1, value = msg_size*5, destination = "c") -- cgit v1.2.1 From 70f2437cc12aa48958a0ff3f7a1d59ce29a5eb47 Mon Sep 17 00:00:00 2001 From: Arnaud Simon Date: Fri, 18 Jan 2008 10:36:50 +0000 Subject: Qpid-745 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@613126 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 6 ++---- qpid/python/examples/direct/listener.py | 4 ++-- qpid/python/examples/fanout/declare_queues.py | 5 +---- qpid/python/examples/fanout/fanout_consumer.py | 6 +++--- qpid/python/examples/fanout/fanout_producer.py | 2 +- qpid/python/examples/fanout/listener.py | 6 +++--- qpid/python/examples/pubsub/topic_subscriber.py | 4 ++-- qpid/python/examples/request-response/client.py | 2 +- qpid/python/examples/request-response/server.py | 2 +- 9 files changed, 16 insertions(+), 21 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index e64ad678b8..0160f66a1d 100644 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ - config_direct_exchange.py + declare_queues.py Creates and binds a queue on an AMQP direct exchange. @@ -33,9 +33,6 @@ session.session_open() #----- Create a queue ------------------------------------- -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.direct" exchange. -# # queue_declare() creates an AMQP queue, which is held # on the broker. Published messages are sent to the AMQP queue, # from which messages are delivered to consumers. @@ -51,3 +48,4 @@ session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="ro session.session_close() + diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index e5eee72637..8324cc76a6 100644 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -64,9 +64,9 @@ session.session_open() consumer_tag = "consumer1" queue = client.queue(consumer_tag) -# Call message_consume() to tell the broker to deliver messages +# Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. +# start delivering messages as soon as message_subscribe() is called. session.message_subscribe(queue="message_queue", destination=consumer_tag) session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? diff --git a/qpid/python/examples/fanout/declare_queues.py b/qpid/python/examples/fanout/declare_queues.py index 3315f5bc14..8840ea38e1 100644 --- a/qpid/python/examples/fanout/declare_queues.py +++ b/qpid/python/examples/fanout/declare_queues.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ - config_direct_exchange.py + declare_queues.py Creates and binds a queue on an AMQP direct exchange. @@ -33,9 +33,6 @@ session.session_open() #----- Create a queue ------------------------------------- -# Create a queue named "listener" on channel 1, and bind it -# to the "amq.fanout" exchange. -# # queue_declare() creates an AMQP queue, which is held # on the broker. Published messages are sent to the AMQP queue, # from which messages are delivered to consumers. diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index e9a2291f4c..8d852ca753 100644 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ - direct_consumer.py + fanout_consumer.py This AMQP client reads messages from a message queue named "message_queue". @@ -40,9 +40,9 @@ session.session_open() consumer_tag = "consumer1" queue = client.queue(consumer_tag) -# Call message_consume() to tell the broker to deliver messages +# Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. +# start delivering messages as soon as message_subscribe() is called. session.message_subscribe(queue="message_queue", destination=consumer_tag) session.message_flow(consumer_tag, 0, 0xFFFFFFFF) diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 92ca7b7ec0..d9d8f454c8 100644 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ - direct_producer.py + fanout_producer.py Publishes messages to an AMQP direct exchange, using the routing key "routing_key" diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index 4779466fd9..09dd7ddb80 100644 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ - direct_consumer.py + listener.py This AMQP client reads messages from a message queue named "message_queue". @@ -63,9 +63,9 @@ session.session_open() consumer_tag = "consumer1" queue = client.queue(consumer_tag) -# Call message_consume() to tell the broker to deliver messages +# Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. +# start delivering messages as soon as message_subscribe() is called. session.message_subscribe(queue="message_queue", destination=consumer_tag) session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index 08682f0674..52ec67b77c 100644 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -22,9 +22,9 @@ def dump_queue(client, queue_name): consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag queue = client.queue(consumer_tag) - # Call basic_consume() to tell the broker to deliver messages + # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as basic_consume() is called. + # start delivering messages as soon as message_subscribe() is called. session.message_subscribe(queue=queue_name, destination=consumer_tag) session.message_flow(consumer_tag, 0, 0xFFFFFFFF) diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index bc0cf8a55a..e218acce3d 100644 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -24,7 +24,7 @@ def dump_queue(client, queue_name): # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as basic_consume() is called. + # start delivering messages as soon as message_subscribe() is called. session.message_subscribe(queue=queue_name, destination=consumer_tag) session.message_flow(consumer_tag, 0, 0xFFFFFFFF) diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 28bfd5ee4a..2730768a91 100644 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -38,7 +38,7 @@ password="guest" client = Client(host, port, qpid.spec.load(amqp_spec)) client.start({"LOGIN": user, "PASSWORD": password}) -# Open Channel 1 so we can use it to manage our queue. +# Create a session and open it. session = client.session() session.session_open() -- cgit v1.2.1 From e4341def77be591ea5be66a0b7ccbe3d69f225b6 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 18 Jan 2008 17:32:55 +0000 Subject: fixed python dependence on the content-length attribute (bz 419371) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@613211 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 10 ++++++---- qpid/python/qpid/peer.py | 12 +++++------- qpid/python/tests_0-10/message.py | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 1beb60822d..ecbce295b7 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -360,7 +360,8 @@ class Header(Frame): # message properties store the content-length now, and weight is # deprecated - structs[1].content_length = self.size + if self.size != None: + structs[1].content_length = self.size for s in structs: c.encode_long_struct(s) @@ -412,9 +413,10 @@ class Header(Frame): length = None for s in structs: for f in s.type.fields: - props[f.name] = s.get(f.name) - if f.name == "content_length": - length = s.get(f.name) + if s.has(f.name): + props[f.name] = s.get(f.name) + if f.name == "content_length": + length = s.get(f.name) return Header(None, 0, length, props) @staticmethod diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 376d81e184..c7f56b49b2 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -226,13 +226,12 @@ class Channel: self.write_content(frame.method_type.klass, content) def write_content(self, klass, content): - size = content.size() - header = Header(klass, content.weight(), size, content.properties) + header = Header(klass, content.weight(), content.size(), content.properties) self.write(header) for child in content.children: self.write_content(klass, child) # should split up if content.body exceeds max frame size - if size > 0: + if content.body: self.write(Body(content.body)) def receive(self, frame, work): @@ -360,14 +359,13 @@ def read_content(queue): children = [] for i in range(header.weight): children.append(read_content(queue)) - size = header.size - read = 0 buf = StringIO() - while read < size: + eof = header.eof + while not eof: body = queue.get() + eof = body.eof content = body.content buf.write(content) - read += len(content) return Content(buf.getvalue(), children, header.properties.copy()) class Future: diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 1daaad9ba1..9ec1cc270c 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -720,6 +720,20 @@ class MessageTests(TestBase): #check all 'browsed' messages are still on the queue self.assertEqual(5, channel.queue_query(queue="q").message_count) + def test_no_size(self): + self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + + ch = self.channel + ch.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) + + ch.message_subscribe(queue = "q", destination="d", confirm_mode = 0) + ch.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "d") + ch.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "d") + + queue = self.client.queue("d") + msg = queue.get(timeout = 3) + self.assertEquals("message-body", msg.content.body) + def assertDataEquals(self, channel, msg, expected): self.assertEquals(expected, msg.content.body) @@ -728,3 +742,8 @@ class MessageTests(TestBase): extra = queue.get(timeout=1) self.fail("Queue not empty, contains: " + extra.content.body) except Empty: None + +class SizelessContent(Content): + + def size(self): + return None -- cgit v1.2.1 From bdecc9d9952d735b0fe6e00c90b11f5c8f542c32 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Wed, 23 Jan 2008 19:43:03 +0000 Subject: removed management.py.rej, which had been checked in by mistake git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@614638 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py.rej | 457 ------------------------------------- 1 file changed, 457 deletions(-) delete mode 100644 qpid/python/qpid/management.py.rej (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py.rej b/qpid/python/qpid/management.py.rej deleted file mode 100644 index 28d172abe5..0000000000 --- a/qpid/python/qpid/management.py.rej +++ /dev/null @@ -1,457 +0,0 @@ -*************** -*** 18,24 **** - # - - """ -- Management classes for AMQP - """ - - import qpid ---- 18,24 ---- - # - - """ -+ Management API for Qpid - """ - - import qpid -*************** -*** 42,91 **** - #=================================================================== - class ManagementMetadata: - -- def parseSchema (self, cls, oid, len, codec): -- #print "Schema Record: objId=", oid - -- config = [] -- inst = [] -- while 1: -- flags = codec.decode_octet () -- if flags == 0x80: -- break - -- tc = codec.decode_octet () -- name = codec.decode_shortstr () -- desc = codec.decode_shortstr () - -- if flags & 1: # TODO: Define constants for these -- config.append ((tc, name, desc)) -- if (flags & 1) == 0 or (flags & 2) == 2: -- inst.append ((tc, name, desc)) - - # TODO: Handle notification of schema change outbound -- self.schema[(oid,'C')] = config -- self.schema[(oid,'I')] = inst - -- def parseContent (self, cls, oid, len, codec): -- #print "Content Record: Class=", cls, ", objId=", oid -- - if cls == 'C' and self.broker.config_cb == None: - return - if cls == 'I' and self.broker.inst_cb == None: - return - -- if (oid,cls) not in self.schema: - return - - row = [] - timestamps = [] - -- timestamps.append (codec.decode_longlong ()); # Current Time -- timestamps.append (codec.decode_longlong ()); # Create Time -- timestamps.append (codec.decode_longlong ()); # Delete Time - -- for element in self.schema[(oid,cls)][:]: -- tc = element[0] -- name = element[1] - if tc == 1: # TODO: Define constants for these - data = codec.decode_octet () - elif tc == 2: ---- 42,132 ---- - #=================================================================== - class ManagementMetadata: - -+ def parseSchema (self, cls, codec): -+ className = codec.decode_shortstr () -+ configCount = codec.decode_short () -+ instCount = codec.decode_short () -+ methodCount = codec.decode_short () -+ eventCount = codec.decode_short () - -+ configs = [] -+ insts = [] -+ methods = [] -+ events = [] - -+ configs.append (("id", 4, "", "", 1, 1, None, None, None, None, None)) -+ insts.append (("id", 4, None, None)) - -+ for idx in range (configCount): -+ ft = codec.decode_table () -+ name = ft["name"] -+ type = ft["type"] -+ access = ft["access"] -+ index = ft["index"] -+ unit = None -+ min = None -+ max = None -+ maxlen = None -+ desc = None - -+ for key, value in ft.items (): -+ if key == "unit": -+ unit = value -+ elif key == "min": -+ min = value -+ elif key == "max": -+ max = value -+ elif key == "maxlen": -+ maxlen = value -+ elif key == "desc": -+ desc = value -+ -+ config = (name, type, unit, desc, access, index, min, max, maxlen) -+ configs.append (config) -+ -+ for idx in range (instCount): -+ ft = codec.decode_table () -+ name = ft["name"] -+ type = ft["type"] -+ unit = None -+ desc = None -+ -+ for key, value in ft.items (): -+ if key == "unit": -+ unit = value -+ elif key == "desc": -+ desc = value -+ -+ inst = (name, type, unit, desc) -+ insts.append (inst) -+ - # TODO: Handle notification of schema change outbound -+ self.schema[(className,'C')] = configs -+ self.schema[(className,'I')] = insts -+ self.schema[(className,'M')] = methods -+ self.schema[(className,'E')] = events - -+ def parseContent (self, cls, codec): - if cls == 'C' and self.broker.config_cb == None: - return - if cls == 'I' and self.broker.inst_cb == None: - return - -+ className = codec.decode_shortstr () -+ -+ if (className,cls) not in self.schema: - return - - row = [] - timestamps = [] - -+ timestamps.append (codec.decode_longlong ()) # Current Time -+ timestamps.append (codec.decode_longlong ()) # Create Time -+ timestamps.append (codec.decode_longlong ()) # Delete Time - -+ for element in self.schema[(className,cls)][:]: -+ tc = element[1] -+ name = element[0] - if tc == 1: # TODO: Define constants for these - data = codec.decode_octet () - elif tc == 2: -*************** -*** 98,130 **** - data = codec.decode_octet () - elif tc == 6: - data = codec.decode_shortstr () - row.append ((name, data)) - - if cls == 'C': -- self.broker.config_cb[1] (self.broker.config_cb[0], oid, row, timestamps) -- if cls == 'I': -- self.broker.inst_cb[1] (self.broker.inst_cb[0], oid, row, timestamps) - - def parse (self, codec): -- try: -- opcode = chr (codec.decode_octet ()) -- except EOF: -- return 0 - -- cls = chr (codec.decode_octet ()) -- oid = codec.decode_short () -- len = codec.decode_long () -- -- if len < 8: -- raise ValueError ("parse error: value of length field too small") -- - if opcode == 'S': -- self.parseSchema (cls, oid, len, codec) - -- if opcode == 'C': -- self.parseContent (cls, oid, len, codec) - -- return 1 - - def __init__ (self, broker): - self.broker = broker ---- 139,167 ---- - data = codec.decode_octet () - elif tc == 6: - data = codec.decode_shortstr () -+ elif tc == 7: -+ data = codec.decode_longstr () -+ else: -+ raise ValueError ("Invalid type code: %d" % tc) - row.append ((name, data)) - - if cls == 'C': -+ self.broker.config_cb[1] (self.broker.config_cb[0], className, row, timestamps) -+ elif cls == 'I': -+ self.broker.inst_cb[1] (self.broker.inst_cb[0], className, row, timestamps) - - def parse (self, codec): -+ opcode = chr (codec.decode_octet ()) -+ cls = chr (codec.decode_octet ()) - - if opcode == 'S': -+ self.parseSchema (cls, codec) - -+ elif opcode == 'C': -+ self.parseContent (cls, codec) - -+ else: -+ raise ValueError ("Unknown opcode: %c" % opcode); - - def __init__ (self, broker): - self.broker = broker -*************** -*** 140,146 **** - #=================================================================== - class ManagedBroker: - -- exchange = "qpid.management" - - def checkHeader (self, codec): - octet = chr (codec.decode_octet ()) ---- 177,184 ---- - #=================================================================== - class ManagedBroker: - -+ mExchange = "qpid.management" -+ dExchange = "amq.direct" - - def checkHeader (self, codec): - octet = chr (codec.decode_octet ()) -*************** -*** 157,225 **** - return 0 - return 1 - -- def receive_cb (self, msg): - codec = Codec (StringIO (msg.content.body), self.spec) - - if self.checkHeader (codec) == 0: - raise ValueError ("outer header invalid"); - -- while self.metadata.parse (codec): -- pass - - msg.complete () - -- def __init__ (self, host = "localhost", port = 5672, -- username = "guest", password = "guest"): - -- self.spec = qpid.spec.load ("../specs/amqp.0-10-preview.xml") -- self.client = None -- self.channel = None -- self.queue = None -- self.qname = None -- self.metadata = ManagementMetadata (self) - - # Initialize the callback records - self.schema_cb = None - self.config_cb = None - self.inst_cb = None - - self.host = host - self.port = port - self.username = username - self.password = password - - def schemaListener (self, context, callback): - self.schema_cb = (context, callback) - - def configListener (self, context, callback): - self.config_cb = (context, callback) - - def instrumentationListener (self, context, callback): - self.inst_cb = (context, callback) - - def start (self): -- print "Connecting to broker", self.host - - try: - self.client = Client (self.host, self.port, self.spec) - self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) - self.channel = self.client.channel (1) -- response = self.channel.session_open (detached_lifetime=300) -- self.qname = "mgmt-" + base64.urlsafe_b64encode(response.session_id) - -- self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) -- self.channel.queue_bind (exchange=ManagedBroker.exchange, queue=self.qname, -- routing_key="mgmt") -- self.channel.message_subscribe (queue=self.qname, destination="dest") -- self.queue = self.client.queue ("dest") -- self.queue.listen (self.receive_cb) - -- self.channel.message_flow_mode (destination="dest", mode=1) -- self.channel.message_flow (destination="dest", unit=0, value=0xFFFFFFFF) -- self.channel.message_flow (destination="dest", unit=1, value=0xFFFFFFFF) - - except socket.error, e: - print "Socket Error Detected:", e[1] - raise - except: - raise ---- 195,335 ---- - return 0 - return 1 - -+ def publish_cb (self, msg): - codec = Codec (StringIO (msg.content.body), self.spec) - - if self.checkHeader (codec) == 0: - raise ValueError ("outer header invalid"); - -+ self.metadata.parse (codec) -+ msg.complete () - -+ def reply_cb (self, msg): -+ codec = Codec (StringIO (msg.content.body), self.spec) -+ methodId = codec.decode_long () -+ status = codec.decode_long () -+ sText = codec.decode_shortstr () -+ -+ args = {} -+ if status == 0: -+ args["sequence"] = codec.decode_long () -+ args["body"] = codec.decode_longstr () -+ -+ if self.method_cb != None: -+ self.method_cb[1] (self.method_cb[0], methodId, status, sText, args) -+ - msg.complete () - -+ def __init__ (self, -+ host = "localhost", -+ port = 5672, -+ username = "guest", -+ password = "guest", -+ specfile = "../specs/amqp.0-10-preview.xml"): - -+ self.spec = qpid.spec.load (specfile) -+ self.client = None -+ self.channel = None -+ self.queue = None -+ self.rqueue = None -+ self.qname = None -+ self.rqname = None -+ self.metadata = ManagementMetadata (self) -+ self.connected = 0 -+ self.lastConnectError = None - - # Initialize the callback records -+ self.status_cb = None - self.schema_cb = None - self.config_cb = None - self.inst_cb = None -+ self.method_cb = None - - self.host = host - self.port = port - self.username = username - self.password = password - -+ def statusListener (self, context, callback): -+ self.status_cb = (context, callback) -+ - def schemaListener (self, context, callback): - self.schema_cb = (context, callback) - - def configListener (self, context, callback): - self.config_cb = (context, callback) - -+ def methodListener (self, context, callback): -+ self.method_cb = (context, callback) -+ - def instrumentationListener (self, context, callback): - self.inst_cb = (context, callback) - -+ def method (self, methodId, objId, className, -+ methodName, args=None, packageName="qpid"): -+ codec = Codec (StringIO (), self.spec); -+ codec.encode_long (methodId) -+ codec.encode_longlong (objId) -+ codec.encode_shortstr (self.rqname) -+ -+ # TODO: Encode args according to schema -+ if methodName == "echo": -+ codec.encode_long (args["sequence"]) -+ codec.encode_longstr (args["body"]) -+ -+ msg = Content (codec.stream.getvalue ()) -+ msg["content_type"] = "application/octet-stream" -+ msg["routing_key"] = "method." + packageName + "." + className + "." + methodName -+ msg["reply_to"] = self.spec.struct ("reply_to") -+ self.channel.message_transfer (destination="qpid.management", content=msg) -+ -+ def isConnected (self): -+ return connected -+ - def start (self): -+ print "Connecting to broker %s:%d" % (self.host, self.port) - - try: - self.client = Client (self.host, self.port, self.spec) - self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) - self.channel = self.client.channel (1) -+ response = self.channel.session_open (detached_lifetime=10) -+ self.qname = "mgmt-" + base64.urlsafe_b64encode (response.session_id) -+ self.rqname = "reply-" + base64.urlsafe_b64encode (response.session_id) - -+ self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) -+ self.channel.queue_declare (queue=self.rqname, exclusive=1, auto_delete=1) -+ -+ self.channel.queue_bind (exchange=ManagedBroker.mExchange, queue=self.qname, -+ routing_key="mgmt.#") -+ self.channel.queue_bind (exchange=ManagedBroker.dExchange, queue=self.rqname, -+ routing_key=self.rqname) - -+ self.channel.message_subscribe (queue=self.qname, destination="mdest") -+ self.channel.message_subscribe (queue=self.rqname, destination="rdest") - -+ self.queue = self.client.queue ("mdest") -+ self.queue.listen (self.publish_cb) -+ -+ self.channel.message_flow_mode (destination="mdest", mode=1) -+ self.channel.message_flow (destination="mdest", unit=0, value=0xFFFFFFFF) -+ self.channel.message_flow (destination="mdest", unit=1, value=0xFFFFFFFF) -+ -+ self.rqueue = self.client.queue ("rdest") -+ self.rqueue.listen (self.reply_cb) -+ -+ self.channel.message_flow_mode (destination="rdest", mode=1) -+ self.channel.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) -+ self.channel.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) -+ -+ self.connected = 1 -+ - except socket.error, e: - print "Socket Error Detected:", e[1] -+ self.lastConnectError = e - raise - except: - raise -+ -+ def stop (self): -+ pass -- cgit v1.2.1 From a9687d84a6b50d57fbdcc734267b558f1770e390 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 24 Jan 2008 20:12:12 +0000 Subject: added imports to test module __init__.pys git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@614968 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/__init__.py | 4 + qpid/python/tests_0-10/__init__.py | 12 ++ qpid/python/tests_0-10/alternate-exchange.py | 179 --------------------------- qpid/python/tests_0-10/alternate_exchange.py | 179 +++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 179 deletions(-) delete mode 100644 qpid/python/tests_0-10/alternate-exchange.py create mode 100644 qpid/python/tests_0-10/alternate_exchange.py (limited to 'qpid/python') diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py index 9a09d2d04f..41dcc705e6 100644 --- a/qpid/python/tests/__init__.py +++ b/qpid/python/tests/__init__.py @@ -18,3 +18,7 @@ # specific language governing permissions and limitations # under the License. # + +from codec import * +from queue import * +from spec import * diff --git a/qpid/python/tests_0-10/__init__.py b/qpid/python/tests_0-10/__init__.py index 9a09d2d04f..fe96d9e122 100644 --- a/qpid/python/tests_0-10/__init__.py +++ b/qpid/python/tests_0-10/__init__.py @@ -18,3 +18,15 @@ # specific language governing permissions and limitations # under the License. # + +from alternate_exchange import * +from broker import * +from dtx import * +from example import * +from exchange import * +from execution import * +from message import * +from query import * +from queue import * +from testlib import * +from tx import * diff --git a/qpid/python/tests_0-10/alternate-exchange.py b/qpid/python/tests_0-10/alternate-exchange.py deleted file mode 100644 index 83f8d85811..0000000000 --- a/qpid/python/tests_0-10/alternate-exchange.py +++ /dev/null @@ -1,179 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class AlternateExchangeTests(TestBase): - """ - Tests for the new mechanism for message returns introduced in 0-10 - and available in 0-9 for preview - """ - - def test_unroutable(self): - """ - Test that unroutable messages are delivered to the alternate-exchange if specified - """ - channel = self.channel - #create an exchange with an alternate defined - channel.exchange_declare(exchange="secondary", type="fanout") - channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") - - #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages - channel.queue_declare(queue="returns", exclusive=True, auto_delete=True) - channel.queue_bind(queue="returns", exchange="secondary") - self.subscribe(destination="a", queue="returns") - returned = self.client.queue("a") - - #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages - channel.queue_declare(queue="processed", exclusive=True, auto_delete=True) - channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") - self.subscribe(destination="b", queue="processed") - processed = self.client.queue("b") - - #publish to the primary exchange - #...one message that makes it to the 'processed' queue: - channel.message_transfer(destination="primary", content=Content("Good", properties={'routing_key':"my-key"})) - #...and one that does not: - channel.message_transfer(destination="primary", content=Content("Bad", properties={'routing_key':"unused-key"})) - - #delete the exchanges - channel.exchange_delete(exchange="primary") - channel.exchange_delete(exchange="secondary") - - #verify behaviour - self.assertEqual("Good", processed.get(timeout=1).content.body) - self.assertEqual("Bad", returned.get(timeout=1).content.body) - self.assertEmpty(processed) - self.assertEmpty(returned) - - def test_queue_delete(self): - """ - Test that messages in a queue being deleted are delivered to the alternate-exchange if specified - """ - channel = self.channel - #set up a 'dead letter queue': - channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="deleted", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="dlq", queue="deleted") - self.subscribe(destination="dlq", queue="deleted") - dlq = self.client.queue("dlq") - - #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="delete-me", alternate_exchange="dlq") - #send it some messages: - channel.message_transfer(content=Content("One", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("Two", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("Three", properties={'routing_key':"delete-me"})) - #delete it: - channel.queue_delete(queue="delete-me") - #delete the dlq exchange: - channel.exchange_delete(exchange="dlq") - - #check the messages were delivered to the dlq: - self.assertEqual("One", dlq.get(timeout=1).content.body) - self.assertEqual("Two", dlq.get(timeout=1).content.body) - self.assertEqual("Three", dlq.get(timeout=1).content.body) - self.assertEmpty(dlq) - - - def test_immediate(self): - """ - Test that messages in a queue being deleted are delivered to the alternate-exchange if specified - """ - channel = self.channel - #set up a 'dead letter queue': - channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="immediate", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="dlq", queue="immediate") - self.subscribe(destination="dlq", queue="immediate") - dlq = self.client.queue("dlq") - - #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True, auto_delete=True) - #send it some messages: - #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK - channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) - - #check the messages were delivered to the dlq: - self.assertEqual("no one wants me", dlq.get(timeout=1).content.body) - self.assertEmpty(dlq) - - #cleanup: - channel.queue_delete(queue="no-consumers") - channel.exchange_delete(exchange="dlq") - - - def test_delete_while_used_by_queue(self): - """ - Ensure an exchange still in use as an alternate-exchange for a - queue can't be deleted - """ - channel = self.channel - channel.exchange_declare(exchange="alternate", type="fanout") - channel.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") - try: - channel.exchange_delete(exchange="alternate") - self.fail("Expected deletion of in-use alternate-exchange to fail") - except Closed, e: - #cleanup: - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="alternate") - channel.session_close() - other.close() - - self.assertConnectionException(530, e.args[0]) - - - - def test_delete_while_used_by_exchange(self): - """ - Ensure an exchange still in use as an alternate-exchange for - another exchange can't be deleted - """ - channel = self.channel - channel.exchange_declare(exchange="alternate", type="fanout") - channel.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") - try: - channel.exchange_delete(exchange="alternate") - #cleanup: - channel.exchange_delete(exchange="e") - self.fail("Expected deletion of in-use alternate-exchange to fail") - except Closed, e: - #cleanup: - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="e") - channel.exchange_delete(exchange="alternate") - channel.session_close() - other.close() - - self.assertConnectionException(530, e.args[0]) - - - def assertEmpty(self, queue): - try: - msg = queue.get(timeout=1) - self.fail("Queue not empty: " + msg) - except Empty: None - diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py new file mode 100644 index 0000000000..83f8d85811 --- /dev/null +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -0,0 +1,179 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class AlternateExchangeTests(TestBase): + """ + Tests for the new mechanism for message returns introduced in 0-10 + and available in 0-9 for preview + """ + + def test_unroutable(self): + """ + Test that unroutable messages are delivered to the alternate-exchange if specified + """ + channel = self.channel + #create an exchange with an alternate defined + channel.exchange_declare(exchange="secondary", type="fanout") + channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") + + #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages + channel.queue_declare(queue="returns", exclusive=True, auto_delete=True) + channel.queue_bind(queue="returns", exchange="secondary") + self.subscribe(destination="a", queue="returns") + returned = self.client.queue("a") + + #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages + channel.queue_declare(queue="processed", exclusive=True, auto_delete=True) + channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") + self.subscribe(destination="b", queue="processed") + processed = self.client.queue("b") + + #publish to the primary exchange + #...one message that makes it to the 'processed' queue: + channel.message_transfer(destination="primary", content=Content("Good", properties={'routing_key':"my-key"})) + #...and one that does not: + channel.message_transfer(destination="primary", content=Content("Bad", properties={'routing_key':"unused-key"})) + + #delete the exchanges + channel.exchange_delete(exchange="primary") + channel.exchange_delete(exchange="secondary") + + #verify behaviour + self.assertEqual("Good", processed.get(timeout=1).content.body) + self.assertEqual("Bad", returned.get(timeout=1).content.body) + self.assertEmpty(processed) + self.assertEmpty(returned) + + def test_queue_delete(self): + """ + Test that messages in a queue being deleted are delivered to the alternate-exchange if specified + """ + channel = self.channel + #set up a 'dead letter queue': + channel.exchange_declare(exchange="dlq", type="fanout") + channel.queue_declare(queue="deleted", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="dlq", queue="deleted") + self.subscribe(destination="dlq", queue="deleted") + dlq = self.client.queue("dlq") + + #create a queue using the dlq as its alternate exchange: + channel.queue_declare(queue="delete-me", alternate_exchange="dlq") + #send it some messages: + channel.message_transfer(content=Content("One", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("Two", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("Three", properties={'routing_key':"delete-me"})) + #delete it: + channel.queue_delete(queue="delete-me") + #delete the dlq exchange: + channel.exchange_delete(exchange="dlq") + + #check the messages were delivered to the dlq: + self.assertEqual("One", dlq.get(timeout=1).content.body) + self.assertEqual("Two", dlq.get(timeout=1).content.body) + self.assertEqual("Three", dlq.get(timeout=1).content.body) + self.assertEmpty(dlq) + + + def test_immediate(self): + """ + Test that messages in a queue being deleted are delivered to the alternate-exchange if specified + """ + channel = self.channel + #set up a 'dead letter queue': + channel.exchange_declare(exchange="dlq", type="fanout") + channel.queue_declare(queue="immediate", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="dlq", queue="immediate") + self.subscribe(destination="dlq", queue="immediate") + dlq = self.client.queue("dlq") + + #create a queue using the dlq as its alternate exchange: + channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True, auto_delete=True) + #send it some messages: + #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK + channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) + + #check the messages were delivered to the dlq: + self.assertEqual("no one wants me", dlq.get(timeout=1).content.body) + self.assertEmpty(dlq) + + #cleanup: + channel.queue_delete(queue="no-consumers") + channel.exchange_delete(exchange="dlq") + + + def test_delete_while_used_by_queue(self): + """ + Ensure an exchange still in use as an alternate-exchange for a + queue can't be deleted + """ + channel = self.channel + channel.exchange_declare(exchange="alternate", type="fanout") + channel.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") + try: + channel.exchange_delete(exchange="alternate") + self.fail("Expected deletion of in-use alternate-exchange to fail") + except Closed, e: + #cleanup: + other = self.connect() + channel = other.channel(1) + channel.session_open() + channel.exchange_delete(exchange="alternate") + channel.session_close() + other.close() + + self.assertConnectionException(530, e.args[0]) + + + + def test_delete_while_used_by_exchange(self): + """ + Ensure an exchange still in use as an alternate-exchange for + another exchange can't be deleted + """ + channel = self.channel + channel.exchange_declare(exchange="alternate", type="fanout") + channel.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") + try: + channel.exchange_delete(exchange="alternate") + #cleanup: + channel.exchange_delete(exchange="e") + self.fail("Expected deletion of in-use alternate-exchange to fail") + except Closed, e: + #cleanup: + other = self.connect() + channel = other.channel(1) + channel.session_open() + channel.exchange_delete(exchange="e") + channel.exchange_delete(exchange="alternate") + channel.session_close() + other.close() + + self.assertConnectionException(530, e.args[0]) + + + def assertEmpty(self, queue): + try: + msg = queue.get(timeout=1) + self.fail("Queue not empty: " + msg) + except Empty: None + -- cgit v1.2.1 From 60798b8d43a29d16e62918f49015a406faea2ab9 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 28 Jan 2008 15:31:48 +0000 Subject: Changd name of alternate_exchange. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@615912 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 80e3d44935..77031cad08 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,6 +1,6 @@ tests.codec.FieldTableTestCase.test_field_table_decode tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair -tests_0-10.alternate-exchange.AlternateExchangeTests.test_immediate +tests_0-10.alternate_exchange.AlternateExchangeTests.test_immediate tests_0-10.broker.BrokerTests.test_closed_channel -- cgit v1.2.1 From 56be271a9b61a7baec92b81ba84b9e9e2c51255d Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 8 Feb 2008 15:01:30 +0000 Subject: Refactored verify scripts, added verify for python Examples. To verify an example: /bin/verify See comments in bin/verify for more details. Changes: - Each example dir has its own verify script and verify.in. - Added sys.stdout.flush() to som python examples so verify can tell when they're ready. - Made python examples svn:executable. - C++ examples/Makefile.am runs python examples git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@619903 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 5 ++- qpid/python/examples/direct/direct_consumer.py | 5 ++- qpid/python/examples/direct/direct_producer.py | 5 ++- qpid/python/examples/direct/listener.py | 5 ++- qpid/python/examples/direct/verify | 3 ++ qpid/python/examples/direct/verify.in | 14 +++++++ qpid/python/examples/fanout/declare_queues.py | 5 ++- qpid/python/examples/fanout/fanout_consumer.py | 5 ++- qpid/python/examples/fanout/fanout_producer.py | 5 ++- qpid/python/examples/fanout/listener.py | 5 ++- qpid/python/examples/fanout/verify | 3 ++ qpid/python/examples/fanout/verify.in | 14 +++++++ qpid/python/examples/pubsub/topic_publisher.py | 6 +-- qpid/python/examples/pubsub/topic_subscriber.py | 9 +++-- qpid/python/examples/pubsub/verify | 4 ++ qpid/python/examples/pubsub/verify.in | 51 +++++++++++++++++++++++++ qpid/python/examples/request-response/client.py | 5 ++- qpid/python/examples/request-response/server.py | 6 ++- qpid/python/examples/request-response/verify | 5 +++ qpid/python/examples/request-response/verify.in | 14 +++++++ 20 files changed, 147 insertions(+), 27 deletions(-) mode change 100644 => 100755 qpid/python/examples/direct/declare_queues.py mode change 100644 => 100755 qpid/python/examples/direct/direct_consumer.py mode change 100644 => 100755 qpid/python/examples/direct/direct_producer.py mode change 100644 => 100755 qpid/python/examples/direct/listener.py create mode 100644 qpid/python/examples/direct/verify create mode 100644 qpid/python/examples/direct/verify.in mode change 100644 => 100755 qpid/python/examples/fanout/declare_queues.py mode change 100644 => 100755 qpid/python/examples/fanout/fanout_consumer.py mode change 100644 => 100755 qpid/python/examples/fanout/fanout_producer.py mode change 100644 => 100755 qpid/python/examples/fanout/listener.py create mode 100644 qpid/python/examples/fanout/verify create mode 100644 qpid/python/examples/fanout/verify.in mode change 100644 => 100755 qpid/python/examples/pubsub/topic_publisher.py mode change 100644 => 100755 qpid/python/examples/pubsub/topic_subscriber.py create mode 100644 qpid/python/examples/pubsub/verify create mode 100644 qpid/python/examples/pubsub/verify.in mode change 100644 => 100755 qpid/python/examples/request-response/client.py mode change 100644 => 100755 qpid/python/examples/request-response/server.py create mode 100644 qpid/python/examples/request-response/verify create mode 100644 qpid/python/examples/request-response/verify.in (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py old mode 100644 new mode 100755 index 0160f66a1d..f39f0c3349 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -9,6 +9,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -17,8 +18,8 @@ from qpid.queue import Empty # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py old mode 100644 new mode 100755 index 38b1ba30a0..85c1db0a93 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -7,6 +7,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -16,8 +17,8 @@ from qpid.queue import Empty # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py old mode 100644 new mode 100755 index ff662477ce..2c07bfd8e7 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -7,6 +7,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -15,8 +16,8 @@ from qpid.queue import Empty # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py old mode 100644 new mode 100755 index 8324cc76a6..2dbd502fa0 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -8,6 +8,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -40,8 +41,8 @@ class Receiver: # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/direct/verify b/qpid/python/examples/direct/verify new file mode 100644 index 0000000000..01d81a18a1 --- /dev/null +++ b/qpid/python/examples/direct/verify @@ -0,0 +1,3 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +clients ./declare_queues.py ./direct_producer.py ./direct_consumer.py +outputs ./declare_queues.py.out ./direct_producer.py.out ./direct_consumer.py.out diff --git a/qpid/python/examples/direct/verify.in b/qpid/python/examples/direct/verify.in new file mode 100644 index 0000000000..e0dca33039 --- /dev/null +++ b/qpid/python/examples/direct/verify.in @@ -0,0 +1,14 @@ +==== ./declare_queues.py.out +==== ./direct_producer.py.out +==== ./direct_consumer.py.out +message 0 +message 1 +message 2 +message 3 +message 4 +message 5 +message 6 +message 7 +message 8 +message 9 +That's all, folks! diff --git a/qpid/python/examples/fanout/declare_queues.py b/qpid/python/examples/fanout/declare_queues.py old mode 100644 new mode 100755 index 8840ea38e1..52f23f4f9a --- a/qpid/python/examples/fanout/declare_queues.py +++ b/qpid/python/examples/fanout/declare_queues.py @@ -9,6 +9,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -17,8 +18,8 @@ from qpid.queue import Empty # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py old mode 100644 new mode 100755 index 8d852ca753..b91ea35c0d --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -7,6 +7,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -16,8 +17,8 @@ from qpid.queue import Empty # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py old mode 100644 new mode 100755 index d9d8f454c8..9864c776c1 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -7,6 +7,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -15,8 +16,8 @@ from qpid.queue import Empty # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py old mode 100644 new mode 100755 index 09dd7ddb80..8997c3698f --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -7,6 +7,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -39,8 +40,8 @@ class Receiver: # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/fanout/verify b/qpid/python/examples/fanout/verify new file mode 100644 index 0000000000..f136ccd39b --- /dev/null +++ b/qpid/python/examples/fanout/verify @@ -0,0 +1,3 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +clients ./declare_queues.py ./fanout_producer.py ./fanout_consumer.py +outputs ./declare_queues.py.out ./fanout_producer.py.out ./fanout_consumer.py.out diff --git a/qpid/python/examples/fanout/verify.in b/qpid/python/examples/fanout/verify.in new file mode 100644 index 0000000000..c625c30773 --- /dev/null +++ b/qpid/python/examples/fanout/verify.in @@ -0,0 +1,14 @@ +==== ./declare_queues.py.out +==== ./fanout_producer.py.out +==== ./fanout_consumer.py.out +message 0 +message 1 +message 2 +message 3 +message 4 +message 5 +message 6 +message 7 +message 8 +message 9 +That's all, folks! diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py old mode 100644 new mode 100755 index 1ff983b315..e302d58ad4 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -8,6 +8,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -15,9 +16,8 @@ from qpid.queue import Empty #----- Initialization ----------------------------------- # Set parameters for login. - -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py old mode 100644 new mode 100755 index 52ec67b77c..a5c05ba177 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -7,7 +7,7 @@ """ import base64 - +import sys import qpid from qpid.client import Client from qpid.content import Content @@ -60,8 +60,8 @@ def dump_queue(client, queue_name): # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" @@ -108,7 +108,8 @@ session.queue_bind(exchange="amq.topic", queue=europe, routing_key="control") # Remind the user to start the topic producer -print "Queues create - please start the topic producer" +print "Queues created - please start the topic producer" +sys.stdout.flush() # Call dump_queue to print messages from each queue diff --git a/qpid/python/examples/pubsub/verify b/qpid/python/examples/pubsub/verify new file mode 100644 index 0000000000..bef233b4ff --- /dev/null +++ b/qpid/python/examples/pubsub/verify @@ -0,0 +1,4 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Queues created" ./topic_subscriber.py +clients ./topic_publisher.py +outputs ./topic_publisher.py.out "topic_subscriber.py.out | remove_uuid64 | sort" diff --git a/qpid/python/examples/pubsub/verify.in b/qpid/python/examples/pubsub/verify.in new file mode 100644 index 0000000000..19dcf88312 --- /dev/null +++ b/qpid/python/examples/pubsub/verify.in @@ -0,0 +1,51 @@ +==== ./topic_publisher.py.out +==== topic_subscriber.py.out | remove_uuid64 | sort +message 0 +message 0 +message 0 +message 0 +message 0 +message 0 +message 0 +message 0 +message 1 +message 1 +message 1 +message 1 +message 1 +message 1 +message 1 +message 1 +message 2 +message 2 +message 2 +message 2 +message 2 +message 2 +message 2 +message 2 +message 3 +message 3 +message 3 +message 3 +message 3 +message 3 +message 3 +message 3 +message 4 +message 4 +message 4 +message 4 +message 4 +message 4 +message 4 +message 4 +Messages queue: europe +Messages queue: news +Messages queue: usa +Messages queue: weather +Queues created - please start the topic producer +That's all, folks! +That's all, folks! +That's all, folks! +That's all, folks! diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py old mode 100644 new mode 100755 index e218acce3d..6561bb6fee --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -9,6 +9,7 @@ import base64 import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -59,8 +60,8 @@ def dump_queue(client, queue_name): # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py old mode 100644 new mode 100755 index 2730768a91..04b147d003 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -6,6 +6,7 @@ """ import qpid +import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty @@ -27,8 +28,8 @@ def respond(session, request): # Set parameters for login -host="127.0.0.1" -port=5672 +host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" +port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" @@ -61,6 +62,7 @@ session.message_flow(dest, 1, 0xFFFFFFFF) print "Request server running - run your client now." print "(Times out after 100 seconds ...)" +sys.stdout.flush() # Respond to each request diff --git a/qpid/python/examples/request-response/verify b/qpid/python/examples/request-response/verify new file mode 100644 index 0000000000..2a2d479077 --- /dev/null +++ b/qpid/python/examples/request-response/verify @@ -0,0 +1,5 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Request server running" ./server.py +clients ./client.py +kill %% # Must kill the server. +outputs "./client.py.out | remove_uuid64" " server.py.out | remove_uuid64" diff --git a/qpid/python/examples/request-response/verify.in b/qpid/python/examples/request-response/verify.in new file mode 100644 index 0000000000..c02a423bcb --- /dev/null +++ b/qpid/python/examples/request-response/verify.in @@ -0,0 +1,14 @@ +==== ./client.py.out | remove_uuid64 +Request: Twas brilling, and the slithy toves +Request: Did gyre and gimble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Messages queue: ReplyTo: +Response: TWAS BRILLING, AND THE SLITHY TOVES +Response: DID GYRE AND GIMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +No more messages! +==== server.py.out | remove_uuid64 +Request server running - run your client now. +(Times out after 100 seconds ...) -- cgit v1.2.1 From 1d7c9ae37f99102dd86783f481e2e7930ed56267 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 8 Feb 2008 22:05:50 +0000 Subject: whitespace cleanup git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@620011 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 68 +++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 9ec1cc270c..c251e6aca0 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -6,9 +6,9 @@ # 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 @@ -47,7 +47,7 @@ class MessageTests(TestBase): msg = included.get(timeout=1) self.assertEqual("consume_no_local", msg.content.body) try: - excluded.get(timeout=1) + excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") except Empty: None @@ -59,9 +59,9 @@ class MessageTests(TestBase): could be left on the queue, possibly never being consumed (this is the case for example in the qpid JMS mapping of topics). This test excercises a Qpid C++ broker hack that - deletes such messages. + deletes such messages. """ - + channel = self.channel #setup: channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) @@ -84,7 +84,7 @@ class MessageTests(TestBase): msg = excluded.get(timeout=1) self.assertEqual("foreign", msg.content.body) try: - excluded.get(timeout=1) + excluded.get(timeout=1) self.fail("Received extra message") except Empty: None #check queue is empty @@ -107,7 +107,7 @@ class MessageTests(TestBase): except Closed, e: self.assertChannelException(403, e.args[0]) - #open new channel and cleanup last consumer: + #open new channel and cleanup last consumer: channel = self.client.channel(2) channel.session_open() @@ -173,7 +173,7 @@ class MessageTests(TestBase): msg = myqueue.get(timeout=1) self.assertEqual("One", msg.content.body) try: - msg = myqueue.get(timeout=1) + msg = myqueue.get(timeout=1) self.fail("Got message after cancellation: " + msg) except Empty: None @@ -188,7 +188,7 @@ class MessageTests(TestBase): """ channel = self.channel channel.queue_declare(queue="test-ack-queue", exclusive=True, auto_delete=True) - + self.subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") @@ -197,13 +197,13 @@ class MessageTests(TestBase): channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Three")) channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Four")) channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Five")) - + msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) msg3 = queue.get(timeout=1) msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - + self.assertEqual("One", msg1.content.body) self.assertEqual("Two", msg2.content.body) self.assertEqual("Three", msg3.content.body) @@ -214,10 +214,10 @@ class MessageTests(TestBase): msg4.complete(cumulative=False) channel.message_recover(requeue=False) - + msg3b = queue.get(timeout=1) msg5b = queue.get(timeout=1) - + self.assertEqual("Three", msg3b.content.body) self.assertEqual("Five", msg5b.content.body) @@ -236,7 +236,7 @@ class MessageTests(TestBase): channel.queue_bind(exchange="amq.fanout", queue="queue-a") channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) channel.queue_bind(exchange="amq.fanout", queue="queue-b") - + self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) self.subscribe(queue="queue-b", destination="confirmed", confirm_mode=0) confirmed = self.client.queue("confirmed") @@ -246,10 +246,10 @@ class MessageTests(TestBase): for d in data: channel.message_transfer(destination="amq.fanout", content=Content(body=d)) - for q in [confirmed, unconfirmed]: + for q in [confirmed, unconfirmed]: for d in data: self.assertEqual(d, q.get(timeout=1).content.body) - self.assertEmpty(q) + self.assertEmpty(q) channel.message_recover(requeue=False) @@ -265,7 +265,7 @@ class MessageTests(TestBase): data.remove(msg.content.body) msg.complete(cumulative=False) channel.message_recover(requeue=False) - + def test_recover_requeue(self): """ @@ -273,7 +273,7 @@ class MessageTests(TestBase): """ channel = self.channel channel.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) - + self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) queue = self.client.queue("consumer_tag") @@ -282,13 +282,13 @@ class MessageTests(TestBase): channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) - + msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) msg3 = queue.get(timeout=1) msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - + self.assertEqual("One", msg1.content.body) self.assertEqual("Two", msg2.content.body) self.assertEqual("Three", msg3.content.body) @@ -307,10 +307,10 @@ class MessageTests(TestBase): self.subscribe(queue="test-requeue", destination="consumer_tag") queue2 = self.client.queue("consumer_tag") - + msg3b = queue2.get(timeout=1) msg5b = queue2.get(timeout=1) - + self.assertEqual("Three", msg3b.content.body) self.assertEqual("Five", msg5b.content.body) @@ -327,8 +327,8 @@ class MessageTests(TestBase): extra = queue.get(timeout=1) self.fail("Got unexpected message in original queue: " + extra.content.body) except Empty: None - - + + def test_qos_prefetch_count(self): """ Test that the prefetch count specified is honoured @@ -370,7 +370,7 @@ class MessageTests(TestBase): except Empty: None - + def test_qos_prefetch_size(self): """ Test that the prefetch size specified is honoured @@ -448,7 +448,7 @@ class MessageTests(TestBase): #send batch of messages to queue for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) - + #set message credit to finite amount (less than enough for all messages) channel.message_flow(unit = 0, value = 5, destination = "c") #set infinite byte credit @@ -458,7 +458,7 @@ class MessageTests(TestBase): for i in range(1, 6): self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) self.assertEmpty(q) - + #increase credit again and check more are received for i in range(6, 11): channel.message_flow(unit = 0, value = 1, destination = "c") @@ -512,7 +512,7 @@ class MessageTests(TestBase): #send batch of messages to queue for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) - + #set message credit to finite amount (less than enough for all messages) channel.message_flow(unit = 0, value = 5, destination = "c") #set infinite byte credit @@ -523,7 +523,7 @@ class MessageTests(TestBase): msg = q.get(timeout = 1) self.assertDataEquals(channel, msg, "Message %d" % i) self.assertEmpty(q) - + #acknowledge messages and check more are received msg.complete(cumulative=True) for i in range(6, 11): @@ -560,7 +560,7 @@ class MessageTests(TestBase): msgs.append(msg) self.assertDataEquals(channel, msg, "abcdefgh") self.assertEmpty(q) - + #ack each message individually and check more are received for i in range(5): msg = msgs.pop() @@ -650,7 +650,7 @@ class MessageTests(TestBase): channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") queue = self.client.queue("a") first = queue.get(timeout = 1) - for i in range (2, 10): + for i in range (2, 10): self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) last = queue.get(timeout = 1) self.assertEmpty(queue) @@ -672,7 +672,7 @@ class MessageTests(TestBase): channel.message_flow(unit = 0, value = 10, destination = "a") channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") queue = self.client.queue("a") - for i in range (1, 11): + for i in range (1, 11): self.assertEquals("message %s" % (i), queue.get(timeout = 1).content.body) self.assertEmpty(queue) @@ -683,14 +683,14 @@ class MessageTests(TestBase): self.assertEmpty(queue) def test_subscribe_not_acquired_2(self): - channel = self.channel + channel = self.channel #publish some messages self.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) - #consume some of them + #consume some of them channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) channel.message_flow_mode(mode = 0, destination = "a") channel.message_flow(unit = 0, value = 5, destination = "a") -- cgit v1.2.1 From bcc0ea7084a7a6a7c6fc983c6094d6c70c244e2c Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Mon, 11 Feb 2008 21:43:32 +0000 Subject: Fix errors in verify scripts. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@620619 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/verify.in | 6 +++--- qpid/python/examples/fanout/verify.in | 6 +++--- qpid/python/examples/pubsub/verify.in | 2 +- qpid/python/examples/request-response/verify.in | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/verify.in b/qpid/python/examples/direct/verify.in index e0dca33039..5e691619d9 100644 --- a/qpid/python/examples/direct/verify.in +++ b/qpid/python/examples/direct/verify.in @@ -1,6 +1,6 @@ -==== ./declare_queues.py.out -==== ./direct_producer.py.out -==== ./direct_consumer.py.out +==== declare_queues.py.out +==== direct_producer.py.out +==== direct_consumer.py.out message 0 message 1 message 2 diff --git a/qpid/python/examples/fanout/verify.in b/qpid/python/examples/fanout/verify.in index c625c30773..a5f57f0b4b 100644 --- a/qpid/python/examples/fanout/verify.in +++ b/qpid/python/examples/fanout/verify.in @@ -1,6 +1,6 @@ -==== ./declare_queues.py.out -==== ./fanout_producer.py.out -==== ./fanout_consumer.py.out +==== declare_queues.py.out +==== fanout_producer.py.out +==== fanout_consumer.py.out message 0 message 1 message 2 diff --git a/qpid/python/examples/pubsub/verify.in b/qpid/python/examples/pubsub/verify.in index 19dcf88312..69de08d17c 100644 --- a/qpid/python/examples/pubsub/verify.in +++ b/qpid/python/examples/pubsub/verify.in @@ -1,4 +1,4 @@ -==== ./topic_publisher.py.out +==== topic_publisher.py.out ==== topic_subscriber.py.out | remove_uuid64 | sort message 0 message 0 diff --git a/qpid/python/examples/request-response/verify.in b/qpid/python/examples/request-response/verify.in index c02a423bcb..f681253b3c 100644 --- a/qpid/python/examples/request-response/verify.in +++ b/qpid/python/examples/request-response/verify.in @@ -1,4 +1,4 @@ -==== ./client.py.out | remove_uuid64 +==== client.py.out | remove_uuid64 Request: Twas brilling, and the slithy toves Request: Did gyre and gimble in the wabe. Request: All mimsy were the borogroves, @@ -9,6 +9,6 @@ Response: DID GYRE AND GIMBLE IN THE WABE. Response: ALL MIMSY WERE THE BOROGROVES, Response: AND THE MOME RATHS OUTGRABE. No more messages! -==== server.py.out | remove_uuid64 +==== server.py.out | remove_uuid64 Request server running - run your client now. (Times out after 100 seconds ...) -- cgit v1.2.1 From 68b90de9e73c4e30a7a3864e1c46843937341857 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 14 Feb 2008 11:49:17 +0000 Subject: Fixed bug in browsing that failed to deal correctly with 'gaps' in message sequence. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@627718 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index c251e6aca0..65aab2870f 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -720,6 +720,49 @@ class MessageTests(TestBase): #check all 'browsed' messages are still on the queue self.assertEqual(5, channel.queue_query(queue="q").message_count) + def test_subscribe_not_acquired_3(self): + channel = self.channel + + #publish some messages + self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + + #create a not-acquired subscriber + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + channel.message_flow(unit = 0, value = 10, destination = "a") + + #browse through messages + queue = self.client.queue("a") + for i in range(1, 11): + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + if (i % 2): + #try to acquire every second message + channel.message_acquire([msg.command_id, msg.command_id]) + #check that acquire succeeds + response = channel.control_queue.get(timeout=1) + self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) + msg.complete() + self.assertEmpty(queue) + + #create a second not-acquired subscriber + channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + channel.message_flow(unit = 0, value = 1, destination = "b") + #check it gets those not consumed + queue = self.client.queue("b") + for i in [2,4,6,8,10]: + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + msg.complete() + channel.message_flow(unit = 0, value = 1, destination = "b") + self.assertEmpty(queue) + + #check all 'browsed' messages are still on the queue + self.assertEqual(5, channel.queue_query(queue="q").message_count) + def test_no_size(self): self.queue_declare(queue = "q", exclusive=True, auto_delete=True) -- cgit v1.2.1 From 5034a2088eb20620225d2dfd4c2ae40b42b4c57d Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 15 Feb 2008 21:00:44 +0000 Subject: Updated c++ and python fanout examples and verify scripts. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@628169 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/fanout/declare_queues.py | 52 --------------- qpid/python/examples/fanout/fanout_consumer.py | 92 ++++++++++++++++---------- qpid/python/examples/fanout/verify | 6 +- qpid/python/examples/fanout/verify.in | 43 ++++++++---- 4 files changed, 91 insertions(+), 102 deletions(-) delete mode 100755 qpid/python/examples/fanout/declare_queues.py (limited to 'qpid/python') diff --git a/qpid/python/examples/fanout/declare_queues.py b/qpid/python/examples/fanout/declare_queues.py deleted file mode 100755 index 52f23f4f9a..0000000000 --- a/qpid/python/examples/fanout/declare_queues.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -""" - declare_queues.py - - Creates and binds a queue on an AMQP direct exchange. - - All messages using the routing key "routing_key" are - sent to the queue named "message_queue". -""" - -import qpid -import sys -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty - -#----- Initialization ----------------------------------- - -# Set parameters for login - -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" -user="guest" -password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -session = client.session() -session.session_open() - -#----- Create a queue ------------------------------------- - -# queue_declare() creates an AMQP queue, which is held -# on the broker. Published messages are sent to the AMQP queue, -# from which messages are delivered to consumers. -# -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to -# the AMQP queue named "message_queue". - -session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.fanout", queue="message_queue") - -#----- Cleanup --------------------------------------------- - -# Clean up before exiting so there are no open threads. - -session.session_close() diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index b91ea35c0d..ef24bf35b2 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -5,13 +5,57 @@ This AMQP client reads messages from a message queue named "message_queue". """ - +import base64 import qpid import sys from qpid.client import Client from qpid.content import Content from qpid.queue import Empty +#----- Functions ------------------------------------------- + +def dump_queue(client, queue_name): + + print "Messages queue: " + queue_name + + consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag + queue = client.queue(consumer_tag) + + # Call message_subscribe() to tell the broker to deliver messages + # from the AMQP queue to a local client queue. The broker will + # start delivering messages as soon as message_subscribe() is called. + + session.message_subscribe(queue=queue_name, destination=consumer_tag) + session.message_flow(consumer_tag, 0, 0xFFFFFFFF) + session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + + print "Subscribed to queue " + queue_name + sys.stdout.flush() + + message = 0 + + while True: + try: + message = queue.get(timeout=10) + content = message.content.body + print "Response: " + content + except Empty: + print "No more messages!" + break + except: + print "Unexpected exception!" + break + + + # Messages are not removed from the queue until they + # are acknowledged. Using cumulative=True, all messages + # in the session up to and including the one identified + # by the delivery tag are acknowledged. This is more efficient, + # because there are fewer network round-trips. + + if message != 0: + message.complete(cumulative=True) + #----- Initialization -------------------------------------- @@ -29,44 +73,22 @@ client = Client(host, port, qpid.spec.load(amqp_spec)) client.start({"LOGIN": user, "PASSWORD": password}) session = client.session() -session.session_open() - -#----- Read from queue -------------------------------------------- - -# Now let's create a local client queue and tell it to read -# incoming messages. - -# The consumer tag identifies the client-side queue. - -consumer_tag = "consumer1" -queue = client.queue(consumer_tag) - -# Call message_subscribe() to tell the broker to deliver messages -# from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_subscribe() is called. - -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - -# Initialize 'final' and 'content', variables used to identify the last message. +session_info = session.session_open() +session_id = session_info.session_id -final = "That's all, folks!" # In a message body, signals the last message -content = "" # Content of the last message read +#----- Main Body -- ---------------------------------------- -message = None -while content != final: - message = queue.get(timeout=10) - content = message.content.body - print content +# Make a unique queue name for my queue from the session ID. +my_queue = base64.urlsafe_b64encode(session_id) +session.queue_declare(queue=my_queue) -# Messages are not removed from the queue until they are -# acknowledged. Using cumulative=True, all messages from the session -# up to and including the one identified by the delivery tag are -# acknowledged. This is more efficient, because there are fewer -# network round-trips. +# Bind my queue to the fanout exchange. No routing key is required +# the fanout exchange copies messages unconditionally to every +# bound queue +session.queue_bind(queue=my_queue, exchange="amq.fanout") -message.complete(cumulative=True) +# Dump the messages on the queue. +dump_queue(client, my_queue) #----- Cleanup ------------------------------------------------ diff --git a/qpid/python/examples/fanout/verify b/qpid/python/examples/fanout/verify index f136ccd39b..7650853e11 100644 --- a/qpid/python/examples/fanout/verify +++ b/qpid/python/examples/fanout/verify @@ -1,3 +1,5 @@ # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify -clients ./declare_queues.py ./fanout_producer.py ./fanout_consumer.py -outputs ./declare_queues.py.out ./fanout_producer.py.out ./fanout_consumer.py.out +background "Subscribed" ./fanout_consumer.py +background "Subscribed" ./fanout_consumer.py +clients ./fanout_producer.py +outputs ./fanout_producer.py.out "./fanout_consumer.py.out | remove_uuid64" "./fanout_consumer.pyX.out | remove_uuid64" diff --git a/qpid/python/examples/fanout/verify.in b/qpid/python/examples/fanout/verify.in index a5f57f0b4b..d5067b3850 100644 --- a/qpid/python/examples/fanout/verify.in +++ b/qpid/python/examples/fanout/verify.in @@ -1,14 +1,31 @@ -==== declare_queues.py.out ==== fanout_producer.py.out -==== fanout_consumer.py.out -message 0 -message 1 -message 2 -message 3 -message 4 -message 5 -message 6 -message 7 -message 8 -message 9 -That's all, folks! +==== fanout_consumer.py.out | remove_uuid64 +Messages queue: +Subscribed to queue +Response: message 0 +Response: message 1 +Response: message 2 +Response: message 3 +Response: message 4 +Response: message 5 +Response: message 6 +Response: message 7 +Response: message 8 +Response: message 9 +Response: That's all, folks! +No more messages! +==== fanout_consumer.pyX.out | remove_uuid64 +Messages queue: +Subscribed to queue +Response: message 0 +Response: message 1 +Response: message 2 +Response: message 3 +Response: message 4 +Response: message 5 +Response: message 6 +Response: message 7 +Response: message 8 +Response: message 9 +Response: That's all, folks! +No more messages! -- cgit v1.2.1 From af9b6453ca00994a5622265ea1e4646fde44f0e3 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 18 Feb 2008 09:23:54 +0000 Subject: Requests to release a message that has not been acquired should be ignored. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@628659 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 65aab2870f..a3d32bdb2d 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -763,6 +763,48 @@ class MessageTests(TestBase): #check all 'browsed' messages are still on the queue self.assertEqual(5, channel.queue_query(queue="q").message_count) + def test_release_unacquired(self): + channel = self.channel + + #create queue + self.queue_declare(queue = "q", exclusive=True, auto_delete=True, durable=True) + + #send message + channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) + + #create two 'browsers' + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + channel.message_flow(unit = 0, value = 10, destination = "a") + queueA = self.client.queue("a") + + channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + channel.message_flow(unit = 0, value = 10, destination = "b") + queueB = self.client.queue("b") + + #have each browser release the message + msgA = queueA.get(timeout = 1) + channel.message_release([msgA.command_id, msgA.command_id]) + + msgB = queueB.get(timeout = 1) + channel.message_release([msgB.command_id, msgB.command_id]) + + #cancel browsers + channel.message_cancel(destination = "a") + channel.message_cancel(destination = "b") + + #create consumer + channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1, acquire_mode=0) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + channel.message_flow(unit = 0, value = 10, destination = "c") + queueC = self.client.queue("c") + #consume the message then ack it + msgC = queueC.get(timeout = 1) + msgC.complete() + #ensure there are no other messages + self.assertEmpty(queueC) + def test_no_size(self): self.queue_declare(queue = "q", exclusive=True, auto_delete=True) -- cgit v1.2.1 From f054d72c87c3646042c2d4ee91a44fe30523594e Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 21 Feb 2008 17:40:42 +0000 Subject: Start moving towards final 0-10 spec: * marked preview spec as 99-0 to distinguish it from 0-10 (which will now be used for the final version) * modified python client to treat 99-0 as 0-10 for now * modified broker to have two paths for the two different versions: 99-0 uses PreviewConnection, PreviewConnectionHandler and PreviewSessionHandler which are straight copy & pastes of the Connection, ConnectionHandler and SessionHandler now associated with 0-10 (so we can migrate the 0-10 path to the final spec without affecting clients working with the preview version) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@629883 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 12 +++++++++--- qpid/python/qpid/peer.py | 2 +- qpid/python/qpid/testlib.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index ecbce295b7..eafad7067a 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -178,6 +178,12 @@ class Connection: raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) return frame + def write_99_0(self, frame): + self.write_0_10(frame) + + def read_99_0(self): + return self.read_0_10() + class Frame: DECODERS = {} @@ -233,7 +239,7 @@ class Method(Frame): def encode(self, c): version = (c.spec.major, c.spec.minor) - if version == (0, 10): + if version == (0, 10) or version == (99, 0): c.encode_octet(self.method.klass.id) c.encode_octet(self.method.id) else: @@ -244,7 +250,7 @@ class Method(Frame): def decode(spec, c, size): version = (c.spec.major, c.spec.minor) - if version == (0, 10): + if version == (0, 10) or version == (99, 0): klass = spec.classes.byid[c.decode_octet()] meth = klass.methods.byid[c.decode_octet()] else: @@ -315,7 +321,7 @@ class Response(Frame): return "[%s] Response(%s,%s,%s) %s" % (self.channel, self.id, self.request_id, self.batch_offset, self.method) def uses_struct_encoding(spec): - return (spec.major == 0 and spec.minor == 10) + return (spec.major == 0 and spec.minor == 10) or (spec.major == 99 and spec.minor == 0) class Header(Frame): diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index c7f56b49b2..a464e95593 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -198,7 +198,7 @@ class Channel: self.invoker = self.invoke_reliable else: self.invoker = self.invoke_method - self.use_execution_layer = (spec.major == 0 and spec.minor == 10) + self.use_execution_layer = (spec.major == 0 and spec.minor == 10) or (spec.major == 99 and spec.minor == 0) self.synchronous = True def closed(self, reason): diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index c4f55be18a..5174fe10f4 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -141,7 +141,7 @@ Options: self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") - elif self.spec.major == 0 and self.spec.minor == 10: + elif (self.spec.major == 0 and self.spec.minor == 10) or (self.spec.major == 99 and self.spec.minor == 0): self.tests+=findmodules("tests_0-10") else: self.tests+=findmodules("tests_0-9") -- cgit v1.2.1 From 4c7414d38b032fcb84dab463a408b050dd0c7ddf Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 25 Feb 2008 21:29:55 +0000 Subject: put queue listeners in their own thread git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@631002 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/queue.py | 47 ++++++++++++++++++++++++++++++---------------- qpid/python/tests/queue.py | 41 ++++++++++++++++++---------------------- 2 files changed, 49 insertions(+), 39 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index af0565b6cc..00946a9156 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -24,36 +24,51 @@ content of a queue can be notified if the queue is no longer in use. """ from Queue import Queue as BaseQueue, Empty, Full +from threading import Thread class Closed(Exception): pass class Queue(BaseQueue): END = object() + STOP = object() def __init__(self, *args, **kwargs): BaseQueue.__init__(self, *args, **kwargs) - self._real_put = self.put - self.listener = self._real_put + self.listener = None + self.thread = None def close(self): self.put(Queue.END) def get(self, block = True, timeout = None): - self.put = self._real_put - try: - result = BaseQueue.get(self, block, timeout) - if result == Queue.END: - # this guarantees that any other waiting threads or any future - # calls to get will also result in a Closed exception - self.put(Queue.END) - raise Closed() - else: - return result - finally: - self.put = self.listener - pass + result = BaseQueue.get(self, block, timeout) + if result == Queue.END: + # this guarantees that any other waiting threads or any future + # calls to get will also result in a Closed exception + self.put(Queue.END) + raise Closed() + else: + return result def listen(self, listener): self.listener = listener - self.put = self.listener + if listener == None: + if self.thread != None: + self.put(Queue.STOP) + self.thread.join() + self.thread = None + else: + if self.thread == None: + self.thread = Thread(target = self.run) + self.thread.setDaemon(True) + self.thread.start() + + def run(self): + while True: + try: + o = self.get() + if o == Queue.STOP: break + self.listener(o) + except Closed: + break diff --git a/qpid/python/tests/queue.py b/qpid/python/tests/queue.py index d2e495d207..e12354eb43 100644 --- a/qpid/python/tests/queue.py +++ b/qpid/python/tests/queue.py @@ -30,37 +30,32 @@ class QueueTest (TestCase): # all the queue functionality. def test_listen(self): - LISTEN = object() - GET = object() - EMPTY = object() + values = [] + heard = threading.Event() + def listener(x): + values.append(x) + heard.set() q = Queue(0) - values = [] - q.listen(lambda x: values.append((LISTEN, x))) + q.listen(listener) + heard.clear() q.put(1) - assert values[-1] == (LISTEN, 1) + heard.wait() + assert values[-1] == 1 + heard.clear() q.put(2) - assert values[-1] == (LISTEN, 2) - - class Getter(threading.Thread): + heard.wait() + assert values[-1] == 2 - def run(self): - try: - values.append((GET, q.get(timeout=10))) - except Empty: - values.append(EMPTY) - - g = Getter() - g.start() - # let the other thread reach the get - time.sleep(2) + q.listen(None) q.put(3) - g.join() - - assert values[-1] == (GET, 3) + assert q.get(3) == 3 + q.listen(listener) + heard.clear() q.put(4) - assert values[-1] == (LISTEN, 4) + heard.wait() + assert values[-1] == 4 def test_close(self): q = Queue(0) -- cgit v1.2.1 From 4eb9afdd5f2bedc0f4dd8b9f3da44945487b92cc Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 27 Feb 2008 17:18:40 +0000 Subject: improved error message git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@631657 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/dom.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py index 10b19d6db1..7c759dbdd5 100644 --- a/qpid/python/mllib/dom.py +++ b/qpid/python/mllib/dom.py @@ -72,7 +72,7 @@ class Dispatcher: cls = cls.base return False - def dispatch(self, f): + def dispatch(self, f, attrs = ""): cls = self while cls != None: if hasattr(f, cls.type): @@ -81,7 +81,6 @@ class Dispatcher: cls = cls.base cls = self - attrs = "" while cls != None: if attrs: sep = ", " @@ -151,9 +150,10 @@ class Tag(Node): def dispatch(self, f): try: - method = getattr(f, "do_" + self.name) + attr = "do_" + self.name + method = getattr(f, attr) except AttributeError: - return Dispatcher.dispatch(self, f) + return Dispatcher.dispatch(self, f, "'%s'" % attr) return method(self) class Leaf(Component, Dispatcher): -- cgit v1.2.1 From 89bf8bee0cb748de137f1826a3d96a78414dd1f0 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Thu, 28 Feb 2008 18:55:21 +0000 Subject: QPID-820 from tross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@632087 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/main.py | 34 +- qpid/python/mgmt-cli/managementdata.py | 152 +++++---- qpid/python/qpid/codec.py | 32 ++ qpid/python/qpid/management.py | 551 +++++++++++++++++++-------------- 4 files changed, 473 insertions(+), 296 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/main.py b/qpid/python/mgmt-cli/main.py index 76e1f25c14..f4c22012eb 100755 --- a/qpid/python/mgmt-cli/main.py +++ b/qpid/python/mgmt-cli/main.py @@ -104,7 +104,10 @@ class Mcli (Cmd): self.dataObject.do_list (data) def do_call (self, data): - self.dataObject.do_call (data) + try: + self.dataObject.do_call (data) + except ValueError, e: + print "ValueError:", e def do_EOF (self, data): print "quit" @@ -121,7 +124,10 @@ class Mcli (Cmd): self.dataObject.close () def Usage (): - print sys.argv[0], "[ []]" + print "Usage:", sys.argv[0], "[OPTIONS] [ []]" + print + print "Options:" + print " -s default: /usr/share/amqp/amqp.0-10-preview.xml" print sys.exit (1) @@ -134,13 +140,15 @@ try: (optlist, cargs) = getopt.getopt (sys.argv[1:], 's:') except: Usage () + exit (1) specpath = "/usr/share/amqp/amqp.0-10-preview.xml" host = "localhost" port = 5672 -if "s" in optlist: - specpath = optlist["s"] +for opt in optlist: + if opt[0] == "-s": + specpath = opt[1] if len (cargs) > 0: host = cargs[0] @@ -148,19 +156,27 @@ if len (cargs) > 0: if len (cargs) > 1: port = int (cargs[1]) -print ("Management Tool for QPID") disp = Display () # Attempt to make a connection to the target broker try: - data = ManagementData (disp, host, port, spec=specpath) + data = ManagementData (disp, host, port, specfile=specpath) except socket.error, e: - sys.exit (0) + print "Socket Error:", e[1] + sys.exit (1) except Closed, e: if str(e).find ("Exchange not found") != -1: print "Management not enabled on broker: Use '-m yes' option on broker startup." - sys.exit (0) + sys.exit (1) +except IOError, e: + print "IOError: %d - %s: %s" % (e.errno, e.strerror, e.filename) + sys.exit (1) # Instantiate the CLI interpreter and launch it. cli = Mcli (data, disp) -cli.cmdloop () +print ("Management Tool for QPID") +try: + cli.cmdloop () +except Closed, e: + print "Connection to Broker Lost:", e + exit (1) diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index e7233c98ae..5b13594994 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -19,10 +19,12 @@ # under the License. # -from qpid.management import ManagedBroker +import qpid +from qpid.management import managementChannel, managementClient from threading import Lock from disp import Display from shlex import split +from qpid.client import Client class ManagementData: @@ -35,9 +37,10 @@ class ManagementData: # The only historical data it keeps are the high and low watermarks # for hi-lo statistics. # - # tables :== {} + # tables :== {class-key} # {} # (timestamp, config-record, inst-record) + # class-key :== (, , ) # timestamp :== (, , ) # config-record :== [element] # inst-record :== [element] @@ -59,6 +62,10 @@ class ManagementData: return displayId + self.baseId return displayId - 5000 + 0x8000000000000000L + def displayClassName (self, cls): + (packageName, className, hash) = cls + return packageName + "." + className + def dataHandler (self, context, className, list, timestamps): """ Callback for configuration and instrumentation data updates """ self.lock.acquire () @@ -104,6 +111,12 @@ class ManagementData: finally: self.lock.release () + def configHandler (self, context, className, list, timestamps): + self.dataHandler (0, className, list, timestamps); + + def instHandler (self, context, className, list, timestamps): + self.dataHandler (1, className, list, timestamps); + def methodReply (self, broker, sequence, status, sText, args): """ Callback for method-reply messages """ self.lock.acquire () @@ -121,12 +134,8 @@ class ManagementData: self.schema[className] = (configs, insts, methods, events) def __init__ (self, disp, host, port=5672, username="guest", password="guest", - spec="../../specs/amqp.0-10-preview.xml"): - self.broker = ManagedBroker (host, port, username, password, spec) - self.broker.configListener (0, self.dataHandler) - self.broker.instrumentationListener (1, self.dataHandler) - self.broker.methodListener (None, self.methodReply) - self.broker.schemaListener (None, self.schemaHandler) + specfile="../../specs/amqp.0-10-preview.xml"): + self.spec = qpid.spec.load (specfile) self.lock = Lock () self.tables = {} self.schema = {} @@ -135,24 +144,33 @@ class ManagementData: self.lastUnit = None self.methodSeq = 1 self.methodsPending = {} - self.broker.start () + + self.client = Client (host, port, self.spec) + self.client.start ({"LOGIN": username, "PASSWORD": password}) + self.channel = self.client.channel (1) + + self.mclient = managementClient (self.spec, None, self.configHandler, + self.instHandler, self.methodReply) + self.mclient.schemaListener (self.schemaHandler) + self.mch = managementChannel (self.channel, self.mclient.topicCb, self.mclient.replyCb) + self.mclient.addChannel (self.mch) def close (self): - self.broker.stop () + self.mclient.removeChannel (self.mch) def refName (self, oid): if oid == 0: return "NULL" return str (self.displayObjId (oid)) - def valueDisplay (self, className, key, value): + def valueDisplay (self, classKey, key, value): for kind in range (2): - schema = self.schema[className][kind] + schema = self.schema[classKey][kind] for item in schema: if item[0] == key: typecode = item[1] unit = item[2] - if typecode >= 1 and typecode <= 5: # numerics + if (typecode >= 1 and typecode <= 5) or typecode >= 12: # numerics if unit == None or unit == self.lastUnit: return str (value) else: @@ -191,6 +209,20 @@ class ManagementData: result = result + self.valueDisplay (className, key, val) return result + def getClassKey (self, className): + dotPos = className.find(".") + if dotPos == -1: + for key in self.schema: + if key[1] == className: + return key + else: + package = className[0:dotPos] + name = className[dotPos + 1:] + for key in self.schema: + if key[0] == package and key[1] == name: + return key + return None + def classCompletions (self, prefix): """ Provide a list of candidate class names for command completion """ self.lock.acquire () @@ -227,6 +259,10 @@ class ManagementData: return "reference" elif typecode == 11: return "boolean" + elif typecode == 12: + return "float" + elif typecode == 13: + return "double" else: raise ValueError ("Invalid type code: %d" % typecode) @@ -253,16 +289,16 @@ class ManagementData: return False return True - def listOfIds (self, className, tokens): + def listOfIds (self, classKey, tokens): """ Generate a tuple of object ids for a classname based on command tokens. """ list = [] if tokens[0] == "all": - for id in self.tables[className]: + for id in self.tables[classKey]: list.append (self.displayObjId (id)) elif tokens[0] == "active": - for id in self.tables[className]: - if self.tables[className][id][0][2] == 0: + for id in self.tables[classKey]: + if self.tables[classKey][id][0][2] == 0: list.append (self.displayObjId (id)) else: @@ -271,7 +307,7 @@ class ManagementData: if token.find ("-") != -1: ids = token.split("-", 2) for id in range (int (ids[0]), int (ids[1]) + 1): - if self.getClassForId (self.rawObjId (long (id))) == className: + if self.getClassForId (self.rawObjId (long (id))) == classKey: list.append (id) else: list.append (token) @@ -301,7 +337,7 @@ class ManagementData: deleted = deleted + 1 else: active = active + 1 - rows.append ((name, active, deleted)) + rows.append ((self.displayClassName (name), active, deleted)) self.disp.table ("Management Object Types:", ("ObjectType", "Active", "Deleted"), rows) finally: @@ -311,22 +347,23 @@ class ManagementData: """ Generate a display of a list of objects in a class """ self.lock.acquire () try: - if className not in self.tables: + classKey = self.getClassKey (className) + if classKey == None: print ("Object type %s not known" % className) else: rows = [] - sorted = self.tables[className].keys () + sorted = self.tables[classKey].keys () sorted.sort () for objId in sorted: - (ts, config, inst) = self.tables[className][objId] + (ts, config, inst) = self.tables[classKey][objId] createTime = self.disp.timestamp (ts[1]) destroyTime = "-" if ts[2] > 0: destroyTime = self.disp.timestamp (ts[2]) - objIndex = self.getObjIndex (className, config) + objIndex = self.getObjIndex (classKey, config) row = (self.refName (objId), createTime, destroyTime, objIndex) rows.append (row) - self.disp.table ("Objects of type %s" % className, + self.disp.table ("Objects of type %s.%s" % (classKey[0], classKey[1]), ("ID", "Created", "Destroyed", "Index"), rows) finally: @@ -343,57 +380,57 @@ class ManagementData: else: rootId = int (tokens[0]) - className = self.getClassForId (self.rawObjId (rootId)) + classKey = self.getClassForId (self.rawObjId (rootId)) remaining = tokens - if className == None: + if classKey == None: print "Id not known: %d" % int (tokens[0]) raise ValueError () else: - className = tokens[0] + classKey = self.getClassKey (tokens[0]) remaining = tokens[1:] - if className not in self.tables: - print "Class not known: %s" % className + if classKey not in self.tables: + print "Class not known: %s" % tokens[0] raise ValueError () - userIds = self.listOfIds (className, remaining) + userIds = self.listOfIds (classKey, remaining) if len (userIds) == 0: print "No object IDs supplied" raise ValueError () ids = [] for id in userIds: - if self.getClassForId (self.rawObjId (long (id))) == className: + if self.getClassForId (self.rawObjId (long (id))) == classKey: ids.append (self.rawObjId (long (id))) rows = [] timestamp = None - config = self.tables[className][ids[0]][1] + config = self.tables[classKey][ids[0]][1] for eIdx in range (len (config)): key = config[eIdx][0] if key != "id": row = ("config", key) for id in ids: if timestamp == None or \ - timestamp < self.tables[className][id][0][0]: - timestamp = self.tables[className][id][0][0] - (key, value) = self.tables[className][id][1][eIdx] - row = row + (self.valueDisplay (className, key, value),) + timestamp < self.tables[classKey][id][0][0]: + timestamp = self.tables[classKey][id][0][0] + (key, value) = self.tables[classKey][id][1][eIdx] + row = row + (self.valueDisplay (classKey, key, value),) rows.append (row) - inst = self.tables[className][ids[0]][2] + inst = self.tables[classKey][ids[0]][2] for eIdx in range (len (inst)): key = inst[eIdx][0] if key != "id": row = ("inst", key) for id in ids: - (key, value) = self.tables[className][id][2][eIdx] - row = row + (self.valueDisplay (className, key, value),) + (key, value) = self.tables[classKey][id][2][eIdx] + row = row + (self.valueDisplay (classKey, key, value),) rows.append (row) titleRow = ("Type", "Element") for id in ids: titleRow = titleRow + (self.refName (id),) - caption = "Object of type %s:" % className + caption = "Object of type %s.%s:" % (classKey[0], classKey[1]) if timestamp != None: caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" self.disp.table (caption, titleRow, rows) @@ -423,12 +460,13 @@ class ManagementData: """ Generate a display of details of the schema of a particular class """ self.lock.acquire () try: - if className not in self.schema: + classKey = self.getClassKey (className) + if classKey == None: print ("Class name %s not known" % className) raise ValueError () rows = [] - for config in self.schema[className][0]: + for config in self.schema[classKey][0]: name = config[0] if name != "id": typename = self.typeName(config[1]) @@ -446,7 +484,7 @@ class ManagementData: extra = extra + "MaxLen: " + str (config[8]) rows.append ((name, typename, unit, access, extra, desc)) - for config in self.schema[className][1]: + for config in self.schema[classKey][1]: name = config[0] if name != "id": typename = self.typeName(config[1]) @@ -455,10 +493,10 @@ class ManagementData: rows.append ((name, typename, unit, "", "", desc)) titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") - self.disp.table ("Schema for class '%s':" % className, titles, rows) + self.disp.table ("Schema for class '%s.%s':" % (classKey[0], classKey[1]), titles, rows) - for mname in self.schema[className][2]: - (mdesc, args) = self.schema[className][2][mname] + for mname in self.schema[classKey][2]: + (mdesc, args) = self.schema[classKey][2][mname] caption = "\nMethod '%s' %s" % (mname, self.notNone (mdesc)) rows = [] for arg in args: @@ -485,25 +523,25 @@ class ManagementData: self.lock.release () def getClassForId (self, objId): - """ Given an object ID, return the class name for the referenced object """ - for className in self.tables: - if objId in self.tables[className]: - return className + """ Given an object ID, return the class key for the referenced object """ + for classKey in self.tables: + if objId in self.tables[classKey]: + return classKey return None def callMethod (self, userOid, methodName, args): self.lock.acquire () methodOk = True try: - className = self.getClassForId (self.rawObjId (userOid)) - if className == None: + classKey = self.getClassForId (self.rawObjId (userOid)) + if classKey == None: raise ValueError () - if methodName not in self.schema[className][2]: - print "Method '%s' not valid for class '%s'" % (methodName, className) + if methodName not in self.schema[classKey][2]: + print "Method '%s' not valid for class '%s.%s'" % (methodName, classKey[0], classKey[1]) raise ValueError () - schemaMethod = self.schema[className][2][methodName] + schemaMethod = self.schema[classKey][2][methodName] if len (args) != len (schemaMethod[1]): print "Wrong number of method args: Need %d, Got %d" % (len (schemaMethod[1]), len (args)) raise ValueError () @@ -519,8 +557,8 @@ class ManagementData: self.lock.release () if methodOk: # try: - self.broker.method (self.methodSeq, self.rawObjId (userOid), className, - methodName, namedArgs) + self.mclient.callMethod (self.mch, self.methodSeq, self.rawObjId (userOid), classKey, + methodName, namedArgs) # except ValueError, e: # print "Error invoking method:", e diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index b25de11f11..1a9372455d 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -265,6 +265,38 @@ class Codec: """ return self.unpack("!Q") + def encode_float(self, o): + self.pack("!f", o) + + def decode_float(self): + return self.unpack("!f") + + def encode_double(self, o): + self.pack("!d", o) + + def decode_double(self): + return self.unpack("!d") + + def encode_bin128(self, b): + for idx in range (0,16): + self.pack("!B", ord (b[idx])) + + def decode_bin128(self): + result = "" + for idx in range (0,16): + result = result + chr (self.unpack("!B")) + return result + + def encode_raw(self, len, b): + for idx in range (0,len): + self.pack("!B", b[idx]) + + def decode_raw(self, len): + result = "" + for idx in range (0,len): + result = result + chr (self.unpack("!B")) + return result + def enc_str(self, fmt, s): """ encodes a string 's' in network byte order as per format 'fmt' diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 40de2a5298..b5d992cf5d 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -35,12 +35,14 @@ from threading import Lock class SequenceManager: + """ Manage sequence numbers for asynchronous method calls """ def __init__ (self): self.lock = Lock () self.sequence = 0 self.pending = {} def reserve (self, data): + """ Reserve a unique sequence number """ self.lock.acquire () result = self.sequence self.sequence = self.sequence + 1 @@ -49,6 +51,7 @@ class SequenceManager: return result def release (self, seq): + """ Release a reserved sequence number """ data = None self.lock.acquire () if seq in self.pending: @@ -57,12 +60,172 @@ class SequenceManager: self.lock.release () return data -class ManagementMetadata: - """One instance of this class is created for each ManagedBroker. It - is used to store metadata from the broker which is needed for the - proper interpretation of received management content.""" + +class managementChannel: + """ This class represents a connection to an AMQP broker. """ + + def __init__ (self, ch, topicCb, replyCb, cbContext=None): + """ Given a channel on an established AMQP broker connection, this method + opens a session and performs all of the declarations and bindings needed + to participate in the management protocol. """ + response = ch.session_open (detached_lifetime=300) + self.topicName = "mgmt-" + base64.urlsafe_b64encode (response.session_id) + self.replyName = "reply-" + base64.urlsafe_b64encode (response.session_id) + self.qpidChannel = ch + self.tcb = topicCb + self.rcb = replyCb + self.context = cbContext + + ch.queue_declare (queue=self.topicName, exclusive=1, auto_delete=1) + ch.queue_declare (queue=self.replyName, exclusive=1, auto_delete=1) + + ch.queue_bind (exchange="qpid.management", + queue=self.topicName, routing_key="mgmt.#") + ch.queue_bind (exchange="amq.direct", + queue=self.replyName, routing_key=self.replyName) + ch.message_subscribe (queue=self.topicName, destination="tdest") + ch.message_subscribe (queue=self.replyName, destination="rdest") + + ch.client.queue ("tdest").listen (self.topicCb) + ch.client.queue ("rdest").listen (self.replyCb) + + ch.message_flow_mode (destination="tdest", mode=1) + ch.message_flow (destination="tdest", unit=0, value=0xFFFFFFFF) + ch.message_flow (destination="tdest", unit=1, value=0xFFFFFFFF) + + ch.message_flow_mode (destination="rdest", mode=1) + ch.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) + ch.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) + + def topicCb (self, msg): + """ Receive messages via the topic queue on this channel. """ + self.tcb (self, msg) + + def replyCb (self, msg): + """ Receive messages via the reply queue on this channel. """ + self.rcb (self, msg) + + def send (self, exchange, msg): + self.qpidChannel.message_transfer (destination=exchange, content=msg) + + +class managementClient: + """ This class provides an API for access to management data on the AMQP + network. It implements the management protocol and manages the management + schemas as advertised by the various management agents in the network. """ + + #======================================================== + # User API - interacts with the class's user + #======================================================== + def __init__ (self, amqpSpec, ctrlCb, configCb, instCb, methodCb=None): + self.spec = amqpSpec + self.ctrlCb = ctrlCb + self.configCb = configCb + self.instCb = instCb + self.methodCb = methodCb + self.schemaCb = None + self.eventCb = None + self.channels = [] + self.seqMgr = SequenceManager () + self.schema = {} + self.packages = {} + + def schemaListener (self, schemaCb): + """ Optionally register a callback to receive details of the schema of + managed objects in the network. """ + self.schemaCb = schemaCb + + def eventListener (self, eventCb): + """ Optionally register a callback to receive events from managed objects + in the network. """ + self.eventCb = eventCb + + def addChannel (self, channel): + """ Register a new channel. """ + self.channels.append (channel) + codec = Codec (StringIO (), self.spec) + self.setHeader (codec, ord ('H')) + msg = Content (codec.stream.getvalue ()) + msg["content_type"] = "application/octet-stream" + msg["routing_key"] = "agent" + msg["reply_to"] = self.spec.struct ("reply_to") + msg["reply_to"]["exchange_name"] = "amq.direct" + msg["reply_to"]["routing_key"] = channel.replyName + channel.send ("qpid.management", msg) + + def removeChannel (self, channel): + """ Remove a previously added channel from management. """ + self.channels.remove (channel) + + def callMethod (self, channel, userSequence, objId, className, methodName, args=None): + """ Invoke a method on a managed object. """ + self.method (channel, userSequence, objId, className, methodName, args) + + #======================================================== + # Channel API - interacts with registered channel objects + #======================================================== + def topicCb (self, ch, msg): + """ Receive messages via the topic queue of a particular channel. """ + codec = Codec (StringIO (msg.content.body), self.spec) + hdr = self.checkHeader (codec) + if hdr == None: + raise ValueError ("outer header invalid"); + self.parse (ch, codec, hdr[0], hdr[1]) + msg.complete () + + def replyCb (self, ch, msg): + """ Receive messages via the reply queue of a particular channel. """ + codec = Codec (StringIO (msg.content.body), self.spec) + hdr = self.checkHeader (codec) + if hdr == None: + msg.complete () + return + + if hdr[0] == 'm': + self.handleMethodReply (ch, codec) + elif hdr[0] == 'I': + self.handleInit (ch, codec) + elif hdr[0] == 'p': + self.handlePackageInd (ch, codec) + elif hdr[0] == 'q': + self.handleClassInd (ch, codec) + else: + self.parse (ch, codec, hdr[0], hdr[1]) + msg.complete () + + #======================================================== + # Internal Functions + #======================================================== + def setHeader (self, codec, opcode, cls = 0): + """ Compose the header of a management message. """ + codec.encode_octet (ord ('A')) + codec.encode_octet (ord ('M')) + codec.encode_octet (ord ('0')) + codec.encode_octet (ord ('1')) + codec.encode_octet (opcode) + codec.encode_octet (cls) + + def checkHeader (self, codec): + """ Check the header of a management message and extract the opcode and + class. """ + octet = chr (codec.decode_octet ()) + if octet != 'A': + return None + octet = chr (codec.decode_octet ()) + if octet != 'M': + return None + octet = chr (codec.decode_octet ()) + if octet != '0': + return None + octet = chr (codec.decode_octet ()) + if octet != '1': + return None + opcode = chr (codec.decode_octet ()) + cls = chr (codec.decode_octet ()) + return (opcode, cls) def encodeValue (self, codec, value, typecode): + """ Encode, into the codec, a value based on its typecode. """ if typecode == 1: codec.encode_octet (int (value)) elif typecode == 2: @@ -85,10 +248,15 @@ class ManagementMetadata: codec.encode_longlong (long (value)) elif typecode == 11: # BOOL codec.encode_octet (int (value)) + elif typecode == 12: # FLOAT + codec.encode_float (float (value)) + elif typecode == 13: # DOUBLE + codec.encode_double (double (value)) else: raise ValueError ("Invalid type code: %d" % typecode) def decodeValue (self, codec, typecode): + """ Decode, from the codec, a value based on its typecode. """ if typecode == 1: data = codec.decode_octet () elif typecode == 2: @@ -111,17 +279,119 @@ class ManagementMetadata: data = codec.decode_longlong () elif typecode == 11: # BOOL data = codec.decode_octet () + elif typecode == 12: # FLOAT + data = codec.decode_float () + elif typecode == 13: # DOUBLE + data = codec.decode_double () else: raise ValueError ("Invalid type code: %d" % typecode) return data + + def handleMethodReply (self, ch, codec): + sequence = codec.decode_long () + status = codec.decode_long () + sText = codec.decode_shortstr () + + data = self.seqMgr.release (sequence) + if data == None: + return + + (userSequence, classId, methodName) = data + args = {} + + if status == 0: + schemaClass = self.schema[classId] + ms = schemaClass['M'] + arglist = None + for mname in ms: + (mdesc, margs) = ms[mname] + if mname == methodName: + arglist = margs + if arglist == None: + return + + for arg in arglist: + if arg[2].find("O") != -1: + args[arg[0]] = self.decodeValue (codec, arg[1]) + + if self.methodCb != None: + self.methodCb (ch.context, userSequence, status, sText, args) + + def handleInit (self, ch, codec): + len = codec.decode_short () + data = codec.decode_raw (len) + if self.ctrlCb != None: + self.ctrlCb (ch.context, len, data) + + # Send a package request + sendCodec = Codec (StringIO (), self.spec) + self.setHeader (sendCodec, ord ('P')) + smsg = Content (sendCodec.stream.getvalue ()) + smsg["content_type"] = "application/octet-stream" + smsg["routing_key"] = "agent" + smsg["reply_to"] = self.spec.struct ("reply_to") + smsg["reply_to"]["exchange_name"] = "amq.direct" + smsg["reply_to"]["routing_key"] = ch.replyName + ch.send ("qpid.management", smsg) - def parseSchema (self, cls, codec): + def handlePackageInd (self, ch, codec): + pname = codec.decode_shortstr () + if pname not in self.packages: + self.packages[pname] = {} + + # Send a class request + sendCodec = Codec (StringIO (), self.spec) + self.setHeader (sendCodec, ord ('Q')) + sendCodec.encode_shortstr (pname) + smsg = Content (sendCodec.stream.getvalue ()) + smsg["content_type"] = "application/octet-stream" + smsg["routing_key"] = "agent" + smsg["reply_to"] = self.spec.struct ("reply_to") + smsg["reply_to"]["exchange_name"] = "amq.direct" + smsg["reply_to"]["routing_key"] = ch.replyName + ch.send ("qpid.management", smsg) + + def handleClassInd (self, ch, codec): + pname = codec.decode_shortstr () + cname = codec.decode_shortstr () + hash = codec.decode_bin128 () + if pname not in self.packages: + return + + if (cname, hash) not in self.packages[pname]: + # Send a schema request + sendCodec = Codec (StringIO (), self.spec) + self.setHeader (sendCodec, ord ('S')) + sendCodec.encode_shortstr (pname) + sendCodec.encode_shortstr (cname) + sendCodec.encode_bin128 (hash) + smsg = Content (sendCodec.stream.getvalue ()) + smsg["content_type"] = "application/octet-stream" + smsg["routing_key"] = "agent" + smsg["reply_to"] = self.spec.struct ("reply_to") + smsg["reply_to"]["exchange_name"] = "amq.direct" + smsg["reply_to"]["routing_key"] = ch.replyName + ch.send ("qpid.management", smsg) + + def parseSchema (self, ch, cls, codec): + """ Parse a received schema-description message. """ + packageName = codec.decode_shortstr () className = codec.decode_shortstr () + hash = codec.decode_bin128 () configCount = codec.decode_short () instCount = codec.decode_short () methodCount = codec.decode_short () eventCount = codec.decode_short () + if packageName not in self.packages: + return + if (className, hash) in self.packages[packageName]: + return + + classKey = (packageName, className, hash) + if classKey in self.schema: + return + configs = [] insts = [] methods = {} @@ -213,25 +483,29 @@ class ManagementMetadata: args.append (arg) methods[mname] = (mdesc, args) + schemaClass = {} + schemaClass['C'] = configs + schemaClass['I'] = insts + schemaClass['M'] = methods + schemaClass['E'] = events + self.schema[classKey] = schemaClass - self.schema[(className,'C')] = configs - self.schema[(className,'I')] = insts - self.schema[(className,'M')] = methods - self.schema[(className,'E')] = events - - if self.broker.schema_cb != None: - self.broker.schema_cb[1] (self.broker.schema_cb[0], className, - configs, insts, methods, events) + if self.schemaCb != None: + self.schemaCb (ch.context, classKey, configs, insts, methods, events) - def parseContent (self, cls, codec): - if cls == 'C' and self.broker.config_cb == None: + def parseContent (self, ch, cls, codec): + """ Parse a received content message. """ + if cls == 'C' and self.configCb == None: return - if cls == 'I' and self.broker.inst_cb == None: + if cls == 'I' and self.instCb == None: return - className = codec.decode_shortstr () + packageName = codec.decode_shortstr () + className = codec.decode_shortstr () + hash = codec.decode_bin128 () + classKey = (packageName, className, hash) - if (className,cls) not in self.schema: + if classKey not in self.schema: return row = [] @@ -241,184 +515,49 @@ class ManagementMetadata: timestamps.append (codec.decode_longlong ()) # Create Time timestamps.append (codec.decode_longlong ()) # Delete Time - for element in self.schema[(className,cls)][:]: + schemaClass = self.schema[classKey] + for element in schemaClass[cls][:]: tc = element[1] name = element[0] data = self.decodeValue (codec, tc) row.append ((name, data)) - if cls == 'C': - self.broker.config_cb[1] (self.broker.config_cb[0], className, row, timestamps) + if cls == 'C': + self.configCb (ch.context, classKey, row, timestamps) elif cls == 'I': - self.broker.inst_cb[1] (self.broker.inst_cb[0], className, row, timestamps) - - def parse (self, codec, opcode, cls): - if opcode == 'S': - self.parseSchema (cls, codec) + self.instCb (ch.context, classKey, row, timestamps) + def parse (self, ch, codec, opcode, cls): + """ Parse a message received from the topic queue. """ + if opcode == 's': + self.parseSchema (ch, cls, codec) elif opcode == 'C': - self.parseContent (cls, codec) - + self.parseContent (ch, cls, codec) else: raise ValueError ("Unknown opcode: %c" % opcode); - def __init__ (self, broker): - self.broker = broker - self.schema = {} - - -class ManagedBroker: - """An object of this class represents a connection (over AMQP) to a - single managed broker.""" - - mExchange = "qpid.management" - dExchange = "amq.direct" - - def setHeader (self, codec, opcode, cls = 0): - codec.encode_octet (ord ('A')) - codec.encode_octet (ord ('M')) - codec.encode_octet (ord ('0')) - codec.encode_octet (ord ('1')) - codec.encode_octet (opcode) - codec.encode_octet (cls) - - def checkHeader (self, codec): - octet = chr (codec.decode_octet ()) - if octet != 'A': - return None - octet = chr (codec.decode_octet ()) - if octet != 'M': - return None - octet = chr (codec.decode_octet ()) - if octet != '0': - return None - octet = chr (codec.decode_octet ()) - if octet != '1': - return None - opcode = chr (codec.decode_octet ()) - cls = chr (codec.decode_octet ()) - return (opcode, cls) - - def publish_cb (self, msg): - codec = Codec (StringIO (msg.content.body), self.spec) - - hdr = self.checkHeader (codec) - if hdr == None: - raise ValueError ("outer header invalid"); - - self.metadata.parse (codec, hdr[0], hdr[1]) - msg.complete () - - def reply_cb (self, msg): - codec = Codec (StringIO (msg.content.body), self.spec) - hdr = self.checkHeader (codec) - if hdr == None: - msg.complete () - return - if hdr[0] != 'R': - msg.complete () - return - - sequence = codec.decode_long () - status = codec.decode_long () - sText = codec.decode_shortstr () - - data = self.sequenceManager.release (sequence) - if data == None: - msg.complete () - return - - (userSequence, className, methodName) = data - args = {} - - if status == 0: - ms = self.metadata.schema[(className,'M')] - arglist = None - for mname in ms: - (mdesc, margs) = ms[mname] - if mname == methodName: - arglist = margs - if arglist == None: - msg.complete () - return - - for arg in arglist: - if arg[2].find("O") != -1: - args[arg[0]] = self.metadata.decodeValue (codec, arg[1]) - - if self.method_cb != None: - self.method_cb[1] (self.method_cb[0], userSequence, status, sText, args) - - msg.complete () - - def __init__ (self, - host = "localhost", - port = 5672, - username = "guest", - password = "guest", - specfile = "/usr/share/amqp/amqp.0-10-preview.xml"): - - self.spec = qpid.spec.load (specfile) - self.client = None - self.channel = None - self.queue = None - self.rqueue = None - self.qname = None - self.rqname = None - self.metadata = ManagementMetadata (self) - self.sequenceManager = SequenceManager () - self.connected = 0 - self.lastConnectError = None - - # Initialize the callback records - self.status_cb = None - self.schema_cb = None - self.config_cb = None - self.inst_cb = None - self.method_cb = None - - self.host = host - self.port = port - self.username = username - self.password = password - - def statusListener (self, context, callback): - self.status_cb = (context, callback) - - def schemaListener (self, context, callback): - self.schema_cb = (context, callback) - - def configListener (self, context, callback): - self.config_cb = (context, callback) - - def methodListener (self, context, callback): - self.method_cb = (context, callback) - - def instrumentationListener (self, context, callback): - self.inst_cb = (context, callback) - - def method (self, userSequence, objId, className, - methodName, args=None, packageName="qpid"): - codec = Codec (StringIO (), self.spec); - sequence = self.sequenceManager.reserve ((userSequence, className, methodName)) + def method (self, channel, userSequence, objId, classId, methodName, args): + """ Invoke a method on an object """ + codec = Codec (StringIO (), self.spec) + sequence = self.seqMgr.reserve ((userSequence, classId, methodName)) self.setHeader (codec, ord ('M')) codec.encode_long (sequence) # Method sequence id codec.encode_longlong (objId) # ID of object - #codec.encode_shortstr (self.rqname) # name of reply queue # Encode args according to schema - if (className,'M') not in self.metadata.schema: - self.sequenceManager.release (sequence) - raise ValueError ("Unknown class name: %s" % className) + if classId not in self.schema: + self.seqMgr.release (sequence) + raise ValueError ("Unknown class name: %s" % classId) - ms = self.metadata.schema[(className,'M')] - arglist = None + schemaClass = self.schema[classId] + ms = schemaClass['M'] + arglist = None for mname in ms: (mdesc, margs) = ms[mname] if mname == methodName: arglist = margs if arglist == None: - self.sequenceManager.release (sequence) + self.seqMgr.release (sequence) raise ValueError ("Unknown method name: %s" % methodName) for arg in arglist: @@ -427,65 +566,17 @@ class ManagedBroker: if arg[0] in args: value = args[arg[0]] if value == None: - self.sequenceManager.release (sequence) + self.seqMgr.release (sequence) raise ValueError ("Missing non-defaulted argument: %s" % arg[0]) - self.metadata.encodeValue (codec, value, arg[1]) + self.encodeValue (codec, value, arg[1]) + packageName = classId[0] + className = classId[1] msg = Content (codec.stream.getvalue ()) msg["content_type"] = "application/octet-stream" - msg["routing_key"] = "method." + packageName + "." + className + "." + methodName + msg["routing_key"] = "agent.method." + packageName + "." + \ + className + "." + methodName msg["reply_to"] = self.spec.struct ("reply_to") msg["reply_to"]["exchange_name"] = "amq.direct" - msg["reply_to"]["routing_key"] = self.rqname - self.channel.message_transfer (destination="qpid.management", content=msg) - - def isConnected (self): - return connected - - def start (self): - print "Connecting to broker %s:%d" % (self.host, self.port) - - try: - self.client = Client (self.host, self.port, self.spec) - self.client.start ({"LOGIN": self.username, "PASSWORD": self.password}) - self.channel = self.client.channel (1) - response = self.channel.session_open (detached_lifetime=300) - self.qname = "mgmt-" + base64.urlsafe_b64encode (response.session_id) - self.rqname = "reply-" + base64.urlsafe_b64encode (response.session_id) - - self.channel.queue_declare (queue=self.qname, exclusive=1, auto_delete=1) - self.channel.queue_declare (queue=self.rqname, exclusive=1, auto_delete=1) - - self.channel.queue_bind (exchange=ManagedBroker.mExchange, queue=self.qname, - routing_key="mgmt.#") - self.channel.queue_bind (exchange=ManagedBroker.dExchange, queue=self.rqname, - routing_key=self.rqname) - - self.channel.message_subscribe (queue=self.qname, destination="mdest") - self.channel.message_subscribe (queue=self.rqname, destination="rdest") - - self.queue = self.client.queue ("mdest") - self.queue.listen (self.publish_cb) - - self.channel.message_flow_mode (destination="mdest", mode=1) - self.channel.message_flow (destination="mdest", unit=0, value=0xFFFFFFFF) - self.channel.message_flow (destination="mdest", unit=1, value=0xFFFFFFFF) - - self.rqueue = self.client.queue ("rdest") - self.rqueue.listen (self.reply_cb) - - self.channel.message_flow_mode (destination="rdest", mode=1) - self.channel.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) - self.channel.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) - - self.connected = 1 - - except socket.error, e: - print "Socket Error:", e[1] - self.lastConnectError = e - raise - except: - raise - - def stop (self): - pass + msg["reply_to"]["routing_key"] = channel.replyName + channel.send ("qpid.management", msg) -- cgit v1.2.1 From c19ba3eb15620becdbc157005dcc5a465b3ac064 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 4 Mar 2008 20:03:09 +0000 Subject: import of in-process 0-10 final python client git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@633610 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 24 ++ qpid/python/qpid/assembler.py | 110 +++++++ qpid/python/qpid/codec010.py | 186 +++++++++++ qpid/python/qpid/connection010.py | 172 +++++++++++ qpid/python/qpid/datatypes.py | 112 +++++++ qpid/python/qpid/delegates.py | 111 +++++++ qpid/python/qpid/framer.py | 124 ++++++++ qpid/python/qpid/invoker.py | 32 ++ qpid/python/qpid/packer.py | 36 +++ qpid/python/qpid/session.py | 208 +++++++++++++ qpid/python/qpid/spec010.py | 617 +++++++++++++++++++++++++++++++++++++ qpid/python/qpid/util.py | 39 +++ qpid/python/server010 | 34 ++ qpid/python/tests/__init__.py | 5 + qpid/python/tests/assembler.py | 77 +++++ qpid/python/tests/connection010.py | 137 ++++++++ qpid/python/tests/datatypes.py | 91 ++++++ qpid/python/tests/framer.py | 92 ++++++ qpid/python/tests/spec010.py | 60 ++++ 19 files changed, 2267 insertions(+) create mode 100755 qpid/python/hello-010-world create mode 100644 qpid/python/qpid/assembler.py create mode 100644 qpid/python/qpid/codec010.py create mode 100644 qpid/python/qpid/connection010.py create mode 100644 qpid/python/qpid/datatypes.py create mode 100644 qpid/python/qpid/delegates.py create mode 100644 qpid/python/qpid/framer.py create mode 100644 qpid/python/qpid/invoker.py create mode 100644 qpid/python/qpid/packer.py create mode 100644 qpid/python/qpid/session.py create mode 100644 qpid/python/qpid/spec010.py create mode 100644 qpid/python/qpid/util.py create mode 100755 qpid/python/server010 create mode 100644 qpid/python/tests/assembler.py create mode 100644 qpid/python/tests/connection010.py create mode 100644 qpid/python/tests/datatypes.py create mode 100644 qpid/python/tests/framer.py create mode 100644 qpid/python/tests/spec010.py (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world new file mode 100755 index 0000000000..7685af8fd3 --- /dev/null +++ b/qpid/python/hello-010-world @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +from qpid.connection010 import Connection +from qpid.spec010 import load +from qpid.util import connect +from qpid.datatypes import Message + +spec = load("../specs/amqp.0-10.xml") +conn = Connection(connect("0.0.0.0", spec.port), spec) +conn.start(timeout=10) + +ssn = conn.session("my-session") + +ssn.queue_declare("asdf") + +ssn.message_transfer("this", None, None, Message("testing...")) +ssn.message_transfer("is") +ssn.message_transfer("a") +ssn.message_transfer("test") + +print ssn.queue_query("testing") + +ssn.close(timeout=10) +conn.close(timeout=10) diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py new file mode 100644 index 0000000000..e0e5d3fb72 --- /dev/null +++ b/qpid/python/qpid/assembler.py @@ -0,0 +1,110 @@ +# +# 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. +# + +from codec010 import StringCodec +from framer import * + +class Segment: + + def __init__(self, first, last, type, track, channel, payload): + self.id = None + self.offset = None + self.first = first + self.last = last + self.type = type + self.track = track + self.channel = channel + self.payload = payload + + def decode(self, spec): + segs = spec["segment_type"] + choice = segs.choices[self.type] + return getattr(self, "decode_%s" % choice.name)(spec) + + def decode_control(self, spec): + sc = StringCodec(spec, self.payload) + return sc.read_control() + + def decode_command(self, spec): + sc = StringCodec(spec, self.payload) + return sc.read_command() + + def decode_header(self, spec): + sc = StringCodec(spec, self.payload) + values = [] + while len(sc.encoded) > 0: + values.append(sc.read_struct32()) + return values + + def decode_body(self, spec): + return self + + def __str__(self): + return "%s%s %s %s %s %r" % (int(self.first), int(self.last), self.type, + self.track, self.channel, self.payload) + + def __repr__(self): + return str(self) + +class Assembler(Framer): + + def __init__(self, sock, max_payload = Frame.MAX_PAYLOAD): + Framer.__init__(self, sock) + self.max_payload = max_payload + self.fragments = {} + + def read_segment(self): + while True: + frame = self.read_frame() + + key = (frame.channel, frame.track) + seg = self.fragments.get(key) + if seg == None: + seg = Segment(frame.isFirstSegment(), frame.isLastSegment(), + frame.type, frame.track, frame.channel, "") + self.fragments[key] = seg + + seg.payload += frame.payload + + if frame.isLastFrame(): + self.fragments.pop(key) + return seg + + def write_segment(self, segment): + remaining = segment.payload + + first = True + while remaining: + payload = remaining[:self.max_payload] + remaining = remaining[self.max_payload:] + + flags = 0 + if first: + flags |= FIRST_FRM + first = False + if not remaining: + flags |= LAST_FRM + if segment.first: + flags |= FIRST_SEG + if segment.last: + flags |= LAST_SEG + + frame = Frame(flags, segment.type, segment.track, segment.channel, + payload) + self.write_frame(frame) diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py new file mode 100644 index 0000000000..5894981fc6 --- /dev/null +++ b/qpid/python/qpid/codec010.py @@ -0,0 +1,186 @@ +# +# 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. +# + +from packer import Packer + +class Codec(Packer): + + def __init__(self, spec): + self.spec = spec + + def write_bit(self, b): + if not b: raise ValueError(b) + def read_bit(self): + return True + + def read_uint8(self): + return self.unpack("!B") + def write_uint8(self, n): + return self.pack("!B", n) + + def read_int8(self): + return self.unpack("!b") + def write_int8(self, n): + self.pack("!b", n) + + def read_char(self): + return self.unpack("!c") + def write_char(self, c): + self.pack("!c", c) + + def read_boolean(self): + return self.read_uint8() != 0 + def write_boolean(self, b): + if b: n = 1 + else: n = 0 + self.write_uint8(n) + + + def read_uint16(self): + return self.unpack("!H") + def write_uint16(self, n): + self.pack("!H", n) + + def read_int16(self): + return self.unpack("!h") + def write_int16(self, n): + return self.unpack("!h", n) + + + def read_uint32(self): + return self.unpack("!L") + def write_uint32(self, n): + self.pack("!L", n) + + def read_int32(self): + return self.unpack("!l") + def write_int32(self, n): + self.pack("!l", n) + + def read_float(self): + return self.unpack("!f") + def write_float(self, f): + self.pack("!f", f) + + def read_sequence_no(self): + return self.read_uint32() + def write_sequence_no(self, n): + self.write_uint32(n) + + + def read_uint64(self): + return self.unpack("!Q") + def write_uint64(self, n): + self.pack("!Q", n) + + def read_int64(self): + return self.unpack("!q") + def write_int64(self, n): + self.pack("!q", n) + + def read_double(self): + return self.unpack("!d") + def write_double(self, d): + self.pack("!d", d) + + + def read_vbin8(self): + return self.read(self.read_uint8()) + def write_vbin8(self, b): + self.write_uint8(len(b)) + self.write(b) + + def read_str8(self): + return self.read_vbin8().decode("utf8") + def write_str8(self, s): + self.write_vbin8(s.encode("utf8")) + + + def read_vbin16(self): + return self.read(self.read_uint16()) + def write_vbin16(self, b): + self.write_uint16(len(b)) + self.write(b) + + def read_vbin32(self): + return self.read(self.read_uint32()) + def write_vbin32(self, b): + self.write_uint32(len(b)) + self.write(b) + + def write_map(self, m): + pass + def read_map(self): + pass + + def write_array(self, a): + pass + def read_array(self): + pass + + def read_struct32(self): + size = self.read_uint32() + code = self.read_uint16() + struct = self.spec.structs[code] + return struct.decode_fields(self) + def write_struct32(self, value): + sc = StringCodec(self.spec) + sc.write_uint16(value.type.code) + value.type.encode_fields(sc, value) + self.write_vbin32(sc.encoded) + + def read_control(self): + cntrl = self.spec.controls[self.read_uint16()] + return cntrl.decode(self) + def write_control(self, type, ctrl): + self.write_uint16(type.code) + type.encode(self, ctrl) + + def read_command(self): + cmd = self.spec.commands[self.read_uint16()] + return cmd.decode(self) + def write_command(self, type, cmd): + self.write_uint16(type.code) + type.encode(self, cmd) + + def read_size(self, width): + if width > 0: + attr = "read_uint%d" % (width*8) + return getattr(self, attr)() + + def write_size(self, width, n): + if width > 0: + attr = "write_uint%d" % (width*8) + getattr(self, attr)(n) + + + +class StringCodec(Codec): + + def __init__(self, spec, encoded = ""): + Codec.__init__(self, spec) + self.encoded = encoded + + def write(self, s): + self.encoded += s + + def read(self, n): + result = self.encoded[:n] + self.encoded = self.encoded[n:] + return result diff --git a/qpid/python/qpid/connection010.py b/qpid/python/qpid/connection010.py new file mode 100644 index 0000000000..b25efd37a8 --- /dev/null +++ b/qpid/python/qpid/connection010.py @@ -0,0 +1,172 @@ +# +# 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 datatypes, session +from threading import Thread, Event, RLock +from framer import Closed +from assembler import Assembler, Segment +from codec010 import StringCodec +from session import Session +from invoker import Invoker +from spec010 import Control, Command +import delegates + +class Timeout(Exception): pass + +class ChannelBusy(Exception): pass + +class ChannelsBusy(Exception): pass + +class SessionBusy(Exception): pass + +def client(*args): + return delegates.Client(*args) + +def server(*args): + return delegates.Server(*args) + +class Connection(Assembler): + + def __init__(self, sock, spec, delegate=client): + Assembler.__init__(self, sock) + self.spec = spec + self.track = self.spec["track"] + self.delegate = delegate(self) + self.attached = {} + self.sessions = {} + self.lock = RLock() + self.thread = Thread(target=self.run) + self.thread.setDaemon(True) + self.opened = Event() + self.closed = Event() + self.channel_max = 65535 + + def attach(self, name, ch, delegate, force=False): + self.lock.acquire() + try: + ssn = self.attached.get(ch.id) + if ssn is not None: + if ssn.name != name: + raise ChannelBusy(ch, ssn) + else: + ssn = self.sessions.get(name) + if ssn is None: + ssn = Session(name, self.spec, delegate=delegate) + self.sessions[name] = ssn + elif ssn.channel is not None: + if force: + del self.attached[ssn.channel.id] + ssn.channel = None + else: + raise SessionBusy(ssn) + self.attached[ch.id] = ssn + ssn.channel = ch + ch.session = ssn + return ssn + finally: + self.lock.release() + + def detach(self, name, ch): + self.lock.acquire() + try: + self.attached.pop(ch.id, None) + ssn = self.sessions.pop(name, None) + if ssn is not None: + ssn.channel = None + return ssn + finally: + self.lock.release() + + def __channel(self): + # XXX: ch 0? + for i in xrange(self.channel_max): + if not self.attached.has_key(i): + return i + else: + raise ChannelsBusy() + + def session(self, name, timeout=None, delegate=session.client): + self.lock.acquire() + try: + ssn = self.attach(name, Channel(self, self.__channel()), delegate) + ssn.channel.session_attach(name) + ssn.opened.wait(timeout) + if ssn.opened.isSet(): + return ssn + else: + raise Timeout() + finally: + self.lock.release() + + def start(self, timeout=None): + self.delegate.start() + self.thread.start() + self.opened.wait(timeout=timeout) + if not self.opened.isSet(): + raise Timeout() + + def run(self): + # XXX: we don't really have a good way to exit this loop without + # getting the other end to kill the socket + while True: + try: + seg = self.read_segment() + except Closed: + break + self.delegate.received(seg) + + def close(self, timeout=None): + Channel(self, 0).connection_close() + self.closed.wait(timeout=timeout) + if not self.closed.isSet(): + raise Timeout() + self.thread.join(timeout=timeout) + + def __str__(self): + return "%s:%s" % self.sock.getsockname() + + def __repr__(self): + return str(self) + +class Channel(Invoker): + + def __init__(self, connection, id): + self.connection = connection + self.id = id + self.session = None + + def resolve_method(self, name): + inst = self.connection.spec.instructions.get(name) + if inst is not None and isinstance(inst, Control): + return inst + else: + return None + + def invoke(self, type, args, kwargs): + cntrl = type.new(args, kwargs) + sc = StringCodec(self.connection.spec) + sc.write_control(type, cntrl) + self.connection.write_segment(Segment(True, True, type.segment_type, + type.track, self.id, sc.encoded)) + + def __str__(self): + return "%s[%s]" % (self.connection, self.id) + + def __repr__(self): + return str(self) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py new file mode 100644 index 0000000000..9e3177154e --- /dev/null +++ b/qpid/python/qpid/datatypes.py @@ -0,0 +1,112 @@ +# +# 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 threading + +class Struct: + + def __init__(self, fields): + self.__dict__ = fields + + def __repr__(self): + return "Struct(%s)" % ", ".join(["%s=%r" % (k, v) + for k, v in self.__dict__.items()]) + + def fields(self): + return self.__dict__ + +class Message: + + def __init__(self, body): + self.headers = None + self.body = body + +class Range: + + def __init__(self, lower, upper): + self.lower = lower + self.upper = upper + + def __contains__(self, n): + return self.lower <= n and n <= self.upper + + def touches(self, r): + return (self.lower - 1 in r or + self.upper + 1 in r or + r.lower - 1 in self or + r.upper + 1 in self) + + def span(self, r): + return Range(min(self.lower, r.lower), max(self.upper, r.upper)) + + def __str__(self): + return "Range(%s, %s)" % (self.lower, self.upper) + + def __repr__(self): + return str(self) + +class RangeSet: + + def __init__(self): + self.ranges = [] + + def __contains__(self, n): + for r in self.ranges: + if n in r: + return True + return False + + def add_range(self, range): + idx = 0 + while idx < len(self.ranges): + r = self.ranges[idx] + if range.touches(r): + del self.ranges[idx] + range = range.span(r) + elif range.upper < r.lower: + self.ranges.insert(idx, range) + return + else: + idx += 1 + self.ranges.append(range) + + def add(self, n): + self.add_range(Range(n, n)) + + def __str__(self): + return "RangeSet(%s)" % str(self.ranges) + + def __repr__(self): + return str(self) + +class Future: + def __init__(self, initial=None): + self.value = initial + self._set = threading.Event() + + def set(self, value): + self.value = value + self._set.set() + + def get(self, timeout=None): + self._set.wait(timeout) + return self.value + + def is_set(self): + return self._set.isSet() diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py new file mode 100644 index 0000000000..4fdcc37384 --- /dev/null +++ b/qpid/python/qpid/delegates.py @@ -0,0 +1,111 @@ +# +# 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 connection010 +import session + +class Delegate: + + def __init__(self, connection, delegate=session.client): + self.connection = connection + self.spec = connection.spec + self.delegate = delegate + self.control = self.spec["track.control"].value + + def received(self, seg): + ssn = self.connection.attached.get(seg.channel) + if ssn is None: + ch = connection010.Channel(self.connection, seg.channel) + else: + ch = ssn.channel + + if seg.track == self.control: + cntrl = seg.decode(self.spec) + attr = cntrl.type.qname.replace(".", "_") + getattr(self, attr)(ch, cntrl) + elif ssn is None: + ch.session_detached() + else: + ssn.received(seg) + + def connection_close(self, ch, close): + ch.connection_close_ok() + self.connection.sock.close() + + def connection_close_ok(self, ch, close_ok): + self.connection.closed.set() + + def session_attach(self, ch, a): + try: + self.connection.attach(a.name, ch, self.delegate, a.force) + ch.session_attached(a.name) + except connection010.ChannelBusy: + ch.session_detached(a.name) + except connection010.SessionBusy: + ch.session_detached(a.name) + + def session_attached(self, ch, a): + ch.session.opened.set() + + def session_detach(self, ch, d): + self.connection.detach(d.name, ch) + ch.session_detached(d.name) + + def session_detached(self, ch, d): + ssn = self.connection.detach(d.name, ch) + if ssn is not None: + ssn.closed.set() + + def session_command_point(self, ch, cp): + ssn = ch.session + ssn.receiver.next_id = cp.command_id + ssn.receiver.next_offset = cp.command_offset + +class Server(Delegate): + + def start(self): + self.connection.read_header() + self.connection.write_header(self.spec.major, self.spec.minor) + connection010.Channel(self.connection, 0).connection_start() + + def connection_start_ok(self, ch, start_ok): + ch.connection_tune() + + def connection_tune_ok(self, ch, tune_ok): + pass + + def connection_open(self, ch, open): + self.connection.opened.set() + ch.connection_open_ok() + +class Client(Delegate): + + def start(self): + self.connection.write_header(self.spec.major, self.spec.minor) + self.connection.read_header() + + def connection_start(self, ch, start): + ch.connection_start_ok() + + def connection_tune(self, ch, tune): + ch.connection_tune_ok() + ch.connection_open() + + def connection_open_ok(self, ch, open_ok): + self.connection.opened.set() diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py new file mode 100644 index 0000000000..adc52cc3bd --- /dev/null +++ b/qpid/python/qpid/framer.py @@ -0,0 +1,124 @@ +# +# 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 struct, socket +from packer import Packer + +FIRST_SEG = 0x08 +LAST_SEG = 0x04 +FIRST_FRM = 0x02 +LAST_FRM = 0x01 + +class Frame: + + HEADER = "!2BHxBH4x" + MAX_PAYLOAD = 65535 - struct.calcsize(HEADER) + + def __init__(self, flags, type, track, channel, payload): + if len(payload) > Frame.MAX_PAYLOAD: + raise ValueError("max payload size exceeded: %s" % len(payload)) + self.flags = flags + self.type = type + self.track = track + self.channel = channel + self.payload = payload + + def isFirstSegment(self): + return bool(FIRST_SEG & self.flags) + + def isLastSegment(self): + return bool(LAST_SEG & self.flags) + + def isFirstFrame(self): + return bool(FIRST_FRM & self.flags) + + def isLastFrame(self): + return bool(LAST_FRM & self.flags) + + def __str__(self): + return "%s%s%s%s %s %s %s %r" % (int(self.isFirstSegment()), + int(self.isLastSegment()), + int(self.isFirstFrame()), + int(self.isLastFrame()), + self.type, + self.track, + self.channel, + self.payload) + +class Closed(Exception): pass + +class Framer(Packer): + + HEADER="!4s4B" + + def __init__(self, sock): + self.sock = sock + + def aborted(self): + return False + + def write(self, buf): +# print "OUT: %r" % buf + while buf: + try: + n = self.sock.send(buf) + except socket.timeout: + if self.aborted(): + raise Closed() + else: + continue + buf = buf[n:] + + def read(self, n): + data = "" + while len(data) < n: + try: + s = self.sock.recv(n - len(data)) + except socket.timeout: + if self.aborted(): + raise Closed() + else: + continue + except socket.error, e: + if data != "": + raise e + else: + raise Closed() + if len(s) == 0: + raise Closed() +# print "IN: %r" % s + data += s + return data + + def read_header(self): + return self.unpack(Framer.HEADER) + + def write_header(self, major, minor): + self.pack(Framer.HEADER, "AMQP", 1, 1, major, minor) + + def write_frame(self, frame): + size = len(frame.payload) + struct.calcsize(Frame.HEADER) + track = frame.track & 0x0F + self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) + self.write(frame.payload) + + def read_frame(self): + flags, type, size, track, channel = self.unpack(Frame.HEADER) + payload = self.read(size - struct.calcsize(Frame.HEADER)) + return Frame(flags, type, track, channel, payload) diff --git a/qpid/python/qpid/invoker.py b/qpid/python/qpid/invoker.py new file mode 100644 index 0000000000..9e6f6943d8 --- /dev/null +++ b/qpid/python/qpid/invoker.py @@ -0,0 +1,32 @@ +# +# 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. +# + +class Invoker: + + def resolve_method(self, name): + pass + + def __getattr__(self, name): + resolved = self.resolve_method(name) + if resolved == None: + raise AttributeError("%s instance has no attribute '%s'" % + (self.__class__.__name__, name)) + method = lambda *args, **kwargs: self.invoke(resolved, args, kwargs) + self.__dict__[name] = method + return method diff --git a/qpid/python/qpid/packer.py b/qpid/python/qpid/packer.py new file mode 100644 index 0000000000..22c16918dc --- /dev/null +++ b/qpid/python/qpid/packer.py @@ -0,0 +1,36 @@ +# +# 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 struct + +class Packer: + + def read(self, n): abstract + + def write(self, s): abstract + + def unpack(self, fmt): + values = struct.unpack(fmt, self.read(struct.calcsize(fmt))) + if len(values) == 1: + return values[0] + else: + return values + + def pack(self, fmt, *args): + self.write(struct.pack(fmt, *args)) diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py new file mode 100644 index 0000000000..2e5f47b63e --- /dev/null +++ b/qpid/python/qpid/session.py @@ -0,0 +1,208 @@ +# +# 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. +# + +from threading import Event +from invoker import Invoker +from datatypes import RangeSet, Struct, Future +from codec010 import StringCodec +from assembler import Segment + +class SessionDetached(Exception): pass + +def client(*args): + return Client(*args) + +def server(*args): + return Server(*args) + +class Session(Invoker): + + def __init__(self, name, spec, sync=True, timeout=10, delegate=client): + self.name = name + self.spec = spec + self.sync = sync + self.timeout = timeout + self.channel = None + self.opened = Event() + self.closed = Event() + self.receiver = Receiver(self) + self.sender = Sender(self) + self.delegate = delegate(self) + self.send_id = True + self.results = {} + + def close(self, timeout=None): + self.channel.session_detach(self.name) + self.closed.wait(timeout=timeout) + + def resolve_method(self, name): + cmd = self.spec.instructions.get(name) + if cmd is not None and cmd.track == self.spec["track.command"].value: + return cmd + else: + return None + + def invoke(self, type, args, kwargs): + if self.channel == None: + raise SessionDetached() + + if type.segments: + if len(args) == len(type.fields) + 1: + message = args[-1] + args = args[:-1] + else: + message = kwargs.pop("message", None) + else: + message = None + + cmd = type.new(args, kwargs) + sc = StringCodec(self.spec) + sc.write_command(type, cmd) + + seg = Segment(True, (message == None or + (message.headers == None and message.body == None)), + type.segment_type, type.track, self.channel.id, sc.encoded) + + if type.result: + result = Future() + self.results[self.sender.next_id] = result + + self.send(seg) + + if message != None: + if message.headers != None: + sc = StringCodec(self.spec) + for st in message.headers: + sc.write_struct32(st.type, st) + seg = Segment(False, message.body == None, self.spec["segment_type.header"].value, + type.track, self.channel.id, sc.encoded) + self.send(seg) + if message.body != None: + seg = Segment(False, True, self.spec["segment_type.body"].value, + type.track, self.channel.id, message.body) + self.send(seg) + + if type.result: + if self.sync: + return result.get(self.timeout) + else: + return result + + def received(self, seg): + self.receiver.received(seg) + if seg.type == self.spec["segment_type.command"].value: + cmd = seg.decode(self.spec) + attr = cmd.type.qname.replace(".", "_") + result = getattr(self.delegate, attr)(cmd) + if cmd.type.result: + self.execution_result(seg.id, result) + elif seg.type == self.spec["segment_type.header"].value: + self.delegate.header(seg.decode(self.spec)) + elif seg.type == self.spec["segment_type.body"].value: + self.delegate.body(seg.decode(self.spec)) + else: + raise ValueError("unknown segment type: %s" % seg.type) + self.receiver.completed(seg) + + def send(self, seg): + self.sender.send(seg) + + def __str__(self): + return '' % (self.name, self.channel) + + def __repr__(self): + return str(self) + +class Receiver: + + def __init__(self, session): + self.session = session + self.next_id = None + self.next_offset = None + self._completed = RangeSet() + + def received(self, seg): + if self.next_id == None or self.next_offset == None: + raise Exception("todo") + seg.id = self.next_id + seg.offset = self.next_offset + if seg.last: + self.next_id += 1 + self.next_offset = 0 + else: + self.next_offset += len(seg.payload) + + def completed(self, seg): + if seg.id == None: + raise ValueError("cannot complete unidentified segment") + if seg.last: + self._completed.add(seg.id) + +class Sender: + + def __init__(self, session): + self.session = session + self.next_id = 0 + self.next_offset = 0 + self.segments = [] + + def send(self, seg): + seg.id = self.next_id + seg.offset = self.next_offset + if seg.last: + self.next_id += 1 + self.next_offset = 0 + else: + self.next_offset += len(seg.payload) + self.segments.append(seg) + if self.session.send_id: + self.session.send_id = False + self.session.channel.session_command_point(seg.id, seg.offset) + self.session.channel.connection.write_segment(seg) + + def completed(self, commands): + idx = 0 + while idx < len(self.segments): + seg = self.segments[idx] + if seg.id in commands: + del self.segments[idx] + else: + idx += 1 + +from queue import Queue, Closed, Empty + +class Delegate: + + def __init__(self, session): + self.session = session + + def execution_result(self, er): + future = self.session.results[er.command_id] + future.set(er.value) + +class Client(Delegate): + + def message_transfer(self, cmd): + print "TRANSFER:", cmd + + def header(self, hdr): + print "HEADER:", hdr + + def body(self, seg): + print "BODY:", seg diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py new file mode 100644 index 0000000000..2d8bd6050d --- /dev/null +++ b/qpid/python/qpid/spec010.py @@ -0,0 +1,617 @@ +# +# 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 datatypes +from codec010 import StringCodec + +class Node: + + def __init__(self, children): + self.children = children + self.named = {} + self.docs = [] + self.rules = [] + + def register(self): + for ch in self.children: + ch.register(self) + + def resolve(self): + for ch in self.children: + ch.resolve() + + def __getitem__(self, name): + path = name.split(".", 1) + nd = self.named + for step in path: + nd = nd[step] + return nd + + def __iter__(self): + return iter(self.children) + +class Anonymous: + + def __init__(self, children): + self.children = children + + def register(self, node): + for ch in self.children: + ch.register(node) + + def resolve(self): + for ch in self.children: + ch.resolve() + +class Named: + + def __init__(self, name): + self.name = name + self.qname = None + + def register(self, node): + self.spec = node.spec + self.klass = node.klass + node.named[self.name] = self + if node.qname: + self.qname = "%s.%s" % (node.qname, self.name) + else: + self.qname = self.name + + def __str__(self): + return self.qname + + def __repr__(self): + return str(self) + +class Lookup: + + def lookup(self, name): + value = None + if self.klass: + try: + value = self.klass[name] + except KeyError: + pass + if not value: + value = self.spec[name] + return value + +class Coded: + + def __init__(self, code): + self.code = code + +class Constant(Named, Node): + + def __init__(self, name, value, children): + Named.__init__(self, name) + Node.__init__(self, children) + self.value = value + + def register(self, node): + Named.register(self, node) + node.constants.append(self) + Node.register(self) + +class Type(Named, Node): + + def __init__(self, name, children): + Named.__init__(self, name) + Node.__init__(self, children) + + def is_present(self, value): + return value != None + + def register(self, node): + Named.register(self, node) + node.types.append(self) + Node.register(self) + +class Primitive(Coded, Type): + + def __init__(self, name, code, fixed, variable, children): + Coded.__init__(self, code) + Type.__init__(self, name, children) + self.fixed = fixed + self.variable = variable + + def is_present(self, value): + if self.fixed == 0: + return value + else: + return Type.is_present(self, value) + + def encode(self, codec, value): + getattr(codec, "write_%s" % self.name)(value) + + def decode(self, codec): + return getattr(codec, "read_%s" % self.name)() + +class Domain(Type, Lookup): + + def __init__(self, name, type, children): + Type.__init__(self, name, children) + self.type = type + self.choices = {} + + def resolve(self): + self.type = self.lookup(self.type) + Node.resolve(self) + + def encode(self, codec, value): + self.type.encode(codec, value) + + def decode(self, codec): + return self.type.decode(codec) + +class Choice(Named, Node): + + def __init__(self, name, value, children): + Named.__init__(self, name) + Node.__init__(self, children) + self.value = value + + def register(self, node): + Named.register(self, node) + node.choices[self.value] = self + Node.register(self) + +class Composite(Type, Coded): + + def __init__(self, name, code, size, pack, children): + Coded.__init__(self, code) + Type.__init__(self, name, children) + self.fields = [] + self.size = size + self.pack = pack + + def new(self, args, kwargs): + if len(args) > len(self.fields): + raise TypeError("%s takes at most %s arguments (%s given)" % + (self.name, len(self.fields), len(self.args))) + + result = {"type": self} + + for a, f, in zip(args, self.fields): + result[f.name] = a + + for k, v in kwargs.items(): + f = self.named.get(k, None) + if f == None: + raise TypeError("%s got an unexpected keyword argument '%s'" % + (self.name, k)) + result[f.name] = v + + return datatypes.Struct(result) + + def decode(self, codec): + codec.read_size(self.size) + return self.decode_fields(codec) + + def decode_fields(self, codec): + flags = 0 + for i in range(self.pack): + flags |= (codec.read_uint8() << 8*i) + + result = {"type": self} + + for i in range(len(self.fields)): + f = self.fields[i] + if flags & (0x1 << i): + result[f.name] = f.type.decode(codec) + else: + result[f.name] = None + return datatypes.Struct(result) + + def encode(self, codec, value): + sc = StringCodec(self.spec) + self.encode_fields(sc, value) + codec.write_size(self.size, len(sc.encoded)) + codec.write(sc.encoded) + + def encode_fields(self, codec, value): + values = value.__dict__ + flags = 0 + for i in range(len(self.fields)): + f = self.fields[i] + if f.type.is_present(values.get(f.name, None)): + flags |= (0x1 << i) + for i in range(self.pack): + codec.write_uint8((flags >> 8*i) & 0xFF) + for i in range(len(self.fields)): + f = self.fields[i] + if flags & (0x1 << i): + f.type.encode(codec, values[f.name]) + +class Field(Named, Node, Lookup): + + def __init__(self, name, type, children): + Named.__init__(self, name) + Node.__init__(self, children) + self.type = type + self.exceptions = [] + + def register(self, node): + Named.register(self, node) + node.fields.append(self) + Node.register(self) + + def resolve(self): + self.type = self.lookup(self.type) + Node.resolve(self) + + def __str__(self): + return "%s: %s" % (self.qname, self.type.qname) + +class Struct(Composite): + + def register(self, node): + Composite.register(self, node) + self.spec.structs[self.code] = self + + def __str__(self): + fields = ",\n ".join(["%s: %s" % (f.name, f.type.qname) + for f in self.fields]) + return "%s {\n %s\n}" % (self.qname, fields) + +class Segment(Node): + + def register(self, node): + self.spec = node.spec + self.klass = node.klass + node.segments.append(self) + Node.register(self) + +class Instruction(Composite, Segment): + + def __init__(self, name, code, children): + Composite.__init__(self, name, code, 0, 2, children) + self.segment_type = None + self.track = None + self.handlers = [] + + def __str__(self): + return "%s(%s)" % (self.qname, ", ".join(["%s: %s" % (f.name, f.type.qname) + for f in self.fields])) + + def register(self, node): + Composite.register(self, node) + self.spec.instructions[self.qname.replace(".", "_")] = self + +class Control(Instruction): + + def __init__(self, name, code, children): + Instruction.__init__(self, name, code, children) + self.response = None + + def register(self, node): + Instruction.register(self, node) + node.controls.append(self) + self.spec.controls[self.code] = self + self.segment_type = self.spec["segment_type.control"].value + self.track = self.spec["track.control"].value + +class Command(Instruction): + + def __init__(self, name, code, children): + Instruction.__init__(self, name, code, children) + self.result = None + self.exceptions = [] + self.segments = [] + + def register(self, node): + Instruction.register(self, node) + node.commands.append(self) + self.header = self.spec["session.header"] + self.spec.commands[self.code] = self + self.segment_type = self.spec["segment_type.command"].value + self.track = self.spec["track.command"].value + + def decode(self, codec): + hdr = self.header.decode(codec) + args = Instruction.decode(self, codec) + result = {} + result.update(hdr.fields()) + result.update(args.fields()) + return datatypes.Struct(result) + + def encode(self, codec, cmd): + self.header.encode(codec, cmd) + Instruction.encode(self, codec, cmd) + +class Header(Segment): + + def __init__(self, children): + self.entries = [] + Segment.__init__(self, children) + +class Entry(Lookup): + + def __init__(self, type): + self.type = type + + def register(self, node): + self.spec = node.spec + self.klass = node.klass + node.entries.append(self) + + def resolve(self): + self.type = self.lookup(self.type) + +class Body(Segment): + + def resolve(self): pass + +class Class(Named, Coded, Node): + + def __init__(self, name, code, children): + Named.__init__(self, name) + Coded.__init__(self, code) + Node.__init__(self, children) + self.types = [] + self.controls = [] + self.commands = [] + + def register(self, node): + Named.register(self, node) + self.klass = self + node.classes.append(self) + Node.register(self) + +class Doc: + + def __init__(self, type, title, text): + self.type = type + self.title = title + self.text = text + + def register(self, node): + node.docs.append(self) + + def resolve(self): pass + +class Role(Named, Node): + + def __init__(self, name, children): + Named.__init__(self, name) + Node.__init__(self, children) + + def register(self, node): + Named.register(self, node) + Node.register(self) + +class Rule(Named, Node): + + def __init__(self, name, children): + Named.__init__(self, name) + Node.__init__(self, children) + + def register(self, node): + Named.register(self, node) + node.rules.append(self) + Node.register(self) + +class Exception(Named, Node): + + def __init__(self, name, error_code, children): + Named.__init__(self, name) + Node.__init__(self, children) + self.error_code = error_code + + def register(self, node): + Named.register(self, node) + node.exceptions.append(self) + Node.register(self) + +class Spec(Node): + + def __init__(self, major, minor, port, children): + Node.__init__(self, children) + self.major = major + self.minor = minor + self.port = port + self.constants = [] + self.classes = [] + self.types = [] + self.qname = None + self.spec = self + self.klass = None + self.instructions = {} + self.controls = {} + self.commands = {} + self.structs = {} + +class Implement: + + def __init__(self, handle): + self.handle = handle + + def register(self, node): + node.handlers.append(self.handle) + + def resolve(self): pass + +class Response(Node): + + def __init__(self, name, children): + Node.__init__(self, children) + self.name = name + + def register(self, node): + Node.register(self) + +class Result(Node, Lookup): + + def __init__(self, type, children): + self.type = type + Node.__init__(self, children) + + def register(self, node): + node.result = self + self.qname = node.qname + self.klass = node.klass + self.spec = node.spec + Node.register(self) + + def resolve(self): + self.type = self.lookup(self.type) + Node.resolve(self) + +import mllib + +def num(s): + if s: return int(s, 0) + +REPLACE = {" ": "_", "-": "_"} +KEYWORDS = {"global": "global_", + "return": "return_"} + +def id(name): + name = str(name) + for key, val in REPLACE.items(): + name = name.replace(key, val) + try: + name = KEYWORDS[name] + except KeyError: + pass + return name + +class Loader: + + def __init__(self): + self.class_code = 0 + + def code(self, nd): + c = num(nd["@code"]) + if c is None: + return None + else: + return c | (self.class_code << 8) + + def list(self, q): + result = [] + for nd in q: + result.append(nd.dispatch(self)) + return result + + def children(self, n): + return self.list(n.query["#tag"]) + + def data(self, d): + return d.data + + def do_amqp(self, a): + return Spec(num(a["@major"]), num(a["@minor"]), num(a["@port"]), + self.children(a)) + + def do_type(self, t): + return Primitive(id(t["@name"]), self.code(t), num(t["@fixed-width"]), + num(t["@variable-width"]), self.children(t)) + + def do_constant(self, c): + return Constant(id(c["@name"]), num(c["@value"]), self.children(c)) + + def do_domain(self, d): + return Domain(id(d["@name"]), id(d["@type"]), self.children(d)) + + def do_enum(self, e): + return Anonymous(self.children(e)) + + def do_choice(self, c): + return Choice(id(c["@name"]), num(c["@value"]), self.children(c)) + + def do_class(self, c): + code = num(c["@code"]) + self.class_code = code + children = self.children(c) + children += self.list(c.query["command/result/struct"]) + self.class_code = 0 + return Class(id(c["@name"]), code, children) + + def do_doc(self, doc): + text = reduce(lambda x, y: x + y, self.list(doc.children)) + return Doc(doc["@type"], doc["@title"], text) + + def do_xref(self, x): + return x["@ref"] + + def do_role(self, r): + return Role(id(r["@name"]), self.children(r)) + + def do_control(self, c): + return Control(id(c["@name"]), self.code(c), self.children(c)) + + def do_rule(self, r): + return Rule(id(r["@name"]), self.children(r)) + + def do_implement(self, i): + return Implement(id(i["@handle"])) + + def do_response(self, r): + return Response(id(r["@name"]), self.children(r)) + + def do_field(self, f): + return Field(id(f["@name"]), id(f["@type"]), self.children(f)) + + def do_struct(self, s): + return Struct(id(s["@name"]), self.code(s), num(s["@size"]), + num(s["@pack"]), self.children(s)) + + def do_command(self, c): + return Command(id(c["@name"]), self.code(c), self.children(c)) + + def do_segments(self, s): + return Anonymous(self.children(s)) + + def do_header(self, h): + return Header(self.children(h)) + + def do_entry(self, e): + return Entry(id(e["@type"])) + + def do_body(self, b): + return Body(self.children(b)) + + def do_result(self, r): + type = r["@type"] + if not type: + type = r["struct/@name"] + return Result(id(type), self.list(r.query["#tag", lambda x: x.name != "struct"])) + + def do_exception(self, e): + return Exception(id(e["@name"]), id(e["@error-code"]), self.children(e)) + +import os, cPickle + +def load(xml): + fname = xml + ".pcl" + if os.path.exists(fname): + file = open(fname, "r") + s = cPickle.load(file) + file.close() + else: + doc = mllib.xml_parse(xml) + s = doc["amqp"].dispatch(Loader()) + s.register() + s.resolve() + file = open(fname, "w") + cPickle.dump(s, file) + file.close() + return s diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py new file mode 100644 index 0000000000..c88cc0c9d6 --- /dev/null +++ b/qpid/python/qpid/util.py @@ -0,0 +1,39 @@ +# +# 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 socket + +def connect(host, port): + sock = socket.socket() + sock.connect((host, port)) + sock.setblocking(1) + # XXX: we could use this on read, but we'd have to put write in a + # loop as well + # sock.settimeout(1) + return sock + +def listen(host, port, predicate = lambda: True, bound = lambda: None): + sock = socket.socket() + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((host, port)) + bound() + sock.listen(5) + while predicate(): + s, a = sock.accept() + yield s diff --git a/qpid/python/server010 b/qpid/python/server010 new file mode 100755 index 0000000000..b0e13d1e9f --- /dev/null +++ b/qpid/python/server010 @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +from qpid import delegates +from qpid.connection010 import Connection +from qpid.util import connect, listen +from qpid.spec010 import load +from qpid.session import Client + +spec = load("../specs/amqp.0-10.xml") + +class Server: + + def connection(self, connection): + return delegates.Server(connection, self.session) + + def session(self, session): + return SessionDelegate(session) + +class SessionDelegate(Client): + + def __init__(self, session): + self.session = session + + def queue_declare(self, qd): + print "Queue %s declared..." % qd.queue + + def queue_query(self, qq): + return qq.type.result.type.new((qq.queue,), {}) + +server = Server() + +for s in listen("0.0.0.0", spec.port): + conn = Connection(s, spec, server.connection) + conn.start(5) diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py index 41dcc705e6..8e9eeb44d6 100644 --- a/qpid/python/tests/__init__.py +++ b/qpid/python/tests/__init__.py @@ -22,3 +22,8 @@ from codec import * from queue import * from spec import * +from framer import * +from assembler import * +from datatypes import * +from connection010 import * +from spec010 import * diff --git a/qpid/python/tests/assembler.py b/qpid/python/tests/assembler.py new file mode 100644 index 0000000000..b76924e59d --- /dev/null +++ b/qpid/python/tests/assembler.py @@ -0,0 +1,77 @@ +# +# 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. +# + +from threading import * +from unittest import TestCase +from qpid.util import connect, listen +from qpid.assembler import * + +PORT = 1234 + +class AssemblerTest(TestCase): + + def setUp(self): + started = Event() + self.running = True + + def run(): + running = True + for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()): + asm = Assembler(s) + try: + asm.write_header(*asm.read_header()[-2:]) + while True: + seg = asm.read_segment() + asm.write_segment(seg) + except Closed: + pass + + self.server = Thread(target=run) + self.server.setDaemon(True) + self.server.start() + + started.wait(3) + + def tearDown(self): + self.running = False + self.server.join() + + def test(self): + asm = Assembler(connect("0.0.0.0", PORT), max_payload = 1) + asm.write_header(0, 10) + asm.write_segment(Segment(True, False, 1, 2, 3, "TEST")) + asm.write_segment(Segment(False, True, 1, 2, 3, "ING")) + + assert asm.read_header() == ("AMQP", 1, 1, 0, 10) + + seg = asm.read_segment() + assert seg.first == True + assert seg.last == False + assert seg.type == 1 + assert seg.track == 2 + assert seg.channel == 3 + assert seg.payload == "TEST" + + seg = asm.read_segment() + assert seg.first == False + assert seg.last == True + assert seg.type == 1 + assert seg.track == 2 + assert seg.channel == 3 + assert seg.payload == "ING" diff --git a/qpid/python/tests/connection010.py b/qpid/python/tests/connection010.py new file mode 100644 index 0000000000..5e4bf983da --- /dev/null +++ b/qpid/python/tests/connection010.py @@ -0,0 +1,137 @@ +# +# 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. +# + +from threading import * +from unittest import TestCase +from qpid.util import connect, listen +from qpid.connection010 import * +from qpid.datatypes import Message +from qpid.testlib import testrunner +from qpid.delegates import Server +from qpid.queue import Queue +from qpid.spec010 import load +from qpid.session import Delegate + +PORT = 1234 + +class TestServer: + + def __init__(self, queue): + self.queue = queue + + def connection(self, connection): + return Server(connection, delegate=self.session) + + def session(self, session): + return TestSession(session, self.queue) + +class TestSession(Delegate): + + def __init__(self, session, queue): + self.session = session + self.queue = queue + + def queue_query(self, qq): + return qq.type.result.type.new((qq.queue,), {}) + + def message_transfer(self, cmd): + self.queue.put(cmd) + + def body(self, body): + self.queue.put(body) + +class ConnectionTest(TestCase): + + def setUp(self): + self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) + self.queue = Queue() + self.running = True + started = Event() + + def run(): + ts = TestServer(self.queue) + for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()): + conn = Connection(s, self.spec, ts.connection) + try: + conn.start(5) + except Closed: + pass + + self.server = Thread(target=run) + self.server.setDaemon(True) + self.server.start() + + started.wait(3) + + def tearDown(self): + self.running = False + connect("0.0.0.0", PORT).close() + self.server.join(3) + + def test(self): + c = Connection(connect("0.0.0.0", PORT), self.spec) + c.start(10) + + ssn1 = c.session("test1") + ssn2 = c.session("test2") + + assert ssn1 == c.sessions["test1"] + assert ssn2 == c.sessions["test2"] + assert ssn1.channel != None + assert ssn2.channel != None + assert ssn1 in c.attached.values() + assert ssn2 in c.attached.values() + + ssn1.close(5) + + assert ssn1.channel == None + assert ssn1 not in c.attached.values() + assert ssn2 in c.sessions.values() + + ssn2.close(5) + + assert ssn2.channel == None + assert ssn2 not in c.attached.values() + assert ssn2 not in c.sessions.values() + + ssn = c.session("session") + + assert ssn.channel != None + assert ssn in c.sessions.values() + + destinations = ("one", "two", "three") + + for d in destinations: + ssn.message_transfer(d) + + for d in destinations: + cmd = self.queue.get(10) + assert cmd.destination == d + + msg = Message("this is a test") + ssn.message_transfer("four", message=msg) + cmd = self.queue.get(10) + assert cmd.destination == "four" + body = self.queue.get(10) + assert body.payload == msg.body + assert body.last + + qq = ssn.queue_query("asdf") + assert qq.queue == "asdf" + c.close(5) diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py new file mode 100644 index 0000000000..cafd53c89f --- /dev/null +++ b/qpid/python/tests/datatypes.py @@ -0,0 +1,91 @@ +# +# 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. +# + +from unittest import TestCase +from qpid.datatypes import * + +class RangeSetTest(TestCase): + + def check(self, ranges): + posts = [] + for range in ranges: + posts.append(range.lower) + posts.append(range.upper) + + sorted = posts[:] + sorted.sort() + + assert posts == sorted + + idx = 1 + while idx + 1 < len(posts): + assert posts[idx] + 1 != posts[idx+1] + idx += 2 + + def test(self): + rs = RangeSet() + + self.check(rs.ranges) + + rs.add(1) + + assert 1 in rs + assert 2 not in rs + assert 0 not in rs + self.check(rs.ranges) + + rs.add(2) + + assert 0 not in rs + assert 1 in rs + assert 2 in rs + assert 3 not in rs + self.check(rs.ranges) + + rs.add(0) + + assert -1 not in rs + assert 0 in rs + assert 1 in rs + assert 2 in rs + assert 3 not in rs + self.check(rs.ranges) + + rs.add(37) + + assert -1 not in rs + assert 0 in rs + assert 1 in rs + assert 2 in rs + assert 3 not in rs + assert 36 not in rs + assert 37 in rs + assert 38 not in rs + self.check(rs.ranges) + + rs.add(-1) + self.check(rs.ranges) + + rs.add(-3) + self.check(rs.ranges) + + rs.add_range(Range(1, 20)) + assert 21 not in rs + assert 20 in rs + self.check(rs.ranges) diff --git a/qpid/python/tests/framer.py b/qpid/python/tests/framer.py new file mode 100644 index 0000000000..ea2e04e954 --- /dev/null +++ b/qpid/python/tests/framer.py @@ -0,0 +1,92 @@ +# +# 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. +# + +from threading import * +from unittest import TestCase +from qpid.util import connect, listen +from qpid.framer import * + +PORT = 1234 + +class FramerTest(TestCase): + + def setUp(self): + self.running = True + started = Event() + def run(): + for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()): + conn = Framer(s) + try: + conn.write_header(*conn.read_header()[-2:]) + while True: + frame = conn.read_frame() + conn.write_frame(frame) + except Closed: + pass + + self.server = Thread(target=run) + self.server.setDaemon(True) + self.server.start() + + started.wait(3) + + def tearDown(self): + self.running = False + self.server.join(3) + + def test(self): + c = Framer(connect("0.0.0.0", PORT)) + + c.write_header(0, 10) + assert c.read_header() == ("AMQP", 1, 1, 0, 10) + + c.write_frame(Frame(FIRST_FRM, 1, 2, 3, "THIS")) + c.write_frame(Frame(0, 1, 2, 3, "IS")) + c.write_frame(Frame(0, 1, 2, 3, "A")) + c.write_frame(Frame(LAST_FRM, 1, 2, 3, "TEST")) + + f = c.read_frame() + assert f.flags & FIRST_FRM + assert not (f.flags & LAST_FRM) + assert f.type == 1 + assert f.track == 2 + assert f.channel == 3 + assert f.payload == "THIS" + + f = c.read_frame() + assert f.flags == 0 + assert f.type == 1 + assert f.track == 2 + assert f.channel == 3 + assert f.payload == "IS" + + f = c.read_frame() + assert f.flags == 0 + assert f.type == 1 + assert f.track == 2 + assert f.channel == 3 + assert f.payload == "A" + + f = c.read_frame() + assert f.flags & LAST_FRM + assert not (f.flags & FIRST_FRM) + assert f.type == 1 + assert f.track == 2 + assert f.channel == 3 + assert f.payload == "TEST" diff --git a/qpid/python/tests/spec010.py b/qpid/python/tests/spec010.py new file mode 100644 index 0000000000..1c520ee323 --- /dev/null +++ b/qpid/python/tests/spec010.py @@ -0,0 +1,60 @@ +# +# 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. +# + +from unittest import TestCase +from qpid.spec010 import load +from qpid.codec010 import Codec, StringCodec +from qpid.testlib import testrunner +from qpid.datatypes import Struct + +class SpecTest(TestCase): + + def setUp(self): + self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) + + def testSessionHeader(self): + hdr = self.spec["session.header"] + sc = StringCodec(self.spec) + hdr.encode(sc, Struct({"sync": True})) + assert sc.encoded == "\x01\x01" + + sc = StringCodec(self.spec) + hdr.encode(sc, Struct({"sync": False})) + assert sc.encoded == "\x01\x00" + + def encdec(self, type, value): + sc = StringCodec(self.spec) + type.encode(sc, value) + decoded = type.decode(sc) + return decoded + + def testMessageProperties(self): + props = Struct({"content_length": 0xDEADBEEF, + "reply_to": + Struct({"exchange": "the exchange name", "routing_key": "the routing key"})}) + dec = self.encdec(self.spec["message.message_properties"], props) + assert props.content_length == dec.content_length + assert props.reply_to.exchange == dec.reply_to.exchange + assert props.reply_to.routing_key == dec.reply_to.routing_key + + def testMessageSubscribe(self): + cmd = Struct({"exclusive": True, "destination": "this is a test"}) + dec = self.encdec(self.spec["message.subscribe"], cmd) + assert cmd.exclusive == dec.exclusive + assert cmd.destination == dec.destination -- cgit v1.2.1 From 47e9a55ae0e729ba7dc10c2800cfb14dc721675f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 4 Mar 2008 20:23:38 +0000 Subject: check the mtime of the cached spec against the code that generates it git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@633623 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec010.py | 7 +++---- qpid/python/qpid/util.py | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 2d8bd6050d..e6b7946e17 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -17,8 +17,9 @@ # under the License. # -import datatypes +import os, cPickle, datatypes from codec010 import StringCodec +from util import mtime class Node: @@ -598,11 +599,9 @@ class Loader: def do_exception(self, e): return Exception(id(e["@name"]), id(e["@error-code"]), self.children(e)) -import os, cPickle - def load(xml): fname = xml + ".pcl" - if os.path.exists(fname): + if os.path.exists(fname) and mtime(fname) > mtime(__file__): file = open(fname, "r") s = cPickle.load(file) file.close() diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index c88cc0c9d6..e41dfc75fb 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -17,7 +17,7 @@ # under the License. # -import socket +import os, socket def connect(host, port): sock = socket.socket() @@ -37,3 +37,6 @@ def listen(host, port, predicate = lambda: True, bound = lambda: None): while predicate(): s, a = sock.accept() yield s + +def mtime(filename): + return os.stat(filename).st_mtime -- cgit v1.2.1 From b1d8edc3d82580ee03b2e2e6aa50eabf46c0c19e Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 5 Mar 2008 11:12:39 +0000 Subject: added logging; fixed deprecation warnings in old codec; filled in datatypes.Message git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@633815 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 4 ++++ qpid/python/qpid/assembler.py | 6 ++++++ qpid/python/qpid/codec.py | 6 +++--- qpid/python/qpid/datatypes.py | 12 +++++++++--- qpid/python/qpid/framer.py | 16 +++++++++++++--- 5 files changed, 35 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 7685af8fd3..91ebc242c6 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -1,10 +1,14 @@ #!/usr/bin/env python +import logging from qpid.connection010 import Connection from qpid.spec010 import load from qpid.util import connect from qpid.datatypes import Message +format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" +logging.basicConfig(level=logging.DEBUG, format=format, datefmt='%H:%M:%S') + spec = load("../specs/amqp.0-10.xml") conn = Connection(connect("0.0.0.0", spec.port), spec) conn.start(timeout=10) diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py index e0e5d3fb72..aac8b80cb4 100644 --- a/qpid/python/qpid/assembler.py +++ b/qpid/python/qpid/assembler.py @@ -19,6 +19,9 @@ from codec010 import StringCodec from framer import * +from logging import getLogger + +log = getLogger("qpid.io.seg") class Segment: @@ -84,6 +87,7 @@ class Assembler(Framer): if frame.isLastFrame(): self.fragments.pop(key) + log.debug("RECV: %s", seg) return seg def write_segment(self, segment): @@ -108,3 +112,5 @@ class Assembler(Framer): frame = Frame(flags, segment.type, segment.track, segment.channel, payload) self.write_frame(frame) + + log.debug("SENT: %s", segment) diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index 1a9372455d..dfa74b6a2f 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -198,7 +198,7 @@ class Codec: if (o < 0 or o > 255): raise ValueError('Valid range of octet is [0,255]') - self.pack("!B", o) + self.pack("!B", int(o)) def decode_octet(self): """ @@ -215,7 +215,7 @@ class Codec: if (o < 0 or o > 65535): raise ValueError('Valid range of short int is [0,65535]: %s' % o) - self.pack("!H", o) + self.pack("!H", int(o)) def decode_short(self): """ @@ -233,7 +233,7 @@ class Codec: if (o < 0 or o > 4294967295): raise ValueError('Valid range of long int is [0,4294967295]') - self.pack("!L", o) + self.pack("!L", int(o)) def decode_long(self): """ diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 9e3177154e..649c8f4d76 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -33,9 +33,15 @@ class Struct: class Message: - def __init__(self, body): - self.headers = None - self.body = body + def __init__(self, *args): + if args: + self.body = args[-1] + else: + self.body = None + if len(args) > 1: + self.headers = args[:-1] + else: + self.headers = None class Range: diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index adc52cc3bd..f4ec53dc07 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -19,6 +19,10 @@ import struct, socket from packer import Packer +from logging import getLogger + +raw = getLogger("qpid.io.raw") +frm = getLogger("qpid.io.frm") FIRST_SEG = 0x08 LAST_SEG = 0x04 @@ -63,6 +67,8 @@ class Frame: class Closed(Exception): pass +class FramingError(Exception): pass + class Framer(Packer): HEADER="!4s4B" @@ -74,7 +80,6 @@ class Framer(Packer): return False def write(self, buf): -# print "OUT: %r" % buf while buf: try: n = self.sock.send(buf) @@ -83,6 +88,7 @@ class Framer(Packer): raise Closed() else: continue + raw.debug("SENT: %r", buf[:n]) buf = buf[n:] def read(self, n): @@ -102,8 +108,8 @@ class Framer(Packer): raise Closed() if len(s) == 0: raise Closed() -# print "IN: %r" % s data += s + raw.debug("RECV: %r", s) return data def read_header(self): @@ -117,8 +123,12 @@ class Framer(Packer): track = frame.track & 0x0F self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) self.write(frame.payload) + frm.debug("SENT: %s", frame) def read_frame(self): flags, type, size, track, channel = self.unpack(Frame.HEADER) + if flags & 0xF0: raise FramingError() payload = self.read(size - struct.calcsize(Frame.HEADER)) - return Frame(flags, type, track, channel, payload) + frame = Frame(flags, type, track, channel, payload) + frm.debug("RECV: %s", frame) + return frame -- cgit v1.2.1 From 864fef8c0523cc64be9ef79a0a09e4402c3101b3 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 5 Mar 2008 11:26:52 +0000 Subject: added frame-end back as a temporary workaround for C++; added a timeout to the hello-010-world session open git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@633820 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 2 +- qpid/python/qpid/framer.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 91ebc242c6..1b4c9aed33 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -13,7 +13,7 @@ spec = load("../specs/amqp.0-10.xml") conn = Connection(connect("0.0.0.0", spec.port), spec) conn.start(timeout=10) -ssn = conn.session("my-session") +ssn = conn.session("my-session", timeout=10) ssn.queue_declare("asdf") diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index f4ec53dc07..3cc200e3da 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -123,12 +123,18 @@ class Framer(Packer): track = frame.track & 0x0F self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) self.write(frame.payload) + # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ + self.write("\xCE") frm.debug("SENT: %s", frame) def read_frame(self): flags, type, size, track, channel = self.unpack(Frame.HEADER) if flags & 0xF0: raise FramingError() payload = self.read(size - struct.calcsize(Frame.HEADER)) + # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ + end = self.read(1) + if end != "\xCE": + raise FramingError() frame = Frame(flags, type, track, channel, payload) frm.debug("RECV: %s", frame) return frame -- cgit v1.2.1 From e8966c3c7752bfeb7eb37573c641fe7d10bdd38e Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 5 Mar 2008 14:39:40 +0000 Subject: added incoming queues for messages; altered session dispatch to send entire assembly to a single handler; added logging switch for hello-010-world git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@633861 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 16 ++++++-- qpid/python/qpid/assembler.py | 6 ++- qpid/python/qpid/datatypes.py | 8 ++++ qpid/python/qpid/session.py | 79 +++++++++++++++++++++++++++----------- qpid/python/qpid/spec010.py | 32 +++++++++++---- qpid/python/server010 | 6 +++ qpid/python/tests/connection010.py | 24 ++++++------ 7 files changed, 124 insertions(+), 47 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 1b4c9aed33..2b762b1296 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -1,13 +1,18 @@ #!/usr/bin/env python -import logging +import sys, logging from qpid.connection010 import Connection from qpid.spec010 import load from qpid.util import connect from qpid.datatypes import Message +if "-v" in sys.argv: + level = logging.DEBUG +else: + level = logging.WARN + format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" -logging.basicConfig(level=logging.DEBUG, format=format, datefmt='%H:%M:%S') +logging.basicConfig(level=level, format=format, datefmt='%H:%M:%S') spec = load("../specs/amqp.0-10.xml") conn = Connection(connect("0.0.0.0", spec.port), spec) @@ -18,10 +23,15 @@ ssn = conn.session("my-session", timeout=10) ssn.queue_declare("asdf") ssn.message_transfer("this", None, None, Message("testing...")) -ssn.message_transfer("is") +ssn.message_transfer("is", None, None, Message("more testing...")) ssn.message_transfer("a") ssn.message_transfer("test") +print ssn.incoming("this").get() +print ssn.incoming("is").get() +print ssn.incoming("a").get() +print ssn.incoming("test").get() + print ssn.queue_query("testing") ssn.close(timeout=10) diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py index aac8b80cb4..fe78baaceb 100644 --- a/qpid/python/qpid/assembler.py +++ b/qpid/python/qpid/assembler.py @@ -46,7 +46,9 @@ class Segment: def decode_command(self, spec): sc = StringCodec(spec, self.payload) - return sc.read_command() + cmd = sc.read_command() + cmd.id = self.id + return cmd def decode_header(self, spec): sc = StringCodec(spec, self.payload) @@ -56,7 +58,7 @@ class Segment: return values def decode_body(self, spec): - return self + return self.payload def __str__(self): return "%s%s %s %s %s %r" % (int(self.first), int(self.last), self.type, diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 649c8f4d76..efd1fdd4ff 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -43,6 +43,14 @@ class Message: else: self.headers = None + def __repr__(self): + args = [] + if self.headers: + args.extend(self.headers) + if self.body: + args.append(self.body) + return "Message(%s)" % ", ".join(map(repr, args)) + class Range: def __init__(self, lower, upper): diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 2e5f47b63e..334902bbf3 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -17,11 +17,14 @@ # under the License. # -from threading import Event +from threading import Event, RLock from invoker import Invoker from datatypes import RangeSet, Struct, Future from codec010 import StringCodec from assembler import Segment +from queue import Queue +from datatypes import Message +from logging import getLogger class SessionDetached(Exception): pass @@ -46,6 +49,20 @@ class Session(Invoker): self.delegate = delegate(self) self.send_id = True self.results = {} + self.lock = RLock() + self._incoming = {} + self.assembly = None + + def incoming(self, destination): + self.lock.acquire() + try: + queue = self._incoming.get(destination) + if queue == None: + queue = Queue() + self._incoming[destination] = queue + return queue + finally: + self.lock.release() def close(self, timeout=None): self.channel.session_detach(self.name) @@ -106,19 +123,37 @@ class Session(Invoker): def received(self, seg): self.receiver.received(seg) - if seg.type == self.spec["segment_type.command"].value: - cmd = seg.decode(self.spec) - attr = cmd.type.qname.replace(".", "_") - result = getattr(self.delegate, attr)(cmd) - if cmd.type.result: - self.execution_result(seg.id, result) - elif seg.type == self.spec["segment_type.header"].value: - self.delegate.header(seg.decode(self.spec)) - elif seg.type == self.spec["segment_type.body"].value: - self.delegate.body(seg.decode(self.spec)) - else: - raise ValueError("unknown segment type: %s" % seg.type) - self.receiver.completed(seg) + if seg.first: + assert self.assembly == None + self.assembly = [] + self.assembly.append(seg) + if seg.last: + self.dispatch(self.assembly) + self.assembly = None + + def dispatch(self, assembly): + cmd = assembly.pop(0).decode(self.spec) + args = [] + + for st in cmd.type.segments: + if assembly: + seg = assembly[0] + if seg.type == st.segment_type: + args.append(seg.decode(self.spec)) + assembly.pop(0) + continue + args.append(None) + + assert len(assembly) == 0 + + attr = cmd.type.qname.replace(".", "_") + result = getattr(self.delegate, attr)(cmd, *args) + + if cmd.type.result: + self.execution_result(cmd.id, result) + + for seg in assembly: + self.receiver.completed(seg) def send(self, seg): self.sender.send(seg) @@ -196,13 +231,13 @@ class Delegate: future = self.session.results[er.command_id] future.set(er.value) -class Client(Delegate): +msg = getLogger("qpid.ssn.msg") - def message_transfer(self, cmd): - print "TRANSFER:", cmd - - def header(self, hdr): - print "HEADER:", hdr +class Client(Delegate): - def body(self, seg): - print "BODY:", seg + def message_transfer(self, cmd, headers, body): + m = Message(body) + m.headers = headers + messages = self.session.incoming(cmd.destination) + messages.put(m) + msg.debug("RECV: %s", m) diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index e6b7946e17..c3f3e6ad57 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -194,7 +194,7 @@ class Composite(Type, Coded): result[f.name] = a for k, v in kwargs.items(): - f = self.named.get(k, None) + f = self.named.get(k) if f == None: raise TypeError("%s got an unexpected keyword argument '%s'" % (self.name, k)) @@ -232,7 +232,7 @@ class Composite(Type, Coded): flags = 0 for i in range(len(self.fields)): f = self.fields[i] - if f.type.is_present(values.get(f.name, None)): + if f.type.is_present(values.get(f.name)): flags |= (0x1 << i) for i in range(self.pack): codec.write_uint8((flags >> 8*i) & 0xFF) @@ -272,7 +272,10 @@ class Struct(Composite): for f in self.fields]) return "%s {\n %s\n}" % (self.qname, fields) -class Segment(Node): +class Segment: + + def __init__(self): + self.segment_type = None def register(self, node): self.spec = node.spec @@ -284,7 +287,7 @@ class Instruction(Composite, Segment): def __init__(self, name, code, children): Composite.__init__(self, name, code, 0, 2, children) - self.segment_type = None + Segment.__init__(self) self.track = None self.handlers = [] @@ -337,11 +340,17 @@ class Command(Instruction): self.header.encode(codec, cmd) Instruction.encode(self, codec, cmd) -class Header(Segment): +class Header(Segment, Node): def __init__(self, children): + Segment.__init__(self) + Node.__init__(self, children) self.entries = [] - Segment.__init__(self, children) + + def register(self, node): + Segment.register(self, node) + self.segment_type = self.spec["segment_type.header"].value + Node.register(self) class Entry(Lookup): @@ -356,7 +365,16 @@ class Entry(Lookup): def resolve(self): self.type = self.lookup(self.type) -class Body(Segment): +class Body(Segment, Node): + + def __init__(self, children): + Segment.__init__(self) + Node.__init__(self, children) + + def register(self, node): + Segment.register(self, node) + self.segment_type = self.spec["segment_type.body"].value + Node.register(self) def resolve(self): pass diff --git a/qpid/python/server010 b/qpid/python/server010 index b0e13d1e9f..6d89ee5ea0 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -5,6 +5,7 @@ from qpid.connection010 import Connection from qpid.util import connect, listen from qpid.spec010 import load from qpid.session import Client +from qpid.datatypes import Message spec = load("../specs/amqp.0-10.xml") @@ -27,6 +28,11 @@ class SessionDelegate(Client): def queue_query(self, qq): return qq.type.result.type.new((qq.queue,), {}) + def message_transfer(self, cmd, header, body): + m = Message(body) + m.header = header + self.session.message_transfer(cmd.destination, cmd.accept_mode, cmd.acquire_mode, m) + server = Server() for s in listen("0.0.0.0", spec.port): diff --git a/qpid/python/tests/connection010.py b/qpid/python/tests/connection010.py index 5e4bf983da..8adf20fd78 100644 --- a/qpid/python/tests/connection010.py +++ b/qpid/python/tests/connection010.py @@ -50,11 +50,8 @@ class TestSession(Delegate): def queue_query(self, qq): return qq.type.result.type.new((qq.queue,), {}) - def message_transfer(self, cmd): - self.queue.put(cmd) - - def body(self, body): - self.queue.put(body) + def message_transfer(self, cmd, header, body): + self.queue.put((cmd, header, body)) class ConnectionTest(TestCase): @@ -88,8 +85,8 @@ class ConnectionTest(TestCase): c = Connection(connect("0.0.0.0", PORT), self.spec) c.start(10) - ssn1 = c.session("test1") - ssn2 = c.session("test2") + ssn1 = c.session("test1", timeout=10) + ssn2 = c.session("test2", timeout=10) assert ssn1 == c.sessions["test1"] assert ssn2 == c.sessions["test2"] @@ -110,7 +107,7 @@ class ConnectionTest(TestCase): assert ssn2 not in c.attached.values() assert ssn2 not in c.sessions.values() - ssn = c.session("session") + ssn = c.session("session", timeout=10) assert ssn.channel != None assert ssn in c.sessions.values() @@ -121,16 +118,17 @@ class ConnectionTest(TestCase): ssn.message_transfer(d) for d in destinations: - cmd = self.queue.get(10) + cmd, header, body = self.queue.get(10) assert cmd.destination == d + assert header == None + assert body == None msg = Message("this is a test") ssn.message_transfer("four", message=msg) - cmd = self.queue.get(10) + cmd, header, body = self.queue.get(10) assert cmd.destination == "four" - body = self.queue.get(10) - assert body.payload == msg.body - assert body.last + assert header == None + assert body == msg.body qq = ssn.queue_query("asdf") assert qq.queue == "asdf" -- cgit v1.2.1 From 4b9b00c9318bdd0338e1d3053e5c5665518bcc7a Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 5 Mar 2008 19:56:43 +0000 Subject: forked python tests for 0-10 preview and 0-10 final fixed result handling in c++ broker modified testlib in python to allow new 0-10 client to be run as well converted query tests for final 0-10 added python tests for 0-10 final to automated set for c++ broker (most unconverted still) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634003 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 95 ++- qpid/python/cpp_failing_0-10_preview.txt | 6 + qpid/python/qpid/codec010.py | 10 +- qpid/python/qpid/testlib.py | 47 +- qpid/python/tests_0-10/alternate_exchange.py | 28 - qpid/python/tests_0-10/broker.py | 38 +- qpid/python/tests_0-10/query.py | 233 +++--- qpid/python/tests_0-10_preview/__init__.py | 32 + .../tests_0-10_preview/alternate_exchange.py | 179 +++++ qpid/python/tests_0-10_preview/broker.py | 111 +++ qpid/python/tests_0-10_preview/dtx.py | 645 ++++++++++++++++ qpid/python/tests_0-10_preview/example.py | 95 +++ qpid/python/tests_0-10_preview/exchange.py | 335 +++++++++ qpid/python/tests_0-10_preview/execution.py | 29 + qpid/python/tests_0-10_preview/message.py | 834 +++++++++++++++++++++ qpid/python/tests_0-10_preview/persistence.py | 67 ++ qpid/python/tests_0-10_preview/query.py | 227 ++++++ qpid/python/tests_0-10_preview/queue.py | 338 +++++++++ qpid/python/tests_0-10_preview/testlib.py | 66 ++ qpid/python/tests_0-10_preview/tx.py | 231 ++++++ 20 files changed, 3469 insertions(+), 177 deletions(-) create mode 100644 qpid/python/cpp_failing_0-10_preview.txt create mode 100644 qpid/python/tests_0-10_preview/__init__.py create mode 100644 qpid/python/tests_0-10_preview/alternate_exchange.py create mode 100644 qpid/python/tests_0-10_preview/broker.py create mode 100644 qpid/python/tests_0-10_preview/dtx.py create mode 100644 qpid/python/tests_0-10_preview/example.py create mode 100644 qpid/python/tests_0-10_preview/exchange.py create mode 100644 qpid/python/tests_0-10_preview/execution.py create mode 100644 qpid/python/tests_0-10_preview/message.py create mode 100644 qpid/python/tests_0-10_preview/persistence.py create mode 100644 qpid/python/tests_0-10_preview/query.py create mode 100644 qpid/python/tests_0-10_preview/queue.py create mode 100644 qpid/python/tests_0-10_preview/testlib.py create mode 100644 qpid/python/tests_0-10_preview/tx.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 77031cad08..974c165ae9 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,6 +1,97 @@ tests.codec.FieldTableTestCase.test_field_table_decode tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair -tests_0-10.alternate_exchange.AlternateExchangeTests.test_immediate +tests_0-10.query.QueryTests.test_exchange_bound_header +tests_0-10.persistence.PersistenceTests.test_ack_message_from_deleted_queue +tests_0-10.persistence.PersistenceTests.test_delete_queue_after_publish +tests_0-10.persistence.PersistenceTests.test_queue_deletion +tests_0-10.tx.TxTests.test_auto_rollback +tests_0-10.tx.TxTests.test_commit +tests_0-10.tx.TxTests.test_rollback +tests_0-10.execution.ExecutionTests.test_flush +tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_exchange +tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_queue +tests_0-10.alternate_exchange.AlternateExchangeTests.test_queue_delete +tests_0-10.alternate_exchange.AlternateExchangeTests.test_unroutable +tests_0-10.exchange.DeclareMethodPassiveFieldNotFoundRuleTests.test +tests_0-10.exchange.DefaultExchangeRuleTests.testDefaultExchange +tests_0-10.exchange.ExchangeTests.testHeadersBindNoMatchArg +tests_0-10.exchange.HeadersExchangeTests.testMatchAll +tests_0-10.exchange.HeadersExchangeTests.testMatchAny +tests_0-10.exchange.MiscellaneousErrorsTests.testDifferentDeclaredType +tests_0-10.exchange.MiscellaneousErrorsTests.testTypeNotKnown +tests_0-10.exchange.RecommendedTypesRuleTests.testDirect +tests_0-10.exchange.RecommendedTypesRuleTests.testFanout +tests_0-10.exchange.RecommendedTypesRuleTests.testHeaders +tests_0-10.exchange.RecommendedTypesRuleTests.testTopic +tests_0-10.exchange.RequiredInstancesRuleTests.testAmqDirect +tests_0-10.exchange.RequiredInstancesRuleTests.testAmqFanOut +tests_0-10.exchange.RequiredInstancesRuleTests.testAmqMatch +tests_0-10.exchange.RequiredInstancesRuleTests.testAmqTopic +tests_0-10.dtx.DtxTests.test_bad_resume +tests_0-10.dtx.DtxTests.test_end +tests_0-10.dtx.DtxTests.test_end_suspend_and_fail +tests_0-10.dtx.DtxTests.test_end_unknown_xid +tests_0-10.dtx.DtxTests.test_forget_xid_on_completion +tests_0-10.dtx.DtxTests.test_get_timeout +tests_0-10.dtx.DtxTests.test_implicit_end +tests_0-10.dtx.DtxTests.test_invalid_commit_one_phase_false +tests_0-10.dtx.DtxTests.test_invalid_commit_one_phase_true +tests_0-10.dtx.DtxTests.test_recover +tests_0-10.dtx.DtxTests.test_select_required +tests_0-10.dtx.DtxTests.test_set_timeout +tests_0-10.dtx.DtxTests.test_simple_commit +tests_0-10.dtx.DtxTests.test_simple_prepare_commit +tests_0-10.dtx.DtxTests.test_simple_prepare_rollback +tests_0-10.dtx.DtxTests.test_simple_rollback +tests_0-10.dtx.DtxTests.test_start_already_known +tests_0-10.dtx.DtxTests.test_start_join +tests_0-10.dtx.DtxTests.test_start_join_and_resume +tests_0-10.dtx.DtxTests.test_suspend_resume +tests_0-10.dtx.DtxTests.test_suspend_start_end_resume +tests_0-10.message.MessageTests.test_ack +tests_0-10.message.MessageTests.test_acquire +tests_0-10.message.MessageTests.test_cancel +tests_0-10.message.MessageTests.test_consume_exclusive +tests_0-10.message.MessageTests.test_consume_no_local +tests_0-10.message.MessageTests.test_consume_no_local_awkward +tests_0-10.message.MessageTests.test_consume_queue_errors +tests_0-10.message.MessageTests.test_consume_unique_consumers +tests_0-10.message.MessageTests.test_credit_flow_bytes +tests_0-10.message.MessageTests.test_credit_flow_messages +tests_0-10.message.MessageTests.test_no_size +tests_0-10.message.MessageTests.test_qos_prefetch_count +tests_0-10.message.MessageTests.test_qos_prefetch_size +tests_0-10.message.MessageTests.test_ranged_ack +tests_0-10.message.MessageTests.test_recover +tests_0-10.message.MessageTests.test_recover_requeue +tests_0-10.message.MessageTests.test_reject +tests_0-10.message.MessageTests.test_release +tests_0-10.message.MessageTests.test_release_ordering +tests_0-10.message.MessageTests.test_release_unacquired +tests_0-10.message.MessageTests.test_subscribe_not_acquired +tests_0-10.message.MessageTests.test_subscribe_not_acquired_2 +tests_0-10.message.MessageTests.test_subscribe_not_acquired_3 +tests_0-10.message.MessageTests.test_window_flow_bytes +tests_0-10.message.MessageTests.test_window_flow_messages +tests_0-10.testlib.TestBaseTest.testAssertEmptyFail +tests_0-10.testlib.TestBaseTest.testAssertEmptyPass +tests_0-10.testlib.TestBaseTest.testMessageProperties +tests_0-10.queue.QueueTests.test_autodelete_shared +tests_0-10.queue.QueueTests.test_bind +tests_0-10.queue.QueueTests.test_declare_exclusive +tests_0-10.queue.QueueTests.test_declare_passive +tests_0-10.queue.QueueTests.test_delete_ifempty +tests_0-10.queue.QueueTests.test_delete_ifunused +tests_0-10.queue.QueueTests.test_delete_simple +tests_0-10.queue.QueueTests.test_purge +tests_0-10.queue.QueueTests.test_unbind_direct +tests_0-10.queue.QueueTests.test_unbind_fanout +tests_0-10.queue.QueueTests.test_unbind_headers +tests_0-10.queue.QueueTests.test_unbind_topic tests_0-10.broker.BrokerTests.test_closed_channel - +tests_0-10.broker.BrokerTests.test_ack_and_no_ack +tests_0-10.broker.BrokerTests.test_invalid_channel +tests_0-10.broker.BrokerTests.test_simple_delivery_queued +tests_0-10.broker.BrokerTests.test_simple_delivery_immediate +tests_0-10.example.ExampleTest.test_example diff --git a/qpid/python/cpp_failing_0-10_preview.txt b/qpid/python/cpp_failing_0-10_preview.txt new file mode 100644 index 0000000000..91c2a7fce4 --- /dev/null +++ b/qpid/python/cpp_failing_0-10_preview.txt @@ -0,0 +1,6 @@ +tests.codec.FieldTableTestCase.test_field_table_decode +tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair +tests.codec.FieldTableTestCase.test_field_table_name_value_pair +tests_0-10_preview.alternate_exchange.AlternateExchangeTests.test_immediate +tests_0-10_preview.broker.BrokerTests.test_closed_channel + diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 5894981fc6..2dcba4e917 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -111,6 +111,11 @@ class Codec(Packer): def write_str8(self, s): self.write_vbin8(s.encode("utf8")) + def read_str16(self): + return self.read_vbin16().decode("utf8") + def write_str16(self, s): + self.write_vbin16(s.encode("utf8")) + def read_vbin16(self): return self.read(self.read_uint16()) @@ -125,9 +130,10 @@ class Codec(Packer): self.write(b) def write_map(self, m): - pass + self.write_uint32(0) #hack def read_map(self): - pass + size = self.read_uint32() #hack + self.read(size) #hack def write_array(self, a): pass diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 5174fe10f4..e8e54b3a56 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -29,6 +29,11 @@ from getopt import getopt, GetoptError from qpid.content import Content from qpid.message import Message +#0-10 support +from qpid.connection010 import Connection +from qpid.spec010 import load +from qpid.util import connect + def findmodules(root): """Find potential python modules under directory root""" found = [] @@ -125,23 +130,29 @@ Options: if opt in ("-S", "--skip-self-test"): self.skip_self_test = True if opt in ("-F", "--spec-folder"): TestRunner.SPEC_FOLDER = value # Abbreviations for default settings. - if (self.specfile == "0-8"): - self.specfile = self.get_spec_file("amqp.0-8.xml") - elif (self.specfile == "0-9"): - self.specfile = self.get_spec_file("amqp.0-9.xml") - self.errata.append(self.get_spec_file("amqp-errata.0-9.xml")) - - if (self.specfile == None): - self._die("No XML specification provided") - print "Using specification from:", self.specfile - self.spec = qpid.spec.load(self.specfile, *self.errata) + if (self.specfile == "0-10"): + self.spec = load(self.get_spec_file("amqp.0-10.xml")) + else: + if (self.specfile == "0-8"): + self.specfile = self.get_spec_file("amqp.0-8.xml") + elif (self.specfile == "0-9"): + self.specfile = self.get_spec_file("amqp.0-9.xml") + self.errata.append(self.get_spec_file("amqp-errata.0-9.xml")) + + if (self.specfile == None): + self._die("No XML specification provided") + print "Using specification from:", self.specfile + + self.spec = qpid.spec.load(self.specfile, *self.errata) if len(self.tests) == 0: if not self.skip_self_test: self.tests=findmodules("tests") if self.use08spec(): self.tests+=findmodules("tests_0-8") - elif (self.spec.major == 0 and self.spec.minor == 10) or (self.spec.major == 99 and self.spec.minor == 0): + elif (self.spec.major == 99 and self.spec.minor == 0): + self.tests+=findmodules("tests_0-10_preview") + elif (self.spec.major == 0 and self.spec.minor == 10): self.tests+=findmodules("tests_0-10") else: self.tests+=findmodules("tests_0-9") @@ -330,3 +341,17 @@ class TestBase(unittest.TestCase): self.assertEqual("close", message.method.name) self.assertEqual(expectedCode, message.reply_code) +class TestBase010(unittest.TestCase): + """ + Base class for Qpid test cases. using the final 0-10 spec + """ + + def setUp(self): + spec = testrunner.spec + self.conn = Connection(connect(testrunner.host, testrunner.port), spec) + self.conn.start(timeout=10) + self.session = self.conn.session("test-session", timeout=10) + + def tearDown(self): + self.session.close(timeout=10) + self.conn.close(timeout=10) diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index 83f8d85811..225b3cfb69 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -93,34 +93,6 @@ class AlternateExchangeTests(TestBase): self.assertEqual("Three", dlq.get(timeout=1).content.body) self.assertEmpty(dlq) - - def test_immediate(self): - """ - Test that messages in a queue being deleted are delivered to the alternate-exchange if specified - """ - channel = self.channel - #set up a 'dead letter queue': - channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="immediate", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="dlq", queue="immediate") - self.subscribe(destination="dlq", queue="immediate") - dlq = self.client.queue("dlq") - - #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True, auto_delete=True) - #send it some messages: - #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK - channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) - - #check the messages were delivered to the dlq: - self.assertEqual("no one wants me", dlq.get(timeout=1).content.body) - self.assertEmpty(dlq) - - #cleanup: - channel.queue_delete(queue="no-consumers") - channel.exchange_delete(exchange="dlq") - - def test_delete_while_used_by_queue(self): """ Ensure an exchange still in use as an alternate-exchange for a diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 99936ba742..bfecb5c166 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -19,9 +19,10 @@ from qpid.client import Closed from qpid.queue import Empty from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.testlib import TestBase010 +from qpid.datatypes import Message -class BrokerTests(TestBase): +class BrokerTests(TestBase010): """Tests for basic Broker functionality""" def test_ack_and_no_ack(self): @@ -57,37 +58,36 @@ class BrokerTests(TestBase): """ Test simple message delivery where consume is issued before publish """ - channel = self.channel - self.exchange_declare(channel, exchange="test-exchange", type="direct") - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + session = self.session + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + session.exchange_bind(queue="test-queue", exchange="amq.fanout") consumer_tag = "tag1" - self.subscribe(queue="test-queue", destination=consumer_tag) - queue = self.client.queue(consumer_tag) + session.message_subscribe(queue="test-queue", destination=consumer_tag) + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) + queue = session.incoming(consumer_tag) body = "Immediate Delivery" - channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) + session.message_transfer("amq.fanout", None, None, Message(body)) msg = queue.get(timeout=5) self.assert_(msg.content.body == body) - # TODO: Ensure we fail if immediate=True and there's no consumer. - - def test_simple_delivery_queued(self): """ Test basic message delivery where publish is issued before consume (i.e. requires queueing of the message) """ - channel = self.channel - self.exchange_declare(channel, exchange="test-exchange", type="direct") - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + session = self.session + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + session.exchange_bind(queue="test-queue", exchange="amq.fanout") body = "Queued Delivery" - channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) + session.message_transfer("amq.fanout", None, None, Message(body)) consumer_tag = "tag1" - self.subscribe(queue="test-queue", destination=consumer_tag) - queue = self.client.queue(consumer_tag) + session.message_subscribe(queue="test-queue", destination=consumer_tag) + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) + queue = session.incoming(consumer_tag) msg = queue.get(timeout=5) self.assert_(msg.content.body == body) diff --git a/qpid/python/tests_0-10/query.py b/qpid/python/tests_0-10/query.py index eba2ee6dd1..0bbfb079cc 100644 --- a/qpid/python/tests_0-10/query.py +++ b/qpid/python/tests_0-10/query.py @@ -19,209 +19,212 @@ from qpid.client import Client, Closed from qpid.queue import Empty from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.testlib import TestBase010 -class QueryTests(TestBase): - """Tests for various query methods introduced in 0-10 and available in 0-9 for preview""" +class QueryTests(TestBase010): + """Tests for various query methods""" + + def test_queue_query(self): + session = self.session + session.queue_declare(queue="my-queue", exclusive=True) + result = session.queue_query(queue="my-queue") + self.assertEqual("my-queue", result.queue) def test_exchange_query(self): """ Test that the exchange_query method works as expected """ - channel = self.channel + session = self.session #check returned type for the standard exchanges - self.assert_type("direct", channel.exchange_query(name="amq.direct")) - self.assert_type("topic", channel.exchange_query(name="amq.topic")) - self.assert_type("fanout", channel.exchange_query(name="amq.fanout")) - self.assert_type("headers", channel.exchange_query(name="amq.match")) - self.assert_type("direct", channel.exchange_query(name="")) + self.assertEqual("direct", session.exchange_query(name="amq.direct").type) + self.assertEqual("topic", session.exchange_query(name="amq.topic").type) + self.assertEqual("fanout", session.exchange_query(name="amq.fanout").type) + self.assertEqual("headers", session.exchange_query(name="amq.match").type) + self.assertEqual("direct", session.exchange_query(name="").type) #declare an exchange - channel.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) + session.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) #check that the result of a query is as expected - response = channel.exchange_query(name="my-test-exchange") - self.assert_type("direct", response) - self.assertEqual(False, response.durable) - self.assertEqual(False, response.not_found) + response = session.exchange_query(name="my-test-exchange") + self.assertEqual("direct", response.type) + self.assert_(not response.durable) + self.assert_(not response.not_found) #delete the exchange - channel.exchange_delete(exchange="my-test-exchange") + session.exchange_delete(exchange="my-test-exchange") #check that the query now reports not-found - self.assertEqual(True, channel.exchange_query(name="my-test-exchange").not_found) - - def assert_type(self, expected_type, response): - self.assertEqual(expected_type, response.__getattr__("type")) + self.assert_(session.exchange_query(name="my-test-exchange").not_found) - def test_binding_query_direct(self): + def test_exchange_bound_direct(self): """ - Test that the binding_query method works as expected with the direct exchange + Test that the exchange_bound method works as expected with the direct exchange """ - self.binding_query_with_key("amq.direct") + self.exchange_bound_with_key("amq.direct") - def test_binding_query_topic(self): + def test_exchange_bound_topic(self): """ - Test that the binding_query method works as expected with the direct exchange + Test that the exchange_bound method works as expected with the direct exchange """ - self.binding_query_with_key("amq.topic") + self.exchange_bound_with_key("amq.topic") - def binding_query_with_key(self, exchange_name): - channel = self.channel + def exchange_bound_with_key(self, exchange_name): + session = self.session #setup: create two queues - channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) - channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) - channel.queue_bind(exchange=exchange_name, queue="used-queue", routing_key="used-key") + session.exchange_bind(exchange=exchange_name, queue="used-queue", binding_key="used-key") # test detection of any binding to specific queue - response = channel.binding_query(exchange=exchange_name, queue="used-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) + response = session.exchange_bound(exchange=exchange_name, queue="used-queue") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) # test detection of specific binding to any queue - response = channel.binding_query(exchange=exchange_name, routing_key="used-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.key_not_matched) + response = session.exchange_bound(exchange=exchange_name, binding_key="used-key") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.key_not_matched) # test detection of specific binding to specific queue - response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="used-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - self.assertEqual(False, response.key_not_matched) + response = session.exchange_bound(exchange=exchange_name, queue="used-queue", binding_key="used-key") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) + self.assert_(not response.key_not_matched) # test unmatched queue, unspecified binding - response = channel.binding_query(exchange=exchange_name, queue="unused-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange=exchange_name, queue="unused-queue") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) # test unspecified queue, unmatched binding - response = channel.binding_query(exchange=exchange_name, routing_key="unused-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange=exchange_name, binding_key="unused-key") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.key_not_matched) # test matched queue, unmatched binding - response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="unused-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) + response = session.exchange_bound(exchange=exchange_name, queue="used-queue", binding_key="unused-key") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) self.assertEqual(True, response.key_not_matched) # test unmatched queue, matched binding - response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="used-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange=exchange_name, queue="unused-queue", binding_key="used-key") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) - self.assertEqual(False, response.key_not_matched) + self.assert_(not response.key_not_matched) # test unmatched queue, unmatched binding - response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="unused-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange=exchange_name, queue="unused-queue", binding_key="unused-key") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) self.assertEqual(True, response.key_not_matched) #test exchange not found - self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + self.assertEqual(True, session.exchange_bound(exchange="unknown-exchange").exchange_not_found) #test queue not found - self.assertEqual(True, channel.binding_query(exchange=exchange_name, queue="unknown-queue").queue_not_found) + self.assertEqual(True, session.exchange_bound(exchange=exchange_name, queue="unknown-queue").queue_not_found) - def test_binding_query_fanout(self): + def test_exchange_bound_fanout(self): """ - Test that the binding_query method works as expected with fanout exchange + Test that the exchange_bound method works as expected with fanout exchange """ - channel = self.channel + session = self.session #setup - channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) - channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.fanout", queue="used-queue") + session.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) + session.exchange_bind(exchange="amq.fanout", queue="used-queue") # test detection of any binding to specific queue - response = channel.binding_query(exchange="amq.fanout", queue="used-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) + response = session.exchange_bound(exchange="amq.fanout", queue="used-queue") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) # test unmatched queue, unspecified binding - response = channel.binding_query(exchange="amq.fanout", queue="unused-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange="amq.fanout", queue="unused-queue") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) #test exchange not found - self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + self.assertEqual(True, session.exchange_bound(exchange="unknown-exchange").exchange_not_found) #test queue not found - self.assertEqual(True, channel.binding_query(exchange="amq.fanout", queue="unknown-queue").queue_not_found) + self.assertEqual(True, session.exchange_bound(exchange="amq.fanout", queue="unknown-queue").queue_not_found) - def test_binding_query_header(self): + def test_exchange_bound_header(self): """ - Test that the binding_query method works as expected with headers exchanges + Test that the exchange_bound method works as expected with headers exchanges """ - channel = self.channel + session = self.session #setup - channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) - channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) + session.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) + session.exchange_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) # test detection of any binding to specific queue - response = channel.binding_query(exchange="amq.match", queue="used-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) + response = session.exchange_bound(exchange="amq.match", queue="used-queue") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) # test detection of specific binding to any queue - response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "a":"A"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.args_not_matched) + response = session.exchange_bound(exchange="amq.match", arguments={"x-match":"all", "a":"A"}) + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.args_not_matched) # test detection of specific binding to specific queue - response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - self.assertEqual(False, response.args_not_matched) + response = session.exchange_bound(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"}) + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) + self.assert_(not response.args_not_matched) # test unmatched queue, unspecified binding - response = channel.binding_query(exchange="amq.match", queue="unused-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange="amq.match", queue="unused-queue") + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) # test unspecified queue, unmatched binding - response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "b":"B"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange="amq.match", arguments={"x-match":"all", "b":"B"}) + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.args_not_matched) # test matched queue, unmatched binding - response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "b":"B"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) + response = session.exchange_bound(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "b":"B"}) + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) + self.assert_(not response.queue_not_matched) self.assertEqual(True, response.args_not_matched) # test unmatched queue, matched binding - response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "a":"A"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "a":"A"}) + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) - self.assertEqual(False, response.args_not_matched) + self.assert_(not response.args_not_matched) # test unmatched queue, unmatched binding - response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "b":"B"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) + response = session.exchange_bound(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "b":"B"}) + self.assert_(not response.exchange_not_found) + self.assert_(not response.queue_not_found) self.assertEqual(True, response.queue_not_matched) self.assertEqual(True, response.args_not_matched) #test exchange not found - self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + self.assertEqual(True, session.exchange_bound(exchange="unknown-exchange").exchange_not_found) #test queue not found - self.assertEqual(True, channel.binding_query(exchange="amq.match", queue="unknown-queue").queue_not_found) + self.assertEqual(True, session.exchange_bound(exchange="amq.match", queue="unknown-queue").queue_not_found) diff --git a/qpid/python/tests_0-10_preview/__init__.py b/qpid/python/tests_0-10_preview/__init__.py new file mode 100644 index 0000000000..fe96d9e122 --- /dev/null +++ b/qpid/python/tests_0-10_preview/__init__.py @@ -0,0 +1,32 @@ +# Do not delete - marks this directory as a python package. + +# +# 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. +# + +from alternate_exchange import * +from broker import * +from dtx import * +from example import * +from exchange import * +from execution import * +from message import * +from query import * +from queue import * +from testlib import * +from tx import * diff --git a/qpid/python/tests_0-10_preview/alternate_exchange.py b/qpid/python/tests_0-10_preview/alternate_exchange.py new file mode 100644 index 0000000000..83f8d85811 --- /dev/null +++ b/qpid/python/tests_0-10_preview/alternate_exchange.py @@ -0,0 +1,179 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class AlternateExchangeTests(TestBase): + """ + Tests for the new mechanism for message returns introduced in 0-10 + and available in 0-9 for preview + """ + + def test_unroutable(self): + """ + Test that unroutable messages are delivered to the alternate-exchange if specified + """ + channel = self.channel + #create an exchange with an alternate defined + channel.exchange_declare(exchange="secondary", type="fanout") + channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") + + #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages + channel.queue_declare(queue="returns", exclusive=True, auto_delete=True) + channel.queue_bind(queue="returns", exchange="secondary") + self.subscribe(destination="a", queue="returns") + returned = self.client.queue("a") + + #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages + channel.queue_declare(queue="processed", exclusive=True, auto_delete=True) + channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") + self.subscribe(destination="b", queue="processed") + processed = self.client.queue("b") + + #publish to the primary exchange + #...one message that makes it to the 'processed' queue: + channel.message_transfer(destination="primary", content=Content("Good", properties={'routing_key':"my-key"})) + #...and one that does not: + channel.message_transfer(destination="primary", content=Content("Bad", properties={'routing_key':"unused-key"})) + + #delete the exchanges + channel.exchange_delete(exchange="primary") + channel.exchange_delete(exchange="secondary") + + #verify behaviour + self.assertEqual("Good", processed.get(timeout=1).content.body) + self.assertEqual("Bad", returned.get(timeout=1).content.body) + self.assertEmpty(processed) + self.assertEmpty(returned) + + def test_queue_delete(self): + """ + Test that messages in a queue being deleted are delivered to the alternate-exchange if specified + """ + channel = self.channel + #set up a 'dead letter queue': + channel.exchange_declare(exchange="dlq", type="fanout") + channel.queue_declare(queue="deleted", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="dlq", queue="deleted") + self.subscribe(destination="dlq", queue="deleted") + dlq = self.client.queue("dlq") + + #create a queue using the dlq as its alternate exchange: + channel.queue_declare(queue="delete-me", alternate_exchange="dlq") + #send it some messages: + channel.message_transfer(content=Content("One", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("Two", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("Three", properties={'routing_key':"delete-me"})) + #delete it: + channel.queue_delete(queue="delete-me") + #delete the dlq exchange: + channel.exchange_delete(exchange="dlq") + + #check the messages were delivered to the dlq: + self.assertEqual("One", dlq.get(timeout=1).content.body) + self.assertEqual("Two", dlq.get(timeout=1).content.body) + self.assertEqual("Three", dlq.get(timeout=1).content.body) + self.assertEmpty(dlq) + + + def test_immediate(self): + """ + Test that messages in a queue being deleted are delivered to the alternate-exchange if specified + """ + channel = self.channel + #set up a 'dead letter queue': + channel.exchange_declare(exchange="dlq", type="fanout") + channel.queue_declare(queue="immediate", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="dlq", queue="immediate") + self.subscribe(destination="dlq", queue="immediate") + dlq = self.client.queue("dlq") + + #create a queue using the dlq as its alternate exchange: + channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True, auto_delete=True) + #send it some messages: + #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK + channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) + + #check the messages were delivered to the dlq: + self.assertEqual("no one wants me", dlq.get(timeout=1).content.body) + self.assertEmpty(dlq) + + #cleanup: + channel.queue_delete(queue="no-consumers") + channel.exchange_delete(exchange="dlq") + + + def test_delete_while_used_by_queue(self): + """ + Ensure an exchange still in use as an alternate-exchange for a + queue can't be deleted + """ + channel = self.channel + channel.exchange_declare(exchange="alternate", type="fanout") + channel.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") + try: + channel.exchange_delete(exchange="alternate") + self.fail("Expected deletion of in-use alternate-exchange to fail") + except Closed, e: + #cleanup: + other = self.connect() + channel = other.channel(1) + channel.session_open() + channel.exchange_delete(exchange="alternate") + channel.session_close() + other.close() + + self.assertConnectionException(530, e.args[0]) + + + + def test_delete_while_used_by_exchange(self): + """ + Ensure an exchange still in use as an alternate-exchange for + another exchange can't be deleted + """ + channel = self.channel + channel.exchange_declare(exchange="alternate", type="fanout") + channel.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") + try: + channel.exchange_delete(exchange="alternate") + #cleanup: + channel.exchange_delete(exchange="e") + self.fail("Expected deletion of in-use alternate-exchange to fail") + except Closed, e: + #cleanup: + other = self.connect() + channel = other.channel(1) + channel.session_open() + channel.exchange_delete(exchange="e") + channel.exchange_delete(exchange="alternate") + channel.session_close() + other.close() + + self.assertConnectionException(530, e.args[0]) + + + def assertEmpty(self, queue): + try: + msg = queue.get(timeout=1) + self.fail("Queue not empty: " + msg) + except Empty: None + diff --git a/qpid/python/tests_0-10_preview/broker.py b/qpid/python/tests_0-10_preview/broker.py new file mode 100644 index 0000000000..99936ba742 --- /dev/null +++ b/qpid/python/tests_0-10_preview/broker.py @@ -0,0 +1,111 @@ +# +# 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. +# +from qpid.client import Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class BrokerTests(TestBase): + """Tests for basic Broker functionality""" + + def test_ack_and_no_ack(self): + """ + First, this test tries to receive a message with a no-ack + consumer. Second, this test tries to explicitly receive and + acknowledge a message with an acknowledging consumer. + """ + ch = self.channel + self.queue_declare(ch, queue = "myqueue") + + # No ack consumer + ctag = "tag1" + self.subscribe(ch, queue = "myqueue", destination = ctag) + body = "test no-ack" + ch.message_transfer(content = Content(body, properties = {"routing_key" : "myqueue"})) + msg = self.client.queue(ctag).get(timeout = 5) + self.assert_(msg.content.body == body) + + # Acknowledging consumer + self.queue_declare(ch, queue = "otherqueue") + ctag = "tag2" + self.subscribe(ch, queue = "otherqueue", destination = ctag, confirm_mode = 1) + ch.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) + ch.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) + body = "test ack" + ch.message_transfer(content = Content(body, properties = {"routing_key" : "otherqueue"})) + msg = self.client.queue(ctag).get(timeout = 5) + msg.complete() + self.assert_(msg.content.body == body) + + def test_simple_delivery_immediate(self): + """ + Test simple message delivery where consume is issued before publish + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + consumer_tag = "tag1" + self.subscribe(queue="test-queue", destination=consumer_tag) + queue = self.client.queue(consumer_tag) + + body = "Immediate Delivery" + channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) + msg = queue.get(timeout=5) + self.assert_(msg.content.body == body) + + # TODO: Ensure we fail if immediate=True and there's no consumer. + + + def test_simple_delivery_queued(self): + """ + Test basic message delivery where publish is issued before consume + (i.e. requires queueing of the message) + """ + channel = self.channel + self.exchange_declare(channel, exchange="test-exchange", type="direct") + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + body = "Queued Delivery" + channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) + + consumer_tag = "tag1" + self.subscribe(queue="test-queue", destination=consumer_tag) + queue = self.client.queue(consumer_tag) + msg = queue.get(timeout=5) + self.assert_(msg.content.body == body) + + def test_invalid_channel(self): + channel = self.client.channel(200) + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for invalid channel") + except Closed, e: + self.assertConnectionException(504, e.args[0]) + + def test_closed_channel(self): + channel = self.client.channel(200) + channel.session_open() + channel.session_close() + try: + channel.queue_declare(exclusive=True) + self.fail("Expected error on queue_declare for closed channel") + except Closed, e: + if isinstance(e.args[0], str): self.fail(e) + self.assertConnectionException(504, e.args[0]) diff --git a/qpid/python/tests_0-10_preview/dtx.py b/qpid/python/tests_0-10_preview/dtx.py new file mode 100644 index 0000000000..f84f91c75a --- /dev/null +++ b/qpid/python/tests_0-10_preview/dtx.py @@ -0,0 +1,645 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from struct import pack, unpack +from time import sleep + +class DtxTests(TestBase): + """ + Tests for the amqp dtx related classes. + + Tests of the form test_simple_xxx test the basic transactional + behaviour. The approach here is to 'swap' a message from one queue + to another by consuming and re-publishing in the same + transaction. That transaction is then completed in different ways + and the appropriate result verified. + + The other tests enforce more specific rules and behaviour on a + per-method or per-field basis. + """ + + XA_RBROLLBACK = 1 + XA_RBTIMEOUT = 2 + XA_OK = 0 + tx_counter = 0 + + def reset_channel(self): + self.channel.session_close() + self.channel = self.client.channel(self.channel.id + 1) + self.channel.session_open() + + def test_simple_commit(self): + """ + Test basic one-phase commit behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "commit") + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #commit + self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).status) + + #should close and reopen channel to ensure no unacked messages are held + self.reset_channel() + + #check result + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(1, "queue-b") + self.assertMessageId("commit", "queue-b") + + def test_simple_prepare_commit(self): + """ + Test basic two-phase commit behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "prepare-commit") + + #prepare + self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #commit + self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).status) + + self.reset_channel() + + #check result + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(1, "queue-b") + self.assertMessageId("prepare-commit", "queue-b") + + + def test_simple_rollback(self): + """ + Test basic rollback behaviour. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "rollback") + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #rollback + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + + self.reset_channel() + + #check result + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("rollback", "queue-a") + + def test_simple_prepare_rollback(self): + """ + Test basic rollback behaviour after the transaction has been prepared. + """ + channel = self.channel + tx = self.xid("my-xid") + self.txswap(tx, "prepare-rollback") + + #prepare + self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) + + #neither queue should have any messages accessible + self.assertMessageCount(0, "queue-a") + self.assertMessageCount(0, "queue-b") + + #rollback + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + + self.reset_channel() + + #check result + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("prepare-rollback", "queue-a") + + def test_select_required(self): + """ + check that an error is flagged if select is not issued before + start or end + """ + channel = self.channel + tx = self.xid("dummy") + try: + channel.dtx_demarcation_start(xid=tx) + + #if we get here we have failed, but need to do some cleanup: + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + self.fail("Channel not selected for use with dtx, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_start_already_known(self): + """ + Verify that an attempt to start an association with a + transaction that is already known is not allowed (unless the + join flag is set). + """ + #create two channels on different connection & select them for use with dtx: + channel1 = self.channel + channel1.dtx_demarcation_select() + + other = self.connect() + channel2 = other.channel(1) + channel2.session_open() + channel2.dtx_demarcation_select() + + #create a xid + tx = self.xid("dummy") + #start work on one channel under that xid: + channel1.dtx_demarcation_start(xid=tx) + #then start on the other without the join set + failed = False + try: + channel2.dtx_demarcation_start(xid=tx) + except Closed, e: + failed = True + error = e + + #cleanup: + if not failed: + channel2.dtx_demarcation_end(xid=tx) + other.close() + channel1.dtx_demarcation_end(xid=tx) + channel1.dtx_coordination_rollback(xid=tx) + + #verification: + if failed: self.assertConnectionException(503, e.args[0]) + else: self.fail("Xid already known, expected exception!") + + def test_forget_xid_on_completion(self): + """ + Verify that a xid is 'forgotten' - and can therefore be used + again - once it is completed. + """ + #do some transactional work & complete the transaction + self.test_simple_commit() + # channel has been reset, so reselect for use with dtx + self.channel.dtx_demarcation_select() + + #start association for the same xid as the previously completed txn + tx = self.xid("my-xid") + self.channel.dtx_demarcation_start(xid=tx) + self.channel.dtx_demarcation_end(xid=tx) + self.channel.dtx_coordination_rollback(xid=tx) + + def test_start_join_and_resume(self): + """ + Ensure the correct error is signalled when both the join and + resume flags are set on starting an association between a + channel and a transcation. + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("dummy") + try: + channel.dtx_demarcation_start(xid=tx, join=True, resume=True) + #failed, but need some cleanup: + channel.dtx_demarcation_end(xid=tx) + channel.dtx_coordination_rollback(xid=tx) + self.fail("Join and resume both set, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def test_start_join(self): + """ + Verify 'join' behaviour, where a channel is associated with a + transaction that is already associated with another channel. + """ + #create two channels & select them for use with dtx: + channel1 = self.channel + channel1.dtx_demarcation_select() + + channel2 = self.client.channel(2) + channel2.session_open() + channel2.dtx_demarcation_select() + + #setup + channel1.queue_declare(queue="one", exclusive=True, auto_delete=True) + channel1.queue_declare(queue="two", exclusive=True, auto_delete=True) + channel1.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) + channel1.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + + #create a xid + tx = self.xid("dummy") + #start work on one channel under that xid: + channel1.dtx_demarcation_start(xid=tx) + #then start on the other with the join flag set + channel2.dtx_demarcation_start(xid=tx, join=True) + + #do work through each channel + self.swap(channel1, "one", "two")#swap 'a' from 'one' to 'two' + self.swap(channel2, "two", "one")#swap 'b' from 'two' to 'one' + + #mark end on both channels + channel1.dtx_demarcation_end(xid=tx) + channel2.dtx_demarcation_end(xid=tx) + + #commit and check + channel1.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + + def test_suspend_resume(self): + """ + Test suspension and resumption of an association + """ + channel = self.channel + channel.dtx_demarcation_select() + + #setup + channel.queue_declare(queue="one", exclusive=True, auto_delete=True) + channel.queue_declare(queue="two", exclusive=True, auto_delete=True) + channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) + channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + + tx = self.xid("dummy") + + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' + channel.dtx_demarcation_end(xid=tx, suspend=True) + + channel.dtx_demarcation_start(xid=tx, resume=True) + self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' + channel.dtx_demarcation_end(xid=tx) + + #commit and check + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + def test_suspend_start_end_resume(self): + """ + Test suspension and resumption of an association with work + done on another transaction when the first transaction is + suspended + """ + channel = self.channel + channel.dtx_demarcation_select() + + #setup + channel.queue_declare(queue="one", exclusive=True, auto_delete=True) + channel.queue_declare(queue="two", exclusive=True, auto_delete=True) + channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) + channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + + tx = self.xid("dummy") + + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' + channel.dtx_demarcation_end(xid=tx, suspend=True) + + channel.dtx_demarcation_start(xid=tx, resume=True) + self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' + channel.dtx_demarcation_end(xid=tx) + + #commit and check + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "one") + self.assertMessageCount(1, "two") + self.assertMessageId("a", "two") + self.assertMessageId("b", "one") + + def test_end_suspend_and_fail(self): + """ + Verify that the correct error is signalled if the suspend and + fail flag are both set when disassociating a transaction from + the channel + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("suspend_and_fail") + channel.dtx_demarcation_start(xid=tx) + try: + channel.dtx_demarcation_end(xid=tx, suspend=True, fail=True) + self.fail("Suspend and fail both set, expected exception!") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.session_open() + channel.dtx_coordination_rollback(xid=tx) + channel.session_close() + other.close() + + + def test_end_unknown_xid(self): + """ + Verifies that the correct exception is thrown when an attempt + is made to end the association for a xid not previously + associated with the channel + """ + channel = self.channel + channel.dtx_demarcation_select() + tx = self.xid("unknown-xid") + try: + channel.dtx_demarcation_end(xid=tx) + self.fail("Attempted to end association with unknown xid, expected exception!") + except Closed, e: + #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming... + self.assertConnectionException(503, e.args[0]) + + def test_end(self): + """ + Verify that the association is terminated by end and subsequent + operations are non-transactional + """ + channel = self.client.channel(2) + channel.session_open() + channel.queue_declare(queue="tx-queue", exclusive=True, auto_delete=True) + + #publish a message under a transaction + channel.dtx_demarcation_select() + tx = self.xid("dummy") + channel.dtx_demarcation_start(xid=tx) + channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"one"}, body="DtxMessage")) + channel.dtx_demarcation_end(xid=tx) + + #now that association with txn is ended, publish another message + channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"two"}, body="DtxMessage")) + + #check the second message is available, but not the first + self.assertMessageCount(1, "tx-queue") + self.subscribe(channel, queue="tx-queue", destination="results", confirm_mode=1) + msg = self.client.queue("results").get(timeout=1) + self.assertEqual("two", msg.content['message_id']) + channel.message_cancel(destination="results") + #ack the message then close the channel + msg.complete() + channel.session_close() + + channel = self.channel + #commit the transaction and check that the first message (and + #only the first message) is then delivered + channel.dtx_coordination_commit(xid=tx, one_phase=True) + self.assertMessageCount(1, "tx-queue") + self.assertMessageId("one", "tx-queue") + + def test_invalid_commit_one_phase_true(self): + """ + Test that a commit with one_phase = True is rejected if the + transaction in question has already been prepared. + """ + other = self.connect() + tester = other.channel(1) + tester.session_open() + tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + tester.dtx_demarcation_select() + tx = self.xid("dummy") + tester.dtx_demarcation_start(xid=tx) + tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) + tester.dtx_demarcation_end(xid=tx) + tester.dtx_coordination_prepare(xid=tx) + failed = False + try: + tester.dtx_coordination_commit(xid=tx, one_phase=True) + except Closed, e: + failed = True + error = e + + if failed: + self.channel.dtx_coordination_rollback(xid=tx) + self.assertConnectionException(503, e.args[0]) + else: + tester.session_close() + other.close() + self.fail("Invalid use of one_phase=True, expected exception!") + + def test_invalid_commit_one_phase_false(self): + """ + Test that a commit with one_phase = False is rejected if the + transaction in question has not yet been prepared. + """ + """ + Test that a commit with one_phase = True is rejected if the + transaction in question has already been prepared. + """ + other = self.connect() + tester = other.channel(1) + tester.session_open() + tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + tester.dtx_demarcation_select() + tx = self.xid("dummy") + tester.dtx_demarcation_start(xid=tx) + tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) + tester.dtx_demarcation_end(xid=tx) + failed = False + try: + tester.dtx_coordination_commit(xid=tx, one_phase=False) + except Closed, e: + failed = True + error = e + + if failed: + self.channel.dtx_coordination_rollback(xid=tx) + self.assertConnectionException(503, e.args[0]) + else: + tester.session_close() + other.close() + self.fail("Invalid use of one_phase=False, expected exception!") + + def test_implicit_end(self): + """ + Test that an association is implicitly ended when the channel + is closed (whether by exception or explicit client request) + and the transaction in question is marked as rollback only. + """ + channel1 = self.channel + channel2 = self.client.channel(2) + channel2.session_open() + + #setup: + channel2.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) + tx = self.xid("dummy") + + channel2.dtx_demarcation_select() + channel2.dtx_demarcation_start(xid=tx) + channel2.message_subscribe(queue="dummy", destination="dummy", confirm_mode=1) + channel2.message_flow(destination="dummy", unit=0, value=1) + channel2.message_flow(destination="dummy", unit=1, value=0xFFFFFFFF) + self.client.queue("dummy").get(timeout=1).complete() + channel2.message_cancel(destination="dummy") + channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) + channel2.session_close() + + self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).status) + channel1.dtx_coordination_rollback(xid=tx) + + def test_get_timeout(self): + """ + Check that get-timeout returns the correct value, (and that a + transaction with a timeout can complete normally) + """ + channel = self.channel + tx = self.xid("dummy") + + channel.dtx_demarcation_select() + channel.dtx_demarcation_start(xid=tx) + self.assertEqual(0, channel.dtx_coordination_get_timeout(xid=tx).timeout) + channel.dtx_coordination_set_timeout(xid=tx, timeout=60) + self.assertEqual(60, channel.dtx_coordination_get_timeout(xid=tx).timeout) + self.assertEqual(self.XA_OK, channel.dtx_demarcation_end(xid=tx).status) + self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + + def test_set_timeout(self): + """ + Test the timeout of a transaction results in the expected + behaviour + """ + #open new channel to allow self.channel to be used in checking te queue + channel = self.client.channel(2) + channel.session_open() + #setup: + tx = self.xid("dummy") + channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) + channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) + channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':"timeout"}, body="DtxMessage")) + + channel.dtx_demarcation_select() + channel.dtx_demarcation_start(xid=tx) + self.swap(channel, "queue-a", "queue-b") + channel.dtx_coordination_set_timeout(xid=tx, timeout=2) + sleep(3) + #check that the work has been rolled back already + self.assertMessageCount(1, "queue-a") + self.assertMessageCount(0, "queue-b") + self.assertMessageId("timeout", "queue-a") + #check the correct codes are returned when we try to complete the txn + self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_demarcation_end(xid=tx).status) + self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_coordination_rollback(xid=tx).status) + + + + def test_recover(self): + """ + Test basic recover behaviour + """ + channel = self.channel + + channel.dtx_demarcation_select() + channel.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + + prepared = [] + for i in range(1, 10): + tx = self.xid("tx%s" % (i)) + channel.dtx_demarcation_start(xid=tx) + channel.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="message%s" % (i))) + channel.dtx_demarcation_end(xid=tx) + if i in [2, 5, 6, 8]: + channel.dtx_coordination_prepare(xid=tx) + prepared.append(tx) + else: + channel.dtx_coordination_rollback(xid=tx) + + xids = channel.dtx_coordination_recover().in_doubt + + #rollback the prepared transactions returned by recover + for x in xids: + channel.dtx_coordination_rollback(xid=x) + + #validate against the expected list of prepared transactions + actual = set(xids) + expected = set(prepared) + intersection = actual.intersection(expected) + + if intersection != expected: + missing = expected.difference(actual) + extra = actual.difference(expected) + for x in missing: + channel.dtx_coordination_rollback(xid=x) + self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) + + def test_bad_resume(self): + """ + Test that a resume on a session not selected for use with dtx fails + """ + channel = self.channel + try: + channel.dtx_demarcation_start(resume=True) + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def xid(self, txid): + DtxTests.tx_counter += 1 + branchqual = "v%s" % DtxTests.tx_counter + return pack('!LBB', 0, len(txid), len(branchqual)) + txid + branchqual + + def txswap(self, tx, id): + channel = self.channel + #declare two queues: + channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) + channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) + #put message with specified id on one queue: + channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':id}, body="DtxMessage")) + + #start the transaction: + channel.dtx_demarcation_select() + self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_start(xid=tx).status) + + #'swap' the message from one queue to the other, under that transaction: + self.swap(self.channel, "queue-a", "queue-b") + + #mark the end of the transactional work: + self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_end(xid=tx).status) + + def swap(self, channel, src, dest): + #consume from src: + channel.message_subscribe(destination="temp-swap", queue=src, confirm_mode=1) + channel.message_flow(destination="temp-swap", unit=0, value=1) + channel.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) + msg = self.client.queue("temp-swap").get(timeout=1) + channel.message_cancel(destination="temp-swap") + msg.complete(); + + #re-publish to dest + channel.message_transfer(content=Content(properties={'routing_key':dest, 'message_id':msg.content['message_id']}, + body=msg.content.body)) + + def assertMessageCount(self, expected, queue): + self.assertEqual(expected, self.channel.queue_query(queue=queue).message_count) + + def assertMessageId(self, expected, queue): + self.channel.message_subscribe(queue=queue, destination="results") + self.channel.message_flow(destination="results", unit=0, value=1) + self.channel.message_flow(destination="results", unit=1, value=0xFFFFFFFF) + self.assertEqual(expected, self.client.queue("results").get(timeout=1).content['message_id']) + self.channel.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10_preview/example.py b/qpid/python/tests_0-10_preview/example.py new file mode 100644 index 0000000000..da5ee2441f --- /dev/null +++ b/qpid/python/tests_0-10_preview/example.py @@ -0,0 +1,95 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExampleTest (TestBase): + """ + An example Qpid test, illustrating the unittest framework and the + python Qpid client. The test class must inherit TestBase. The + test code uses the Qpid client to interact with a qpid broker and + verify it behaves as expected. + """ + + def test_example(self): + """ + An example test. Note that test functions must start with 'test_' + to be recognized by the test framework. + """ + + # By inheriting TestBase, self.client is automatically connected + # and self.channel is automatically opened as channel(1) + # Other channel methods mimic the protocol. + channel = self.channel + + # Now we can send regular commands. If you want to see what the method + # arguments mean or what other commands are available, you can use the + # python builtin help() method. For example: + #help(chan) + #help(chan.exchange_declare) + + # If you want browse the available protocol methods without being + # connected to a live server you can use the amqp-doc utility: + # + # Usage amqp-doc [] [ ... ] + # + # Options: + # -e, --regexp use regex instead of glob when matching + + # Now that we know what commands are available we can use them to + # interact with the server. + + # Here we use ordinal arguments. + self.exchange_declare(channel, 0, "test", "direct") + + # Here we use keyword arguments. + self.queue_declare(channel, queue="test-queue") + channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") + + # Call Channel.basic_consume to register as a consumer. + # All the protocol methods return a message object. The message object + # has fields corresponding to the reply method fields, plus a content + # field that is filled if the reply includes content. In this case the + # interesting field is the consumer_tag. + channel.message_subscribe(queue="test-queue", destination="consumer_tag") + channel.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) + channel.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) + + # We can use the Client.queue(...) method to access the queue + # corresponding to our consumer_tag. + queue = self.client.queue("consumer_tag") + + # Now lets publish a message and see if our consumer gets it. To do + # this we need to import the Content class. + sent = Content("Hello World!") + sent["routing_key"] = "key" + channel.message_transfer(destination="test", content=sent) + + # Now we'll wait for the message to arrive. We can use the timeout + # argument in case the server hangs. By default queue.get() will wait + # until a message arrives or the connection to the server dies. + msg = queue.get(timeout=10) + + # And check that we got the right response with assertEqual + self.assertEqual(sent.body, msg.content.body) + + # Now acknowledge the message. + msg.complete() + diff --git a/qpid/python/tests_0-10_preview/exchange.py b/qpid/python/tests_0-10_preview/exchange.py new file mode 100644 index 0000000000..86c39b7736 --- /dev/null +++ b/qpid/python/tests_0-10_preview/exchange.py @@ -0,0 +1,335 @@ +# +# 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. +# + +""" +Tests for exchange behaviour. + +Test classes ending in 'RuleTests' are derived from rules in amqp.xml. +""" + +import Queue, logging +from qpid.testlib import TestBase +from qpid.content import Content +from qpid.client import Closed + + +class StandardExchangeVerifier: + """Verifies standard exchange behavior. + + Used as base class for classes that test standard exchanges.""" + + def verifyDirectExchange(self, ex): + """Verify that ex behaves like a direct exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") + self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") + try: + self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") + self.fail("Expected Empty exception") + except Queue.Empty: None # Expected + + def verifyFanOutExchange(self, ex): + """Verify that ex behaves like a fanout exchange.""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex) + self.queue_declare(queue="p") + self.channel.queue_bind(queue="p", exchange=ex) + for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) + + def verifyTopicExchange(self, ex): + """Verify that ex behaves like a topic exchange""" + self.queue_declare(queue="a") + self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + q = self.consume("a") + self.assertPublishGet(q, ex, "a.b.x") + self.assertPublishGet(q, ex, "a.x.b.x") + self.assertPublishGet(q, ex, "a.x.x.b.x") + # Shouldn't match + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b.x.y"})) + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"x.a.b.x"})) + self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) + self.assert_(q.empty()) + + def verifyHeadersExchange(self, ex): + """Verify that ex is a headers exchange""" + self.queue_declare(queue="q") + self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) + q = self.consume("q") + headers = {"name":"fred", "age":3} + self.assertPublishGet(q, exchange=ex, properties=headers) + self.channel.message_transfer(destination=ex) # No headers, won't deliver + self.assertEmpty(q); + + +class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server SHOULD implement these standard exchange types: topic, headers. + + Client attempts to declare an exchange with each of these standard types. + """ + + def testDirect(self): + """Declare and test a direct exchange""" + self.exchange_declare(0, exchange="d", type="direct") + self.verifyDirectExchange("d") + + def testFanout(self): + """Declare and test a fanout exchange""" + self.exchange_declare(0, exchange="f", type="fanout") + self.verifyFanOutExchange("f") + + def testTopic(self): + """Declare and test a topic exchange""" + self.exchange_declare(0, exchange="t", type="topic") + self.verifyTopicExchange("t") + + def testHeaders(self): + """Declare and test a headers exchange""" + self.exchange_declare(0, exchange="h", type="headers") + self.verifyHeadersExchange("h") + + +class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST, in each virtual host, pre-declare an exchange instance + for each standard exchange type that it implements, where the name of the + exchange instance is amq. followed by the exchange type name. + + Client creates a temporary queue and attempts to bind to each required + exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if + those types are defined). + """ + def testAmqDirect(self): self.verifyDirectExchange("amq.direct") + + def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") + + def testAmqTopic(self): self.verifyTopicExchange("amq.topic") + + def testAmqMatch(self): self.verifyHeadersExchange("amq.match") + +class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): + """ + The server MUST predeclare a direct exchange to act as the default exchange + for content Publish methods and for default queue bindings. + + Client checks that the default exchange is active by specifying a queue + binding with no exchange name, and publishing a message with a suitable + routing key but without specifying the exchange name, then ensuring that + the message arrives in the queue correctly. + """ + def testDefaultExchange(self): + # Test automatic binding by queue name. + self.queue_declare(queue="d") + self.assertPublishConsume(queue="d", routing_key="d") + # Test explicit bind to default queue + self.verifyDirectExchange("") + + +# TODO aconway 2006-09-27: Fill in empty tests: + +class DefaultAccessRuleTests(TestBase): + """ + The server MUST NOT allow clients to access the default exchange except + by specifying an empty exchange name in the Queue.Bind and content Publish + methods. + """ + +class ExtensionsRuleTests(TestBase): + """ + The server MAY implement other exchange types as wanted. + """ + + +class DeclareMethodMinimumRuleTests(TestBase): + """ + The server SHOULD support a minimum of 16 exchanges per virtual host and + ideally, impose no limit except as defined by available resources. + + The client creates as many exchanges as it can until the server reports + an error; the number of exchanges successfuly created must be at least + sixteen. + """ + + +class DeclareMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access to + the realm in which the exchange exists or will be created, or "passive" + access if the if-exists flag is set. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeclareMethodExchangeFieldReservedRuleTests(TestBase): + """ + Exchange names starting with "amq." are reserved for predeclared and + standardised exchanges. The client MUST NOT attempt to create an exchange + starting with "amq.". + + + """ + + +class DeclareMethodTypeFieldTypedRuleTests(TestBase): + """ + Exchanges cannot be redeclared with different types. The client MUST not + attempt to redeclare an existing exchange with a different type than used + in the original Exchange.Declare method. + + + """ + + +class DeclareMethodTypeFieldSupportRuleTests(TestBase): + """ + The client MUST NOT attempt to create an exchange with a type that the + server does not support. + + + """ + + +class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): + """ + If set, and the exchange does not already exist, the server MUST raise a + channel exception with reply code 404 (not found). + """ + def test(self): + try: + self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) + self.fail("Expected 404 for passive declaration of unknown exchange.") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + +class DeclareMethodDurableFieldSupportRuleTests(TestBase): + """ + The server MUST support both durable and transient exchanges. + + + """ + + +class DeclareMethodDurableFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the durable field if the exchange already exists. + + + """ + + +class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): + """ + The server MUST ignore the auto-delete field if the exchange already + exists. + + + """ + + +class DeleteMethodTicketFieldValidityRuleTests(TestBase): + """ + The client MUST provide a valid access ticket giving "active" access + rights to the exchange's access realm. + + Client creates access ticket with wrong access rights and attempts to use + in this method. + """ + + +class DeleteMethodExchangeFieldExistsRuleTests(TestBase): + """ + The client MUST NOT attempt to delete an exchange that does not exist. + """ + + +class HeadersExchangeTests(TestBase): + """ + Tests for headers exchange functionality. + """ + def setUp(self): + TestBase.setUp(self) + self.queue_declare(queue="q") + self.q = self.consume("q") + + def myAssertPublishGet(self, headers): + self.assertPublishGet(self.q, exchange="amq.match", properties=headers) + + def myBasicPublish(self, headers): + self.channel.message_transfer(destination="amq.match", content=Content("foobar", properties={'application_headers':headers})) + + def testMatchAll(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) + + # None of these should match + self.myBasicPublish({}) + self.myBasicPublish({"name":"barney"}) + self.myBasicPublish({"name":10}) + self.myBasicPublish({"name":"fred", "age":2}) + self.assertEmpty(self.q) + + def testMatchAny(self): + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) + self.myAssertPublishGet({"name":"fred"}) + self.myAssertPublishGet({"name":"fred", "ignoreme":10}) + self.myAssertPublishGet({"ignoreme":10, "age":3}) + + # Wont match + self.myBasicPublish({}) + self.myBasicPublish({"irrelevant":0}) + self.assertEmpty(self.q) + + +class MiscellaneousErrorsTests(TestBase): + """ + Test some miscellaneous error conditions + """ + def testTypeNotKnown(self): + try: + self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") + self.fail("Expected 503 for declaration of unknown exchange type.") + except Closed, e: + self.assertConnectionException(503, e.args[0]) + + def testDifferentDeclaredType(self): + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + try: + self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + self.fail("Expected 530 for redeclaration of exchange with different type.") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + #cleanup + other = self.connect() + c2 = other.channel(1) + c2.session_open() + c2.exchange_delete(exchange="test_different_declared_type_exchange") + +class ExchangeTests(TestBase): + def testHeadersBindNoMatchArg(self): + self.channel.queue_declare(queue="q", exclusive=True, auto_delete=True) + try: + self.channel.queue_bind(queue="q", exchange="amq.match", arguments={"name":"fred" , "age":3} ) + self.fail("Expected failure for missing x-match arg.") + except Closed, e: + self.assertConnectionException(541, e.args[0]) diff --git a/qpid/python/tests_0-10_preview/execution.py b/qpid/python/tests_0-10_preview/execution.py new file mode 100644 index 0000000000..3ff6d8ea65 --- /dev/null +++ b/qpid/python/tests_0-10_preview/execution.py @@ -0,0 +1,29 @@ +# +# 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. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class ExecutionTests (TestBase): + def test_flush(self): + channel = self.channel + for i in [1, 2, 3]: + channel.message_transfer( + content=Content(properties={'routing_key':str(i)})) + assert(channel.completion.wait(channel.completion.command_id, timeout=1)) diff --git a/qpid/python/tests_0-10_preview/message.py b/qpid/python/tests_0-10_preview/message.py new file mode 100644 index 0000000000..a3d32bdb2d --- /dev/null +++ b/qpid/python/tests_0-10_preview/message.py @@ -0,0 +1,834 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from qpid.reference import Reference, ReferenceId + +class MessageTests(TestBase): + """Tests for 'methods' on the amqp message 'class'""" + + def test_consume_no_local(self): + """ + Test that the no_local flag is honoured in the consume method + """ + channel = self.channel + #setup, declare two queues: + channel.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) + channel.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True) + #establish two consumers one of which excludes delivery of locally sent messages + self.subscribe(destination="local_included", queue="test-queue-1a") + self.subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) + + #send a message + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1b"}, body="consume_no_local")) + + #check the queues of the two consumers + excluded = self.client.queue("local_excluded") + included = self.client.queue("local_included") + msg = included.get(timeout=1) + self.assertEqual("consume_no_local", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + + def test_consume_no_local_awkward(self): + + """ + If an exclusive queue gets a no-local delivered to it, that + message could 'block' delivery of subsequent messages or it + could be left on the queue, possibly never being consumed + (this is the case for example in the qpid JMS mapping of + topics). This test excercises a Qpid C++ broker hack that + deletes such messages. + """ + + channel = self.channel + #setup: + channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + #establish consumer which excludes delivery of locally sent messages + self.subscribe(destination="local_excluded", queue="test-queue", no_local=True) + + #send a 'local' message + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="local")) + + #send a non local message + other = self.connect() + channel2 = other.channel(1) + channel2.session_open() + channel2.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="foreign")) + channel2.session_close() + other.close() + + #check that the second message only is delivered + excluded = self.client.queue("local_excluded") + msg = excluded.get(timeout=1) + self.assertEqual("foreign", msg.content.body) + try: + excluded.get(timeout=1) + self.fail("Received extra message") + except Empty: None + #check queue is empty + self.assertEqual(0, channel.queue_query(queue="test-queue").message_count) + + + def test_consume_exclusive(self): + """ + Test that the exclusive flag is honoured in the consume method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) + + #check that an exclusive consumer prevents other consumer being created: + self.subscribe(destination="first", queue="test-queue-2", exclusive=True) + try: + self.subscribe(destination="second", queue="test-queue-2") + self.fail("Expected consume request to fail due to previous exclusive consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + #open new channel and cleanup last consumer: + channel = self.client.channel(2) + channel.session_open() + + #check that an exclusive consumer cannot be created if a consumer already exists: + self.subscribe(channel, destination="first", queue="test-queue-2") + try: + self.subscribe(destination="second", queue="test-queue-2", exclusive=True) + self.fail("Expected exclusive consume request to fail due to previous consumer") + except Closed, e: + self.assertChannelException(403, e.args[0]) + + def test_consume_queue_errors(self): + """ + Test error conditions associated with the queue field of the consume method: + """ + channel = self.channel + try: + #queue specified but doesn't exist: + self.subscribe(queue="invalid-queue", destination="") + self.fail("Expected failure when consuming from non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(2) + channel.session_open() + try: + #queue not specified and none previously declared for channel: + self.subscribe(channel, queue="", destination="") + self.fail("Expected failure when consuming from unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_consume_unique_consumers(self): + """ + Ensure unique consumer tags are enforced + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-3", exclusive=True, auto_delete=True) + + #check that attempts to use duplicate tags are detected and prevented: + self.subscribe(destination="first", queue="test-queue-3") + try: + self.subscribe(destination="first", queue="test-queue-3") + self.fail("Expected consume request to fail due to non-unique tag") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + def test_cancel(self): + """ + Test compliance of the basic.cancel method + """ + channel = self.channel + #setup, declare a queue: + channel.queue_declare(queue="test-queue-4", exclusive=True, auto_delete=True) + self.subscribe(destination="my-consumer", queue="test-queue-4") + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="One")) + + #cancel should stop messages being delivered + channel.message_cancel(destination="my-consumer") + channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="Two")) + myqueue = self.client.queue("my-consumer") + msg = myqueue.get(timeout=1) + self.assertEqual("One", msg.content.body) + try: + msg = myqueue.get(timeout=1) + self.fail("Got message after cancellation: " + msg) + except Empty: None + + #cancellation of non-existant consumers should be handled without error + channel.message_cancel(destination="my-consumer") + channel.message_cancel(destination="this-never-existed") + + + def test_ack(self): + """ + Test basic ack/recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="test-ack-queue", exclusive=True, auto_delete=True) + + self.subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="One")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Two")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Three")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Four")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + msg2.complete(cumulative=True)#One and Two + msg4.complete(cumulative=False) + + channel.message_recover(requeue=False) + + msg3b = queue.get(timeout=1) + msg5b = queue.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + + def test_recover(self): + """ + Test recover behaviour + """ + channel = self.channel + channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="amq.fanout", queue="queue-a") + channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="amq.fanout", queue="queue-b") + + self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) + self.subscribe(queue="queue-b", destination="confirmed", confirm_mode=0) + confirmed = self.client.queue("confirmed") + unconfirmed = self.client.queue("unconfirmed") + + data = ["One", "Two", "Three", "Four", "Five"] + for d in data: + channel.message_transfer(destination="amq.fanout", content=Content(body=d)) + + for q in [confirmed, unconfirmed]: + for d in data: + self.assertEqual(d, q.get(timeout=1).content.body) + self.assertEmpty(q) + + channel.message_recover(requeue=False) + + self.assertEmpty(confirmed) + + while len(data): + msg = None + for d in data: + msg = unconfirmed.get(timeout=1) + self.assertEqual(d, msg.content.body) + self.assertEqual(True, msg.content['redelivered']) + self.assertEmpty(unconfirmed) + data.remove(msg.content.body) + msg.complete(cumulative=False) + channel.message_recover(requeue=False) + + + def test_recover_requeue(self): + """ + Test requeing on recovery + """ + channel = self.channel + channel.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) + + self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) + queue = self.client.queue("consumer_tag") + + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Two")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) + + msg1 = queue.get(timeout=1) + msg2 = queue.get(timeout=1) + msg3 = queue.get(timeout=1) + msg4 = queue.get(timeout=1) + msg5 = queue.get(timeout=1) + + self.assertEqual("One", msg1.content.body) + self.assertEqual("Two", msg2.content.body) + self.assertEqual("Three", msg3.content.body) + self.assertEqual("Four", msg4.content.body) + self.assertEqual("Five", msg5.content.body) + + msg2.complete(cumulative=True) #One and Two + msg4.complete(cumulative=False) #Four + + channel.message_cancel(destination="consumer_tag") + + #publish a new message + channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Six")) + #requeue unacked messages (Three and Five) + channel.message_recover(requeue=True) + + self.subscribe(queue="test-requeue", destination="consumer_tag") + queue2 = self.client.queue("consumer_tag") + + msg3b = queue2.get(timeout=1) + msg5b = queue2.get(timeout=1) + + self.assertEqual("Three", msg3b.content.body) + self.assertEqual("Five", msg5b.content.body) + + self.assertEqual(True, msg3b.content['redelivered']) + self.assertEqual(True, msg5b.content['redelivered']) + + self.assertEqual("Six", queue2.get(timeout=1).content.body) + + try: + extra = queue2.get(timeout=1) + self.fail("Got unexpected message in second queue: " + extra.content.body) + except Empty: None + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in original queue: " + extra.content.body) + except Empty: None + + + def test_qos_prefetch_count(self): + """ + Test that the prefetch count specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-count", exclusive=True, auto_delete=True) + subscription = self.subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) + queue = self.client.queue("consumer_tag") + + #set prefetch to 5: + channel.message_qos(prefetch_count=5) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-count"}, body="Message %d" % i)) + + #only 5 messages should have been delivered: + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + msg.complete() + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg.complete() + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + + + def test_qos_prefetch_size(self): + """ + Test that the prefetch size specified is honoured + """ + #setup: declare queue and subscribe + channel = self.channel + channel.queue_declare(queue="test-prefetch-size", exclusive=True, auto_delete=True) + subscription = self.subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) + queue = self.client.queue("consumer_tag") + + #set prefetch to 50 bytes (each message is 9 or 10 bytes): + channel.message_qos(prefetch_size=50) + + #publish 10 messages: + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body="Message %d" % i)) + + #only 5 messages should have been delivered (i.e. 45 bytes worth): + for i in range(1, 6): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 6th message in original queue: " + extra.content.body) + except Empty: None + + #ack messages and check that the next set arrive ok: + msg.complete() + + for i in range(6, 11): + msg = queue.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg.complete() + + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected 11th message in original queue: " + extra.content.body) + except Empty: None + + #make sure that a single oversized message still gets delivered + large = "abcdefghijklmnopqrstuvwxyz" + large = large + "-" + large; + channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body=large)) + msg = queue.get(timeout=1) + self.assertEqual(large, msg.content.body) + + def test_reject(self): + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True, alternate_exchange="amq.fanout") + channel.queue_declare(queue = "r", exclusive=True, auto_delete=True) + channel.queue_bind(queue = "r", exchange = "amq.fanout") + + self.subscribe(queue = "q", destination = "consumer", confirm_mode = 1) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body="blah, blah")) + msg = self.client.queue("consumer").get(timeout = 1) + self.assertEquals(msg.content.body, "blah, blah") + channel.message_reject([msg.command_id, msg.command_id]) + + self.subscribe(queue = "r", destination = "checker") + msg = self.client.queue("checker").get(timeout = 1) + self.assertEquals(msg.content.body, "blah, blah") + + def test_credit_flow_messages(self): + """ + Test basic credit based flow control with unit = message + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + #create consumer (for now that defaults to infinite credit) + channel.message_subscribe(queue = "q", destination = "c") + channel.message_flow_mode(mode = 0, destination = "c") + #send batch of messages to queue + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) + + #set message credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 0, value = 5, destination = "c") + #set infinite byte credit + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + for i in range(1, 6): + self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertEmpty(q) + + #increase credit again and check more are received + for i in range(6, 11): + channel.message_flow(unit = 0, value = 1, destination = "c") + self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertEmpty(q) + + def test_credit_flow_bytes(self): + """ + Test basic credit based flow control with unit = bytes + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + #create consumer (for now that defaults to infinite credit) + channel.message_subscribe(queue = "q", destination = "c") + channel.message_flow_mode(mode = 0, destination = "c") + #send batch of messages to queue + for i in range(10): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) + + #each message is currently interpreted as requiring msg_size bytes of credit + msg_size = 35 + + #set byte credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 1, value = msg_size*5, destination = "c") + #set infinite message credit + channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + for i in range(5): + self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertEmpty(q) + + #increase credit again and check more are received + for i in range(5): + channel.message_flow(unit = 1, value = msg_size, destination = "c") + self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertEmpty(q) + + + def test_window_flow_messages(self): + """ + Test basic window based flow control with unit = message + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + #create consumer (for now that defaults to infinite credit) + channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) + channel.message_flow_mode(mode = 1, destination = "c") + #send batch of messages to queue + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) + + #set message credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 0, value = 5, destination = "c") + #set infinite byte credit + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + for i in range(1, 6): + msg = q.get(timeout = 1) + self.assertDataEquals(channel, msg, "Message %d" % i) + self.assertEmpty(q) + + #acknowledge messages and check more are received + msg.complete(cumulative=True) + for i in range(6, 11): + self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertEmpty(q) + + + def test_window_flow_bytes(self): + """ + Test basic window based flow control with unit = bytes + """ + #declare an exclusive queue + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + #create consumer (for now that defaults to infinite credit) + channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) + channel.message_flow_mode(mode = 1, destination = "c") + #send batch of messages to queue + for i in range(10): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) + + #each message is currently interpreted as requiring msg_size bytes of credit + msg_size = 40 + + #set byte credit to finite amount (less than enough for all messages) + channel.message_flow(unit = 1, value = msg_size*5, destination = "c") + #set infinite message credit + channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + #check that expected number were received + q = self.client.queue("c") + msgs = [] + for i in range(5): + msg = q.get(timeout = 1) + msgs.append(msg) + self.assertDataEquals(channel, msg, "abcdefgh") + self.assertEmpty(q) + + #ack each message individually and check more are received + for i in range(5): + msg = msgs.pop() + msg.complete(cumulative=False) + self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertEmpty(q) + + def test_subscribe_not_acquired(self): + """ + Test the not-acquired modes works as expected for a simple case + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range(1, 6): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + + self.subscribe(queue = "q", destination = "a", acquire_mode = 1) + self.subscribe(queue = "q", destination = "b", acquire_mode = 1) + + for i in range(6, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + + #both subscribers should see all messages + qA = self.client.queue("a") + qB = self.client.queue("b") + for i in range(1, 11): + for q in [qA, qB]: + msg = q.get(timeout = 1) + self.assertEquals("Message %s" % i, msg.content.body) + msg.complete() + + #messages should still be on the queue: + self.assertEquals(10, channel.queue_query(queue = "q").message_count) + + def test_acquire(self): + """ + Test explicit acquire function + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) + + self.subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) + msg = self.client.queue("a").get(timeout = 1) + #message should still be on the queue: + self.assertEquals(1, channel.queue_query(queue = "q").message_count) + + channel.message_acquire([msg.command_id, msg.command_id]) + #check that we get notification (i.e. message_acquired) + response = channel.control_queue.get(timeout=1) + self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) + #message should have been removed from the queue: + self.assertEquals(0, channel.queue_query(queue = "q").message_count) + msg.complete() + + + + + def test_release(self): + """ + Test explicit release function + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "release me")) + + self.subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) + msg = self.client.queue("a").get(timeout = 1) + channel.message_cancel(destination = "a") + channel.message_release([msg.command_id, msg.command_id]) + msg.complete() + + #message should not have been removed from the queue: + self.assertEquals(1, channel.queue_query(queue = "q").message_count) + + def test_release_ordering(self): + """ + Test order of released messages is as expected + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range (1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "released message %s" % (i))) + + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + channel.message_flow(unit = 0, value = 10, destination = "a") + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + queue = self.client.queue("a") + first = queue.get(timeout = 1) + for i in range (2, 10): + self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + last = queue.get(timeout = 1) + self.assertEmpty(queue) + channel.message_release([first.command_id, last.command_id]) + last.complete()#will re-allocate credit, as in window mode + for i in range (1, 11): + self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + + def test_ranged_ack(self): + """ + Test acking of messages ranges + """ + channel = self.channel + channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range (1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message %s" % (i))) + + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + channel.message_flow(unit = 0, value = 10, destination = "a") + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + queue = self.client.queue("a") + for i in range (1, 11): + self.assertEquals("message %s" % (i), queue.get(timeout = 1).content.body) + self.assertEmpty(queue) + + #ack all but the third message (command id 2) + channel.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=[0,1,3,6,7,7,8,9]) + channel.message_recover() + self.assertEquals("message 3", queue.get(timeout = 1).content.body) + self.assertEmpty(queue) + + def test_subscribe_not_acquired_2(self): + channel = self.channel + + #publish some messages + self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + + #consume some of them + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + channel.message_flow_mode(mode = 0, destination = "a") + channel.message_flow(unit = 0, value = 5, destination = "a") + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + + queue = self.client.queue("a") + for i in range(1, 6): + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + msg.complete() + self.assertEmpty(queue) + + #now create a not-acquired subscriber + channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + + #check it gets those not consumed + queue = self.client.queue("b") + channel.message_flow(unit = 0, value = 1, destination = "b") + for i in range(6, 11): + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + msg.complete() + channel.message_flow(unit = 0, value = 1, destination = "b") + self.assertEmpty(queue) + + #check all 'browsed' messages are still on the queue + self.assertEqual(5, channel.queue_query(queue="q").message_count) + + def test_subscribe_not_acquired_3(self): + channel = self.channel + + #publish some messages + self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + for i in range(1, 11): + channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + + #create a not-acquired subscriber + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + channel.message_flow(unit = 0, value = 10, destination = "a") + + #browse through messages + queue = self.client.queue("a") + for i in range(1, 11): + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + if (i % 2): + #try to acquire every second message + channel.message_acquire([msg.command_id, msg.command_id]) + #check that acquire succeeds + response = channel.control_queue.get(timeout=1) + self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) + msg.complete() + self.assertEmpty(queue) + + #create a second not-acquired subscriber + channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + channel.message_flow(unit = 0, value = 1, destination = "b") + #check it gets those not consumed + queue = self.client.queue("b") + for i in [2,4,6,8,10]: + msg = queue.get(timeout = 1) + self.assertEquals("message-%d" % (i), msg.content.body) + msg.complete() + channel.message_flow(unit = 0, value = 1, destination = "b") + self.assertEmpty(queue) + + #check all 'browsed' messages are still on the queue + self.assertEqual(5, channel.queue_query(queue="q").message_count) + + def test_release_unacquired(self): + channel = self.channel + + #create queue + self.queue_declare(queue = "q", exclusive=True, auto_delete=True, durable=True) + + #send message + channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) + + #create two 'browsers' + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + channel.message_flow(unit = 0, value = 10, destination = "a") + queueA = self.client.queue("a") + + channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + channel.message_flow(unit = 0, value = 10, destination = "b") + queueB = self.client.queue("b") + + #have each browser release the message + msgA = queueA.get(timeout = 1) + channel.message_release([msgA.command_id, msgA.command_id]) + + msgB = queueB.get(timeout = 1) + channel.message_release([msgB.command_id, msgB.command_id]) + + #cancel browsers + channel.message_cancel(destination = "a") + channel.message_cancel(destination = "b") + + #create consumer + channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1, acquire_mode=0) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + channel.message_flow(unit = 0, value = 10, destination = "c") + queueC = self.client.queue("c") + #consume the message then ack it + msgC = queueC.get(timeout = 1) + msgC.complete() + #ensure there are no other messages + self.assertEmpty(queueC) + + def test_no_size(self): + self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + + ch = self.channel + ch.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) + + ch.message_subscribe(queue = "q", destination="d", confirm_mode = 0) + ch.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "d") + ch.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "d") + + queue = self.client.queue("d") + msg = queue.get(timeout = 3) + self.assertEquals("message-body", msg.content.body) + + def assertDataEquals(self, channel, msg, expected): + self.assertEquals(expected, msg.content.body) + + def assertEmpty(self, queue): + try: + extra = queue.get(timeout=1) + self.fail("Queue not empty, contains: " + extra.content.body) + except Empty: None + +class SizelessContent(Content): + + def size(self): + return None diff --git a/qpid/python/tests_0-10_preview/persistence.py b/qpid/python/tests_0-10_preview/persistence.py new file mode 100644 index 0000000000..ad578474eb --- /dev/null +++ b/qpid/python/tests_0-10_preview/persistence.py @@ -0,0 +1,67 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class PersistenceTests(TestBase): + def test_delete_queue_after_publish(self): + channel = self.channel + channel.synchronous = False + + #create queue + channel.queue_declare(queue = "q", auto_delete=True, durable=True) + + #send message + for i in range(1, 10): + channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) + + channel.synchronous = True + #explicitly delete queue + channel.queue_delete(queue = "q") + + def test_ack_message_from_deleted_queue(self): + channel = self.channel + channel.synchronous = False + + #create queue + channel.queue_declare(queue = "q", auto_delete=True, durable=True) + + #send message + channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) + + #create consumer + channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=0) + channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + channel.message_flow(unit = 0, value = 10, destination = "a") + queue = self.client.queue("a") + + #consume the message, cancel subscription (triggering auto-delete), then ack it + msg = queue.get(timeout = 5) + channel.message_cancel(destination = "a") + msg.complete() + + def test_queue_deletion(self): + channel = self.channel + channel.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True) + channel.queue_bind(exchange="amq.topic", queue="durable-subscriber-queue", routing_key="xyz") + channel.message_transfer(destination= "amq.topic", content=Content(properties={'routing_key' : "xyz", 'delivery_mode':2}, body = "my-message")) + channel.queue_delete(queue = "durable-subscriber-queue") + diff --git a/qpid/python/tests_0-10_preview/query.py b/qpid/python/tests_0-10_preview/query.py new file mode 100644 index 0000000000..eba2ee6dd1 --- /dev/null +++ b/qpid/python/tests_0-10_preview/query.py @@ -0,0 +1,227 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueryTests(TestBase): + """Tests for various query methods introduced in 0-10 and available in 0-9 for preview""" + + def test_exchange_query(self): + """ + Test that the exchange_query method works as expected + """ + channel = self.channel + #check returned type for the standard exchanges + self.assert_type("direct", channel.exchange_query(name="amq.direct")) + self.assert_type("topic", channel.exchange_query(name="amq.topic")) + self.assert_type("fanout", channel.exchange_query(name="amq.fanout")) + self.assert_type("headers", channel.exchange_query(name="amq.match")) + self.assert_type("direct", channel.exchange_query(name="")) + #declare an exchange + channel.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) + #check that the result of a query is as expected + response = channel.exchange_query(name="my-test-exchange") + self.assert_type("direct", response) + self.assertEqual(False, response.durable) + self.assertEqual(False, response.not_found) + #delete the exchange + channel.exchange_delete(exchange="my-test-exchange") + #check that the query now reports not-found + self.assertEqual(True, channel.exchange_query(name="my-test-exchange").not_found) + + def assert_type(self, expected_type, response): + self.assertEqual(expected_type, response.__getattr__("type")) + + def test_binding_query_direct(self): + """ + Test that the binding_query method works as expected with the direct exchange + """ + self.binding_query_with_key("amq.direct") + + def test_binding_query_topic(self): + """ + Test that the binding_query method works as expected with the direct exchange + """ + self.binding_query_with_key("amq.topic") + + def binding_query_with_key(self, exchange_name): + channel = self.channel + #setup: create two queues + channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) + + channel.queue_bind(exchange=exchange_name, queue="used-queue", routing_key="used-key") + + # test detection of any binding to specific queue + response = channel.binding_query(exchange=exchange_name, queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test detection of specific binding to any queue + response = channel.binding_query(exchange=exchange_name, routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.key_not_matched) + + # test detection of specific binding to specific queue + response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(False, response.key_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + # test unspecified queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.key_not_matched) + + # test matched queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(True, response.key_not_matched) + + # test unmatched queue, matched binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="used-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(False, response.key_not_matched) + + # test unmatched queue, unmatched binding + response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="unused-key") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(True, response.key_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange=exchange_name, queue="unknown-queue").queue_not_found) + + + def test_binding_query_fanout(self): + """ + Test that the binding_query method works as expected with fanout exchange + """ + channel = self.channel + #setup + channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="amq.fanout", queue="used-queue") + + # test detection of any binding to specific queue + response = channel.binding_query(exchange="amq.fanout", queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange="amq.fanout", queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange="amq.fanout", queue="unknown-queue").queue_not_found) + + def test_binding_query_header(self): + """ + Test that the binding_query method works as expected with headers exchanges + """ + channel = self.channel + #setup + channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) + channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) + channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) + + # test detection of any binding to specific queue + response = channel.binding_query(exchange="amq.match", queue="used-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + + # test detection of specific binding to any queue + response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.args_not_matched) + + # test detection of specific binding to specific queue + response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(False, response.args_not_matched) + + # test unmatched queue, unspecified binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue") + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + + # test unspecified queue, unmatched binding + response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.args_not_matched) + + # test matched queue, unmatched binding + response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(False, response.queue_not_matched) + self.assertEqual(True, response.args_not_matched) + + # test unmatched queue, matched binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "a":"A"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(False, response.args_not_matched) + + # test unmatched queue, unmatched binding + response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "b":"B"}) + self.assertEqual(False, response.exchange_not_found) + self.assertEqual(False, response.queue_not_found) + self.assertEqual(True, response.queue_not_matched) + self.assertEqual(True, response.args_not_matched) + + #test exchange not found + self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) + + #test queue not found + self.assertEqual(True, channel.binding_query(exchange="amq.match", queue="unknown-queue").queue_not_found) + diff --git a/qpid/python/tests_0-10_preview/queue.py b/qpid/python/tests_0-10_preview/queue.py new file mode 100644 index 0000000000..7b3590d11b --- /dev/null +++ b/qpid/python/tests_0-10_preview/queue.py @@ -0,0 +1,338 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class QueueTests(TestBase): + """Tests for 'methods' on the amqp queue 'class'""" + + def test_purge(self): + """ + Test that the purge method removes messages from the queue + """ + channel = self.channel + #setup, declare a queue and add some messages to it: + channel.exchange_declare(exchange="test-exchange", type="direct") + channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") + channel.message_transfer(destination="test-exchange", content=Content("one", properties={'routing_key':"key"})) + channel.message_transfer(destination="test-exchange", content=Content("two", properties={'routing_key':"key"})) + channel.message_transfer(destination="test-exchange", content=Content("three", properties={'routing_key':"key"})) + + #check that the queue now reports 3 messages: + channel.queue_declare(queue="test-queue") + reply = channel.queue_query(queue="test-queue") + self.assertEqual(3, reply.message_count) + + #now do the purge, then test that three messages are purged and the count drops to 0 + channel.queue_purge(queue="test-queue"); + reply = channel.queue_query(queue="test-queue") + self.assertEqual(0, reply.message_count) + + #send a further message and consume it, ensuring that the other messages are really gone + channel.message_transfer(destination="test-exchange", content=Content("four", properties={'routing_key':"key"})) + self.subscribe(queue="test-queue", destination="tag") + queue = self.client.queue("tag") + msg = queue.get(timeout=1) + self.assertEqual("four", msg.content.body) + + #check error conditions (use new channels): + channel = self.client.channel(2) + channel.session_open() + try: + #queue specified but doesn't exist: + channel.queue_purge(queue="invalid-queue") + self.fail("Expected failure when purging non-existent queue") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + channel = self.client.channel(3) + channel.session_open() + try: + #queue not specified and none previously declared for channel: + channel.queue_purge() + self.fail("Expected failure when purging unspecified queue") + except Closed, e: + self.assertConnectionException(530, e.args[0]) + + #cleanup + other = self.connect() + channel = other.channel(1) + channel.session_open() + channel.exchange_delete(exchange="test-exchange") + + def test_declare_exclusive(self): + """ + Test that the exclusive field is honoured in queue.declare + """ + # TestBase.setUp has already opened channel(1) + c1 = self.channel + # Here we open a second separate connection: + other = self.connect() + c2 = other.channel(1) + c2.session_open() + + #declare an exclusive queue: + c1.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) + try: + #other connection should not be allowed to declare this: + c2.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) + self.fail("Expected second exclusive queue_declare to raise a channel exception") + except Closed, e: + self.assertChannelException(405, e.args[0]) + + + def test_declare_passive(self): + """ + Test that the passive field is honoured in queue.declare + """ + channel = self.channel + #declare an exclusive queue: + channel.queue_declare(queue="passive-queue-1", exclusive=True, auto_delete=True) + channel.queue_declare(queue="passive-queue-1", passive=True) + try: + #other connection should not be allowed to declare this: + channel.queue_declare(queue="passive-queue-2", passive=True) + self.fail("Expected passive declaration of non-existant queue to raise a channel exception") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_bind(self): + """ + Test various permutations of the queue.bind method + """ + channel = self.channel + channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) + + #straightforward case, both exchange & queue exist so no errors expected: + channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + + #use the queue name where the routing key is not specified: + channel.queue_bind(queue="queue-1", exchange="amq.direct") + + #try and bind to non-existant exchange + try: + channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + self.fail("Expected bind to non-existant exchange to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #need to reopen a channel: + channel = self.client.channel(2) + channel.session_open() + + #try and bind non-existant queue: + try: + channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + self.fail("Expected bind of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_unbind_direct(self): + self.unbind_test(exchange="amq.direct", routing_key="key") + + def test_unbind_topic(self): + self.unbind_test(exchange="amq.topic", routing_key="key") + + def test_unbind_fanout(self): + self.unbind_test(exchange="amq.fanout") + + def test_unbind_headers(self): + self.unbind_test(exchange="amq.match", args={ "x-match":"all", "a":"b"}, headers={"a":"b"}) + + def unbind_test(self, exchange, routing_key="", args=None, headers={}): + #bind two queues and consume from them + channel = self.channel + + channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) + channel.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) + + self.subscribe(queue="queue-1", destination="queue-1") + self.subscribe(queue="queue-2", destination="queue-2") + + queue1 = self.client.queue("queue-1") + queue2 = self.client.queue("queue-2") + + channel.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) + + #send a message that will match both bindings + channel.message_transfer(destination=exchange, + content=Content("one", properties={'routing_key':routing_key, 'application_headers':headers})) + + #unbind first queue + channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + + #send another message + channel.message_transfer(destination=exchange, + content=Content("two", properties={'routing_key':routing_key, 'application_headers':headers})) + + #check one queue has both messages and the other has only one + self.assertEquals("one", queue1.get(timeout=1).content.body) + try: + msg = queue1.get(timeout=1) + self.fail("Got extra message: %s" % msg.content.body) + except Empty: pass + + self.assertEquals("one", queue2.get(timeout=1).content.body) + self.assertEquals("two", queue2.get(timeout=1).content.body) + try: + msg = queue2.get(timeout=1) + self.fail("Got extra message: " + msg) + except Empty: pass + + + def test_delete_simple(self): + """ + Test core queue deletion behaviour + """ + channel = self.channel + + #straight-forward case: + channel.queue_declare(queue="delete-me") + channel.message_transfer(content=Content("a", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("b", properties={'routing_key':"delete-me"})) + channel.message_transfer(content=Content("c", properties={'routing_key':"delete-me"})) + channel.queue_delete(queue="delete-me") + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="delete-me", passive=True) + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + #check attempted deletion of non-existant queue is handled correctly: + channel = self.client.channel(2) + channel.session_open() + try: + channel.queue_delete(queue="i-dont-exist", if_empty=True) + self.fail("Expected delete of non-existant queue to fail") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + + def test_delete_ifempty(self): + """ + Test that if_empty field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and add a message to it (use default binding): + channel.queue_declare(queue="delete-me-2") + channel.queue_declare(queue="delete-me-2", passive=True) + channel.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) + + #try to delete, but only if empty: + try: + channel.queue_delete(queue="delete-me-2", if_empty=True) + self.fail("Expected delete if_empty to fail for non-empty queue") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + #need new channel now: + channel = self.client.channel(2) + channel.session_open() + + #empty queue: + self.subscribe(channel, destination="consumer_tag", queue="delete-me-2") + queue = self.client.queue("consumer_tag") + msg = queue.get(timeout=1) + self.assertEqual("message", msg.content.body) + channel.message_cancel(destination="consumer_tag") + + #retry deletion on empty queue: + channel.queue_delete(queue="delete-me-2", if_empty=True) + + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-2", passive=True) + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + def test_delete_ifunused(self): + """ + Test that if_unused field of queue_delete is honoured + """ + channel = self.channel + + #create a queue and register a consumer: + channel.queue_declare(queue="delete-me-3") + channel.queue_declare(queue="delete-me-3", passive=True) + self.subscribe(destination="consumer_tag", queue="delete-me-3") + + #need new channel now: + channel2 = self.client.channel(2) + channel2.session_open() + #try to delete, but only if empty: + try: + channel2.queue_delete(queue="delete-me-3", if_unused=True) + self.fail("Expected delete if_unused to fail for queue with existing consumer") + except Closed, e: + self.assertChannelException(406, e.args[0]) + + + channel.message_cancel(destination="consumer_tag") + channel.queue_delete(queue="delete-me-3", if_unused=True) + #check that it has gone by declaring passively: + try: + channel.queue_declare(queue="delete-me-3", passive=True) + self.fail("Queue has not been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + + def test_autodelete_shared(self): + """ + Test auto-deletion (of non-exclusive queues) + """ + channel = self.channel + other = self.connect() + channel2 = other.channel(1) + channel2.session_open() + + channel.queue_declare(queue="auto-delete-me", auto_delete=True) + + #consume from both channels + reply = channel.basic_consume(queue="auto-delete-me") + channel2.basic_consume(queue="auto-delete-me") + + #implicit cancel + channel2.session_close() + + #check it is still there + channel.queue_declare(queue="auto-delete-me", passive=True) + + #explicit cancel => queue is now unused again: + channel.basic_cancel(consumer_tag=reply.consumer_tag) + + #NOTE: this assumes there is no timeout in use + + #check that it has gone be declaring passively + try: + channel.queue_declare(queue="auto-delete-me", passive=True) + self.fail("Expected queue to have been deleted") + except Closed, e: + self.assertChannelException(404, e.args[0]) + + diff --git a/qpid/python/tests_0-10_preview/testlib.py b/qpid/python/tests_0-10_preview/testlib.py new file mode 100644 index 0000000000..a0355c4ce0 --- /dev/null +++ b/qpid/python/tests_0-10_preview/testlib.py @@ -0,0 +1,66 @@ +# +# 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. +# + +# +# Tests for the testlib itself. +# + +from qpid.content import Content +from qpid.testlib import testrunner, TestBase +from Queue import Empty + +import sys +from traceback import * + +def mytrace(frame, event, arg): + print_stack(frame); + print "====" + return mytrace + +class TestBaseTest(TestBase): + """Verify TestBase functions work as expected""" + + def testAssertEmptyPass(self): + """Test assert empty works""" + self.queue_declare(queue="empty") + q = self.consume("empty") + self.assertEmpty(q) + try: + q.get(timeout=1) + self.fail("Queue is not empty.") + except Empty: None # Ignore + + def testAssertEmptyFail(self): + self.queue_declare(queue="full") + q = self.consume("full") + self.channel.message_transfer(content=Content("", properties={'routing_key':"full"})) + try: + self.assertEmpty(q); + self.fail("assertEmpty did not assert on non-empty queue") + except AssertionError: None # Ignore + + def testMessageProperties(self): + """Verify properties are passed with message""" + props={"x":1, "y":2} + self.queue_declare(queue="q") + q = self.consume("q") + self.assertPublishGet(q, routing_key="q", properties=props) + + + diff --git a/qpid/python/tests_0-10_preview/tx.py b/qpid/python/tests_0-10_preview/tx.py new file mode 100644 index 0000000000..3fd1065af3 --- /dev/null +++ b/qpid/python/tests_0-10_preview/tx.py @@ -0,0 +1,231 @@ +# +# 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. +# +from qpid.client import Client, Closed +from qpid.queue import Empty +from qpid.content import Content +from qpid.testlib import testrunner, TestBase + +class TxTests(TestBase): + """ + Tests for 'methods' on the amqp tx 'class' + """ + + def test_commit(self): + """ + Test that commited publishes are delivered and commited acks are not re-delivered + """ + channel2 = self.client.channel(2) + channel2.session_open() + self.perform_txn_work(channel2, "tx-commit-a", "tx-commit-b", "tx-commit-c") + channel2.tx_commit() + channel2.session_close() + + #use a different channel with new subscriptions to ensure + #there is no redelivery of acked messages: + channel = self.channel + channel.tx_select() + + self.subscribe(channel, queue="tx-commit-a", destination="qa", confirm_mode=1) + queue_a = self.client.queue("qa") + + self.subscribe(channel, queue="tx-commit-b", destination="qb", confirm_mode=1) + queue_b = self.client.queue("qb") + + self.subscribe(channel, queue="tx-commit-c", destination="qc", confirm_mode=1) + queue_c = self.client.queue("qc") + + #check results + for i in range(1, 5): + msg = queue_c.get(timeout=1) + self.assertEqual("TxMessage %d" % i, msg.content.body) + msg.complete() + + msg = queue_b.get(timeout=1) + self.assertEqual("TxMessage 6", msg.content.body) + msg.complete() + + msg = queue_a.get(timeout=1) + self.assertEqual("TxMessage 7", msg.content.body) + msg.complete() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def test_auto_rollback(self): + """ + Test that a channel closed with an open transaction is effectively rolled back + """ + channel2 = self.client.channel(2) + channel2.session_open() + queue_a, queue_b, queue_c = self.perform_txn_work(channel2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + channel2.session_close() + channel = self.channel + channel.tx_select() + + self.subscribe(channel, queue="tx-autorollback-a", destination="qa", confirm_mode=1) + queue_a = self.client.queue("qa") + + self.subscribe(channel, queue="tx-autorollback-b", destination="qb", confirm_mode=1) + queue_b = self.client.queue("qb") + + self.subscribe(channel, queue="tx-autorollback-c", destination="qc", confirm_mode=1) + queue_c = self.client.queue("qc") + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + msg.complete() + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + msg.complete() + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + msg.complete() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def test_rollback(self): + """ + Test that rolled back publishes are not delivered and rolled back acks are re-delivered + """ + channel = self.channel + queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #stop subscriptions (ensures no delivery occurs during rollback as messages are requeued) + for d in ["sub_a", "sub_b", "sub_c"]: + channel.message_stop(destination=d) + + channel.tx_rollback() + + #restart susbcriptions + for d in ["sub_a", "sub_b", "sub_c"]: + channel.message_flow(destination=d, unit=0, value=0xFFFFFFFF) + channel.message_flow(destination=d, unit=1, value=0xFFFFFFFF) + + #check results + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + msg.complete() + + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + msg.complete() + + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + msg.complete() + + for q in [queue_a, queue_b, queue_c]: + try: + extra = q.get(timeout=1) + self.fail("Got unexpected message: " + extra.content.body) + except Empty: None + + #cleanup + channel.tx_commit() + + def perform_txn_work(self, channel, name_a, name_b, name_c): + """ + Utility method that does some setup and some work under a transaction. Used for testing both + commit and rollback + """ + #setup: + channel.queue_declare(queue=name_a, exclusive=True, auto_delete=True) + channel.queue_declare(queue=name_b, exclusive=True, auto_delete=True) + channel.queue_declare(queue=name_c, exclusive=True, auto_delete=True) + + key = "my_key_" + name_b + topic = "my_topic_" + name_c + + channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) + channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) + + for i in range(1, 5): + channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"msg%d" % i}, body="Message %d" % i)) + + channel.message_transfer(destination="amq.direct", + content=Content(properties={'routing_key':key, 'message_id':"msg6"}, body="Message 6")) + channel.message_transfer(destination="amq.topic", + content=Content(properties={'routing_key':topic, 'message_id':"msg7"}, body="Message 7")) + + channel.tx_select() + + #consume and ack messages + self.subscribe(channel, queue=name_a, destination="sub_a", confirm_mode=1) + queue_a = self.client.queue("sub_a") + for i in range(1, 5): + msg = queue_a.get(timeout=1) + self.assertEqual("Message %d" % i, msg.content.body) + + msg.complete() + + self.subscribe(channel, queue=name_b, destination="sub_b", confirm_mode=1) + queue_b = self.client.queue("sub_b") + msg = queue_b.get(timeout=1) + self.assertEqual("Message 6", msg.content.body) + msg.complete() + + sub_c = self.subscribe(channel, queue=name_c, destination="sub_c", confirm_mode=1) + queue_c = self.client.queue("sub_c") + msg = queue_c.get(timeout=1) + self.assertEqual("Message 7", msg.content.body) + msg.complete() + + #publish messages + for i in range(1, 5): + channel.message_transfer(destination="amq.topic", + content=Content(properties={'routing_key':topic, 'message_id':"tx-msg%d" % i}, + body="TxMessage %d" % i)) + + channel.message_transfer(destination="amq.direct", + content=Content(properties={'routing_key':key, 'message_id':"tx-msg6"}, + body="TxMessage 6")) + channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"tx-msg7"}, + body="TxMessage 7")) + return queue_a, queue_b, queue_c -- cgit v1.2.1 From 7f4b49eba1746a7ce90378dd5f311db38317b16a Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 6 Mar 2008 11:44:36 +0000 Subject: Fix message delivery for 0-10 final codepath Convert two more python tests to use 0-10 client git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634229 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 5 ----- qpid/python/tests_0-10/broker.py | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 974c165ae9..e1f6a60f3a 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -2,9 +2,6 @@ tests.codec.FieldTableTestCase.test_field_table_decode tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair tests_0-10.query.QueryTests.test_exchange_bound_header -tests_0-10.persistence.PersistenceTests.test_ack_message_from_deleted_queue -tests_0-10.persistence.PersistenceTests.test_delete_queue_after_publish -tests_0-10.persistence.PersistenceTests.test_queue_deletion tests_0-10.tx.TxTests.test_auto_rollback tests_0-10.tx.TxTests.test_commit tests_0-10.tx.TxTests.test_rollback @@ -92,6 +89,4 @@ tests_0-10.queue.QueueTests.test_unbind_topic tests_0-10.broker.BrokerTests.test_closed_channel tests_0-10.broker.BrokerTests.test_ack_and_no_ack tests_0-10.broker.BrokerTests.test_invalid_channel -tests_0-10.broker.BrokerTests.test_simple_delivery_queued -tests_0-10.broker.BrokerTests.test_simple_delivery_immediate tests_0-10.example.ExampleTest.test_example diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index bfecb5c166..100288bbbb 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -70,7 +70,7 @@ class BrokerTests(TestBase010): body = "Immediate Delivery" session.message_transfer("amq.fanout", None, None, Message(body)) msg = queue.get(timeout=5) - self.assert_(msg.content.body == body) + self.assert_(msg.body == body) def test_simple_delivery_queued(self): """ @@ -89,7 +89,7 @@ class BrokerTests(TestBase010): session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) queue = session.incoming(consumer_tag) msg = queue.get(timeout=5) - self.assert_(msg.content.body == body) + self.assert_(msg.body == body) def test_invalid_channel(self): channel = self.client.channel(200) -- cgit v1.2.1 From abd31e8b7fc71145d2553020da2c405f8e4cf35d Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 6 Mar 2008 13:00:58 +0000 Subject: added codec for sequence_set; added id to Message; RangeSet -> RangedSet; added RangedSet.add(lower, upper) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634255 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 16 +++++++++++----- qpid/python/qpid/codec010.py | 18 ++++++++++++++++++ qpid/python/qpid/datatypes.py | 36 +++++++++++++++++++----------------- qpid/python/qpid/session.py | 7 +++---- qpid/python/server010 | 3 +++ qpid/python/tests/datatypes.py | 6 +++--- 6 files changed, 57 insertions(+), 29 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 2b762b1296..5ae42c2ac1 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -4,7 +4,7 @@ import sys, logging from qpid.connection010 import Connection from qpid.spec010 import load from qpid.util import connect -from qpid.datatypes import Message +from qpid.datatypes import Message, RangedSet if "-v" in sys.argv: level = logging.DEBUG @@ -27,10 +27,16 @@ ssn.message_transfer("is", None, None, Message("more testing...")) ssn.message_transfer("a") ssn.message_transfer("test") -print ssn.incoming("this").get() -print ssn.incoming("is").get() -print ssn.incoming("a").get() -print ssn.incoming("test").get() +m1 = ssn.incoming("this").get() +print m1 +m2 = ssn.incoming("is").get() +print m2 +m3 = ssn.incoming("a").get() +print m3 +m4 = ssn.incoming("test").get() +print m4 + +ssn.message_accept(RangedSet(m1.id, m2.id, m3.id, m4.id)) print ssn.queue_query("testing") diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 2dcba4e917..f6539ffcee 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -18,6 +18,7 @@ # from packer import Packer +from datatypes import RangedSet class Codec(Packer): @@ -123,6 +124,23 @@ class Codec(Packer): self.write_uint16(len(b)) self.write(b) + def read_sequence_set(self): + result = RangedSet() + size = self.read_uint16() + nranges = size/8 + while nranges > 0: + lower = self.read_sequence_no() + upper = self.read_sequence_no() + result.add(lower, upper) + nranges -= 1 + return result + def write_sequence_set(self, ss): + size = 8*len(ss.ranges) + self.write_uint16(size) + for range in ss.ranges: + self.write_sequence_no(range.lower) + self.write_sequence_no(range.upper) + def read_vbin32(self): return self.read(self.read_uint32()) def write_vbin32(self, b): diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index efd1fdd4ff..7158e0d75e 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -42,20 +42,26 @@ class Message: self.headers = args[:-1] else: self.headers = None + self.id = None def __repr__(self): args = [] if self.headers: - args.extend(self.headers) + args.extend(map(repr, self.headers)) if self.body: - args.append(self.body) - return "Message(%s)" % ", ".join(map(repr, args)) + args.append(repr(self.body)) + if self.id is not None: + args.append("id=%s" % self.id) + return "Message(%s)" % ", ".join(args) class Range: - def __init__(self, lower, upper): + def __init__(self, lower, upper = None): self.lower = lower - self.upper = upper + if upper is None: + self.upper = lower + else: + self.upper = upper def __contains__(self, n): return self.lower <= n and n <= self.upper @@ -69,16 +75,15 @@ class Range: def span(self, r): return Range(min(self.lower, r.lower), max(self.upper, r.upper)) - def __str__(self): - return "Range(%s, %s)" % (self.lower, self.upper) - def __repr__(self): - return str(self) + return "Range(%s, %s)" % (self.lower, self.upper) -class RangeSet: +class RangedSet: - def __init__(self): + def __init__(self, *args): self.ranges = [] + for n in args: + self.add(n) def __contains__(self, n): for r in self.ranges: @@ -100,14 +105,11 @@ class RangeSet: idx += 1 self.ranges.append(range) - def add(self, n): - self.add_range(Range(n, n)) - - def __str__(self): - return "RangeSet(%s)" % str(self.ranges) + def add(self, lower, upper = None): + self.add_range(Range(lower, upper)) def __repr__(self): - return str(self) + return "RangedSet(%s)" % str(self.ranges) class Future: def __init__(self, initial=None): diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 334902bbf3..7702a19251 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -19,7 +19,7 @@ from threading import Event, RLock from invoker import Invoker -from datatypes import RangeSet, Struct, Future +from datatypes import RangedSet, Struct, Future from codec010 import StringCodec from assembler import Segment from queue import Queue @@ -170,7 +170,7 @@ class Receiver: self.session = session self.next_id = None self.next_offset = None - self._completed = RangeSet() + self._completed = RangedSet() def received(self, seg): if self.next_id == None or self.next_offset == None: @@ -220,8 +220,6 @@ class Sender: else: idx += 1 -from queue import Queue, Closed, Empty - class Delegate: def __init__(self, session): @@ -238,6 +236,7 @@ class Client(Delegate): def message_transfer(self, cmd, headers, body): m = Message(body) m.headers = headers + m.id = cmd.id messages = self.session.incoming(cmd.destination) messages.put(m) msg.debug("RECV: %s", m) diff --git a/qpid/python/server010 b/qpid/python/server010 index 6d89ee5ea0..ee47731a60 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -33,6 +33,9 @@ class SessionDelegate(Client): m.header = header self.session.message_transfer(cmd.destination, cmd.accept_mode, cmd.acquire_mode, m) + def message_accept(self, messages): + print "ACCEPT %s" % messages + server = Server() for s in listen("0.0.0.0", spec.port): diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index cafd53c89f..e22e250f61 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -20,7 +20,7 @@ from unittest import TestCase from qpid.datatypes import * -class RangeSetTest(TestCase): +class RangedSetTest(TestCase): def check(self, ranges): posts = [] @@ -39,7 +39,7 @@ class RangeSetTest(TestCase): idx += 2 def test(self): - rs = RangeSet() + rs = RangedSet() self.check(rs.ranges) @@ -85,7 +85,7 @@ class RangeSetTest(TestCase): rs.add(-3) self.check(rs.ranges) - rs.add_range(Range(1, 20)) + rs.add(1, 20) assert 21 not in rs assert 20 in rs self.check(rs.ranges) -- cgit v1.2.1 From 00a8a8b331934b6bebb18d9f2cccad363c939e63 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 6 Mar 2008 15:05:04 +0000 Subject: preliminary support for message headers git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634289 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 4 +++- qpid/python/qpid/session.py | 10 +++++++++- qpid/python/server010 | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 5ae42c2ac1..ff01cf9ed7 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -22,8 +22,10 @@ ssn = conn.session("my-session", timeout=10) ssn.queue_declare("asdf") + ssn.message_transfer("this", None, None, Message("testing...")) -ssn.message_transfer("is", None, None, Message("more testing...")) +props = ssn.delivery_properties(routing_key="asdf") +ssn.message_transfer("is", None, None, Message(props, "more testing...")) ssn.message_transfer("a") ssn.message_transfer("test") diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 7702a19251..c628762dac 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -73,9 +73,17 @@ class Session(Invoker): if cmd is not None and cmd.track == self.spec["track.command"].value: return cmd else: + # XXX + for st in self.spec.structs.values(): + if st.name == name: + return st return None def invoke(self, type, args, kwargs): + # XXX + if not hasattr(type, "track"): + return type.new(args, kwargs) + if self.channel == None: raise SessionDetached() @@ -106,7 +114,7 @@ class Session(Invoker): if message.headers != None: sc = StringCodec(self.spec) for st in message.headers: - sc.write_struct32(st.type, st) + sc.write_struct32(st) seg = Segment(False, message.body == None, self.spec["segment_type.header"].value, type.track, self.channel.id, sc.encoded) self.send(seg) diff --git a/qpid/python/server010 b/qpid/python/server010 index ee47731a60..ecf76577ec 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -28,9 +28,9 @@ class SessionDelegate(Client): def queue_query(self, qq): return qq.type.result.type.new((qq.queue,), {}) - def message_transfer(self, cmd, header, body): + def message_transfer(self, cmd, headers, body): m = Message(body) - m.header = header + m.headers = headers self.session.message_transfer(cmd.destination, cmd.accept_mode, cmd.acquire_mode, m) def message_accept(self, messages): -- cgit v1.2.1 From 8d744424bd71e57768f54ee0c96656ba2bfb9392 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 6 Mar 2008 17:52:59 +0000 Subject: Fixes to c++ broker: - use final version of delivery properties where appropriate - start sequence numbering of outgoing messages at 0 - explicit accept-mode is now 0 (no more confirm-mode) - add default initialisers for numeric fields in methods Converted some more python tests to use final 0-10 client. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634368 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 9 - qpid/python/tests_0-10/broker.py | 52 ++-- qpid/python/tests_0-10/exchange.py | 38 +-- qpid/python/tests_0-10/message.py | 572 ++++++++++++++++++++----------------- qpid/python/tests_0-10/queue.py | 174 ++++++----- 5 files changed, 426 insertions(+), 419 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index e1f6a60f3a..5329c0f6e2 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -46,9 +46,7 @@ tests_0-10.dtx.DtxTests.test_start_join tests_0-10.dtx.DtxTests.test_start_join_and_resume tests_0-10.dtx.DtxTests.test_suspend_resume tests_0-10.dtx.DtxTests.test_suspend_start_end_resume -tests_0-10.message.MessageTests.test_ack tests_0-10.message.MessageTests.test_acquire -tests_0-10.message.MessageTests.test_cancel tests_0-10.message.MessageTests.test_consume_exclusive tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward @@ -62,10 +60,6 @@ tests_0-10.message.MessageTests.test_qos_prefetch_size tests_0-10.message.MessageTests.test_ranged_ack tests_0-10.message.MessageTests.test_recover tests_0-10.message.MessageTests.test_recover_requeue -tests_0-10.message.MessageTests.test_reject -tests_0-10.message.MessageTests.test_release -tests_0-10.message.MessageTests.test_release_ordering -tests_0-10.message.MessageTests.test_release_unacquired tests_0-10.message.MessageTests.test_subscribe_not_acquired tests_0-10.message.MessageTests.test_subscribe_not_acquired_2 tests_0-10.message.MessageTests.test_subscribe_not_acquired_3 @@ -86,7 +80,4 @@ tests_0-10.queue.QueueTests.test_unbind_direct tests_0-10.queue.QueueTests.test_unbind_fanout tests_0-10.queue.QueueTests.test_unbind_headers tests_0-10.queue.QueueTests.test_unbind_topic -tests_0-10.broker.BrokerTests.test_closed_channel -tests_0-10.broker.BrokerTests.test_ack_and_no_ack -tests_0-10.broker.BrokerTests.test_invalid_channel tests_0-10.example.ExampleTest.test_example diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 100288bbbb..25cf1241ec 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -18,9 +18,8 @@ # from qpid.client import Closed from qpid.queue import Empty -from qpid.content import Content from qpid.testlib import TestBase010 -from qpid.datatypes import Message +from qpid.datatypes import Message, RangedSet class BrokerTests(TestBase010): """Tests for basic Broker functionality""" @@ -31,28 +30,30 @@ class BrokerTests(TestBase010): consumer. Second, this test tries to explicitly receive and acknowledge a message with an acknowledging consumer. """ - ch = self.channel - self.queue_declare(ch, queue = "myqueue") + session = self.session + session.queue_declare(queue = "myqueue", exclusive=True, auto_delete=True) # No ack consumer ctag = "tag1" - self.subscribe(ch, queue = "myqueue", destination = ctag) + session.message_subscribe(queue = "myqueue", destination = ctag) + session.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) body = "test no-ack" - ch.message_transfer(content = Content(body, properties = {"routing_key" : "myqueue"})) - msg = self.client.queue(ctag).get(timeout = 5) - self.assert_(msg.content.body == body) + session.message_transfer(message=Message(session.delivery_properties(routing_key="myqueue"), body)) + msg = session.incoming(ctag).get(timeout = 5) + self.assert_(msg.body == body) # Acknowledging consumer - self.queue_declare(ch, queue = "otherqueue") + session.queue_declare(queue = "otherqueue", exclusive=True, auto_delete=True) ctag = "tag2" - self.subscribe(ch, queue = "otherqueue", destination = ctag, confirm_mode = 1) - ch.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) - ch.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) + session.message_subscribe(queue = "otherqueue", destination = ctag, accept_mode = 1) + session.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) body = "test ack" - ch.message_transfer(content = Content(body, properties = {"routing_key" : "otherqueue"})) - msg = self.client.queue(ctag).get(timeout = 5) - msg.complete() - self.assert_(msg.content.body == body) + session.message_transfer(message=Message(session.delivery_properties(routing_key="otherqueue"), body)) + msg = session.incoming(ctag).get(timeout = 5) + session.message_accept(RangedSet(msg.id)) + self.assert_(msg.body == body) def test_simple_delivery_immediate(self): """ @@ -90,22 +91,3 @@ class BrokerTests(TestBase010): queue = session.incoming(consumer_tag) msg = queue.get(timeout=5) self.assert_(msg.body == body) - - def test_invalid_channel(self): - channel = self.client.channel(200) - try: - channel.queue_declare(exclusive=True) - self.fail("Expected error on queue_declare for invalid channel") - except Closed, e: - self.assertConnectionException(504, e.args[0]) - - def test_closed_channel(self): - channel = self.client.channel(200) - channel.session_open() - channel.session_close() - try: - channel.queue_declare(exclusive=True) - self.fail("Expected error on queue_declare for closed channel") - except Closed, e: - if isinstance(e.args[0], str): self.fail(e) - self.assertConnectionException(504, e.args[0]) diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 86c39b7736..afa7f05a49 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -37,7 +37,7 @@ class StandardExchangeVerifier: def verifyDirectExchange(self, ex): """Verify that ex behaves like a direct exchange.""" self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") + self.session.exchange_bind(queue="q", exchange=ex, routing_key="k") self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") try: self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") @@ -47,34 +47,34 @@ class StandardExchangeVerifier: def verifyFanOutExchange(self, ex): """Verify that ex behaves like a fanout exchange.""" self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex) + self.session.exchange_bind(queue="q", exchange=ex) self.queue_declare(queue="p") - self.channel.queue_bind(queue="p", exchange=ex) + self.session.exchange_bind(queue="p", exchange=ex) for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) def verifyTopicExchange(self, ex): """Verify that ex behaves like a topic exchange""" self.queue_declare(queue="a") - self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + self.session.exchange_bind(queue="a", exchange=ex, routing_key="a.#.b.*") q = self.consume("a") self.assertPublishGet(q, ex, "a.b.x") self.assertPublishGet(q, ex, "a.x.b.x") self.assertPublishGet(q, ex, "a.x.x.b.x") # Shouldn't match - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b.x.y"})) - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"x.a.b.x"})) - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) + self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) + self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b.x.y"})) + self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"x.a.b.x"})) + self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) self.assert_(q.empty()) def verifyHeadersExchange(self, ex): """Verify that ex is a headers exchange""" self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) + self.session.exchange_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) q = self.consume("q") headers = {"name":"fred", "age":3} self.assertPublishGet(q, exchange=ex, properties=headers) - self.channel.message_transfer(destination=ex) # No headers, won't deliver + self.session.message_transfer(destination=ex) # No headers, won't deliver self.assertEmpty(q); @@ -215,7 +215,7 @@ class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): """ def test(self): try: - self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) + self.session.exchange_declare(exchange="humpty_dumpty", passive=True) self.fail("Expected 404 for passive declaration of unknown exchange.") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -275,10 +275,10 @@ class HeadersExchangeTests(TestBase): self.assertPublishGet(self.q, exchange="amq.match", properties=headers) def myBasicPublish(self, headers): - self.channel.message_transfer(destination="amq.match", content=Content("foobar", properties={'application_headers':headers})) + self.session.message_transfer(destination="amq.match", content=Content("foobar", properties={'application_headers':headers})) def testMatchAll(self): - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) + self.session.exchange_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) self.myAssertPublishGet({"name":"fred", "age":3}) self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) @@ -290,7 +290,7 @@ class HeadersExchangeTests(TestBase): self.assertEmpty(self.q) def testMatchAny(self): - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) + self.session.exchange_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) self.myAssertPublishGet({"name":"fred"}) self.myAssertPublishGet({"name":"fred", "ignoreme":10}) self.myAssertPublishGet({"ignoreme":10, "age":3}) @@ -307,15 +307,15 @@ class MiscellaneousErrorsTests(TestBase): """ def testTypeNotKnown(self): try: - self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") + self.session.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") self.fail("Expected 503 for declaration of unknown exchange type.") except Closed, e: self.assertConnectionException(503, e.args[0]) def testDifferentDeclaredType(self): - self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + self.session.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") try: - self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + self.session.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") self.fail("Expected 530 for redeclaration of exchange with different type.") except Closed, e: self.assertConnectionException(530, e.args[0]) @@ -327,9 +327,9 @@ class MiscellaneousErrorsTests(TestBase): class ExchangeTests(TestBase): def testHeadersBindNoMatchArg(self): - self.channel.queue_declare(queue="q", exclusive=True, auto_delete=True) + self.session.queue_declare(queue="q", exclusive=True, auto_delete=True) try: - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={"name":"fred" , "age":3} ) + self.session.exchange_bind(queue="q", exchange="amq.match", arguments={"name":"fred" , "age":3} ) self.fail("Expected failure for missing x-match arg.") except Closed, e: self.assertConnectionException(541, e.args[0]) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index a3d32bdb2d..5f97d6c705 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -19,33 +19,34 @@ from qpid.client import Client, Closed from qpid.queue import Empty from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from qpid.reference import Reference, ReferenceId -class MessageTests(TestBase): +from qpid.testlib import TestBase010 +from qpid.datatypes import Message, RangedSet + +class MessageTests(TestBase010): """Tests for 'methods' on the amqp message 'class'""" def test_consume_no_local(self): """ Test that the no_local flag is honoured in the consume method """ - channel = self.channel + session = self.session #setup, declare two queues: - channel.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) - channel.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True) #establish two consumers one of which excludes delivery of locally sent messages self.subscribe(destination="local_included", queue="test-queue-1a") self.subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) #send a message - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1b"}, body="consume_no_local")) + session.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) + session.message_transfer(content=Content(properties={'routing_key' : "test-queue-1b"}, body="consume_no_local")) #check the queues of the two consumers - excluded = self.client.queue("local_excluded") - included = self.client.queue("local_included") + excluded = session.incoming("local_excluded") + included = session.incoming("local_included") msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.content.body) + self.assertEqual("consume_no_local", msg.body) try: excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") @@ -62,42 +63,42 @@ class MessageTests(TestBase): deletes such messages. """ - channel = self.channel + session = self.session #setup: - channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) #establish consumer which excludes delivery of locally sent messages self.subscribe(destination="local_excluded", queue="test-queue", no_local=True) #send a 'local' message - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="local")) + session.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="local")) #send a non local message other = self.connect() - channel2 = other.channel(1) - channel2.session_open() - channel2.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="foreign")) - channel2.session_close() + session2 = other.session(1) + session2.session_open() + session2.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="foreign")) + session2.session_close() other.close() #check that the second message only is delivered - excluded = self.client.queue("local_excluded") + excluded = session.incoming("local_excluded") msg = excluded.get(timeout=1) - self.assertEqual("foreign", msg.content.body) + self.assertEqual("foreign", msg.body) try: excluded.get(timeout=1) self.fail("Received extra message") except Empty: None #check queue is empty - self.assertEqual(0, channel.queue_query(queue="test-queue").message_count) + self.assertEqual(0, session.queue_query(queue="test-queue").message_count) def test_consume_exclusive(self): """ Test that the exclusive flag is honoured in the consume method """ - channel = self.channel + session = self.session #setup, declare a queue: - channel.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) #check that an exclusive consumer prevents other consumer being created: self.subscribe(destination="first", queue="test-queue-2", exclusive=True) @@ -107,12 +108,12 @@ class MessageTests(TestBase): except Closed, e: self.assertChannelException(403, e.args[0]) - #open new channel and cleanup last consumer: - channel = self.client.channel(2) - channel.session_open() + #open new session and cleanup last consumer: + session = self.client.session(2) + session.session_open() #check that an exclusive consumer cannot be created if a consumer already exists: - self.subscribe(channel, destination="first", queue="test-queue-2") + self.subscribe(session, destination="first", queue="test-queue-2") try: self.subscribe(destination="second", queue="test-queue-2", exclusive=True) self.fail("Expected exclusive consume request to fail due to previous consumer") @@ -123,7 +124,7 @@ class MessageTests(TestBase): """ Test error conditions associated with the queue field of the consume method: """ - channel = self.channel + session = self.session try: #queue specified but doesn't exist: self.subscribe(queue="invalid-queue", destination="") @@ -131,11 +132,11 @@ class MessageTests(TestBase): except Closed, e: self.assertChannelException(404, e.args[0]) - channel = self.client.channel(2) - channel.session_open() + session = self.client.session(2) + session.session_open() try: #queue not specified and none previously declared for channel: - self.subscribe(channel, queue="", destination="") + self.subscribe(session, queue="", destination="") self.fail("Expected failure when consuming from unspecified queue") except Closed, e: self.assertConnectionException(530, e.args[0]) @@ -144,9 +145,9 @@ class MessageTests(TestBase): """ Ensure unique consumer tags are enforced """ - channel = self.channel + session = self.session #setup, declare a queue: - channel.queue_declare(queue="test-queue-3", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue-3", exclusive=True, auto_delete=True) #check that attempts to use duplicate tags are detected and prevented: self.subscribe(destination="first", queue="test-queue-3") @@ -160,43 +161,48 @@ class MessageTests(TestBase): """ Test compliance of the basic.cancel method """ - channel = self.channel + session = self.session #setup, declare a queue: - channel.queue_declare(queue="test-queue-4", exclusive=True, auto_delete=True) - self.subscribe(destination="my-consumer", queue="test-queue-4") - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="One")) + session.queue_declare(queue="test-queue-4", exclusive=True, auto_delete=True) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-4"), "One")) + + session.message_subscribe(destination="my-consumer", queue="test-queue-4") + session.message_flow(destination="my-consumer", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="my-consumer", unit=1, value=0xFFFFFFFF) + + #should flush here #cancel should stop messages being delivered - channel.message_cancel(destination="my-consumer") - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="Two")) - myqueue = self.client.queue("my-consumer") + session.message_cancel(destination="my-consumer") + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-4"), "Two")) + myqueue = session.incoming("my-consumer") msg = myqueue.get(timeout=1) - self.assertEqual("One", msg.content.body) + self.assertEqual("One", msg.body) try: msg = myqueue.get(timeout=1) self.fail("Got message after cancellation: " + msg) except Empty: None #cancellation of non-existant consumers should be handled without error - channel.message_cancel(destination="my-consumer") - channel.message_cancel(destination="this-never-existed") + session.message_cancel(destination="my-consumer") + session.message_cancel(destination="this-never-existed") def test_ack(self): """ Test basic ack/recover behaviour """ - channel = self.channel - channel.queue_declare(queue="test-ack-queue", exclusive=True, auto_delete=True) + session = self.conn.session("alternate-session", timeout=10) + session.queue_declare(queue="test-ack-queue", auto_delete=True) - self.subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") + session.message_subscribe(queue = "test-ack-queue", destination = "consumer") + session.message_flow(destination="consumer", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=1, value=0xFFFFFFFF) + queue = session.incoming("consumer") - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="One")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Two")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Three")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Four")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Five")) + delivery_properties = session.delivery_properties(routing_key="test-ack-queue") + for i in ["One", "Two", "Three", "Four", "Five"]: + session.message_transfer(message=Message(delivery_properties, i)) msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) @@ -204,26 +210,36 @@ class MessageTests(TestBase): msg4 = queue.get(timeout=1) msg5 = queue.get(timeout=1) - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) + self.assertEqual("One", msg1.body) + self.assertEqual("Two", msg2.body) + self.assertEqual("Three", msg3.body) + self.assertEqual("Four", msg4.body) + self.assertEqual("Five", msg5.body) + + session.message_accept(RangedSet(msg1.id, msg2.id, msg4.id))#One, Two and Four + + #subscribe from second session here to ensure queue is not + #auto-deleted when alternate session closes (no need to ack on these): + self.session.message_subscribe(queue = "test-ack-queue", destination = "checker", accept_mode=1) - msg2.complete(cumulative=True)#One and Two - msg4.complete(cumulative=False) + #now close the session, and see that the unacked messages are + #then redelivered to another subscriber: + session.close(timeout=10) - channel.message_recover(requeue=False) + session = self.session + session.message_flow(destination="checker", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=1, value=0xFFFFFFFF) + queue = session.incoming("checker") msg3b = queue.get(timeout=1) msg5b = queue.get(timeout=1) - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) + self.assertEqual("Three", msg3b.body) + self.assertEqual("Five", msg5b.body) try: extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None @@ -231,27 +247,27 @@ class MessageTests(TestBase): """ Test recover behaviour """ - channel = self.channel - channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.fanout", queue="queue-a") - channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.fanout", queue="queue-b") + session = self.session + session.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) + session.queue_bind(exchange="amq.fanout", queue="queue-a") + session.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) + session.queue_bind(exchange="amq.fanout", queue="queue-b") self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) self.subscribe(queue="queue-b", destination="confirmed", confirm_mode=0) - confirmed = self.client.queue("confirmed") - unconfirmed = self.client.queue("unconfirmed") + confirmed = session.incoming("confirmed") + unconfirmed = session.incoming("unconfirmed") data = ["One", "Two", "Three", "Four", "Five"] for d in data: - channel.message_transfer(destination="amq.fanout", content=Content(body=d)) + session.message_transfer(destination="amq.fanout", content=Content(body=d)) for q in [confirmed, unconfirmed]: for d in data: self.assertEqual(d, q.get(timeout=1).content.body) self.assertEmpty(q) - channel.message_recover(requeue=False) + session.message_recover(requeue=False) self.assertEmpty(confirmed) @@ -259,29 +275,29 @@ class MessageTests(TestBase): msg = None for d in data: msg = unconfirmed.get(timeout=1) - self.assertEqual(d, msg.content.body) + self.assertEqual(d, msg.body) self.assertEqual(True, msg.content['redelivered']) self.assertEmpty(unconfirmed) - data.remove(msg.content.body) + data.remove(msg.body) msg.complete(cumulative=False) - channel.message_recover(requeue=False) + session.message_recover(requeue=False) def test_recover_requeue(self): """ Test requeing on recovery """ - channel = self.channel - channel.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") + queue = session.incoming("consumer_tag") - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Two")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) + session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) + session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Two")) + session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) + session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) + session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) msg1 = queue.get(timeout=1) msg2 = queue.get(timeout=1) @@ -298,15 +314,15 @@ class MessageTests(TestBase): msg2.complete(cumulative=True) #One and Two msg4.complete(cumulative=False) #Four - channel.message_cancel(destination="consumer_tag") + session.message_cancel(destination="consumer_tag") #publish a new message - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Six")) + session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Six")) #requeue unacked messages (Three and Five) - channel.message_recover(requeue=True) + session.message_recover(requeue=True) self.subscribe(queue="test-requeue", destination="consumer_tag") - queue2 = self.client.queue("consumer_tag") + queue2 = session.incoming("consumer_tag") msg3b = queue2.get(timeout=1) msg5b = queue2.get(timeout=1) @@ -334,22 +350,22 @@ class MessageTests(TestBase): Test that the prefetch count specified is honoured """ #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-count", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue="test-prefetch-count", exclusive=True, auto_delete=True) subscription = self.subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") + queue = session.incoming("consumer_tag") #set prefetch to 5: - channel.message_qos(prefetch_count=5) + session.message_qos(prefetch_count=5) #publish 10 messages: for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-count"}, body="Message %d" % i)) + session.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-count"}, body="Message %d" % i)) #only 5 messages should have been delivered: for i in range(1, 6): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + self.assertEqual("Message %d" % i, msg.body) try: extra = queue.get(timeout=1) self.fail("Got unexpected 6th message in original queue: " + extra.content.body) @@ -360,7 +376,7 @@ class MessageTests(TestBase): for i in range(6, 11): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + self.assertEqual("Message %d" % i, msg.body) msg.complete() @@ -376,22 +392,22 @@ class MessageTests(TestBase): Test that the prefetch size specified is honoured """ #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-size", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue="test-prefetch-size", exclusive=True, auto_delete=True) subscription = self.subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") + queue = session.incoming("consumer_tag") #set prefetch to 50 bytes (each message is 9 or 10 bytes): - channel.message_qos(prefetch_size=50) + session.message_qos(prefetch_size=50) #publish 10 messages: for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body="Message %d" % i)) + session.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body="Message %d" % i)) #only 5 messages should have been delivered (i.e. 45 bytes worth): for i in range(1, 6): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + self.assertEqual("Message %d" % i, msg.body) try: extra = queue.get(timeout=1) @@ -403,7 +419,7 @@ class MessageTests(TestBase): for i in range(6, 11): msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + self.assertEqual("Message %d" % i, msg.body) msg.complete() @@ -415,54 +431,58 @@ class MessageTests(TestBase): #make sure that a single oversized message still gets delivered large = "abcdefghijklmnopqrstuvwxyz" large = large + "-" + large; - channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body=large)) + session.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body=large)) msg = queue.get(timeout=1) - self.assertEqual(large, msg.content.body) + self.assertEqual(large, msg.body) def test_reject(self): - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True, alternate_exchange="amq.fanout") - channel.queue_declare(queue = "r", exclusive=True, auto_delete=True) - channel.queue_bind(queue = "r", exchange = "amq.fanout") - - self.subscribe(queue = "q", destination = "consumer", confirm_mode = 1) - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body="blah, blah")) - msg = self.client.queue("consumer").get(timeout = 1) - self.assertEquals(msg.content.body, "blah, blah") - channel.message_reject([msg.command_id, msg.command_id]) - - self.subscribe(queue = "r", destination = "checker") - msg = self.client.queue("checker").get(timeout = 1) - self.assertEquals(msg.content.body, "blah, blah") + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True, alternate_exchange="amq.fanout") + session.queue_declare(queue = "r", exclusive=True, auto_delete=True) + session.exchange_bind(queue = "r", exchange = "amq.fanout") + + session.message_subscribe(queue = "q", destination = "consumer") + session.message_flow(destination="consumer", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=1, value=0xFFFFFFFF) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "blah, blah")) + msg = session.incoming("consumer").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") + session.message_reject(RangedSet(msg.id)) + + session.message_subscribe(queue = "r", destination = "checker") + session.message_flow(destination="checker", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=1, value=0xFFFFFFFF) + msg = session.incoming("checker").get(timeout = 1) + self.assertEquals(msg.body, "blah, blah") def test_credit_flow_messages(self): """ Test basic credit based flow control with unit = message """ #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c") - channel.message_flow_mode(mode = 0, destination = "c") + session.message_subscribe(queue = "q", destination = "c") + session.message_flow_mode(mode = 0, destination = "c") #send batch of messages to queue for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 0, value = 5, destination = "c") + session.message_flow(unit = 0, value = 5, destination = "c") #set infinite byte credit - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") #check that expected number were received - q = self.client.queue("c") + q = session.incoming("c") for i in range(1, 6): - self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertDataEquals(session, q.get(timeout = 1), "Message %d" % i) self.assertEmpty(q) #increase credit again and check more are received for i in range(6, 11): - channel.message_flow(unit = 0, value = 1, destination = "c") - self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + session.message_flow(unit = 0, value = 1, destination = "c") + self.assertDataEquals(session, q.get(timeout = 1), "Message %d" % i) self.assertEmpty(q) def test_credit_flow_bytes(self): @@ -470,32 +490,32 @@ class MessageTests(TestBase): Test basic credit based flow control with unit = bytes """ #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c") - channel.message_flow_mode(mode = 0, destination = "c") + session.message_subscribe(queue = "q", destination = "c") + session.message_flow_mode(mode = 0, destination = "c") #send batch of messages to queue for i in range(10): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit msg_size = 35 #set byte credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 1, value = msg_size*5, destination = "c") + session.message_flow(unit = 1, value = msg_size*5, destination = "c") #set infinite message credit - channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") #check that expected number were received - q = self.client.queue("c") + q = session.incoming("c") for i in range(5): - self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertDataEquals(session, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) #increase credit again and check more are received for i in range(5): - channel.message_flow(unit = 1, value = msg_size, destination = "c") - self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + session.message_flow(unit = 1, value = msg_size, destination = "c") + self.assertDataEquals(session, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) @@ -504,30 +524,30 @@ class MessageTests(TestBase): Test basic window based flow control with unit = message """ #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) - channel.message_flow_mode(mode = 1, destination = "c") + session.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) + session.message_flow_mode(mode = 1, destination = "c") #send batch of messages to queue for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 0, value = 5, destination = "c") + session.message_flow(unit = 0, value = 5, destination = "c") #set infinite byte credit - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") #check that expected number were received - q = self.client.queue("c") + q = session.incoming("c") for i in range(1, 6): msg = q.get(timeout = 1) - self.assertDataEquals(channel, msg, "Message %d" % i) + self.assertDataEquals(session, msg, "Message %d" % i) self.assertEmpty(q) #acknowledge messages and check more are received msg.complete(cumulative=True) for i in range(6, 11): - self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) + self.assertDataEquals(session, q.get(timeout = 1), "Message %d" % i) self.assertEmpty(q) @@ -536,291 +556,307 @@ class MessageTests(TestBase): Test basic window based flow control with unit = bytes """ #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) - channel.message_flow_mode(mode = 1, destination = "c") + session.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) + session.message_flow_mode(mode = 1, destination = "c") #send batch of messages to queue for i in range(10): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit msg_size = 40 #set byte credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 1, value = msg_size*5, destination = "c") + session.message_flow(unit = 1, value = msg_size*5, destination = "c") #set infinite message credit - channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") #check that expected number were received - q = self.client.queue("c") + q = session.incoming("c") msgs = [] for i in range(5): msg = q.get(timeout = 1) msgs.append(msg) - self.assertDataEquals(channel, msg, "abcdefgh") + self.assertDataEquals(session, msg, "abcdefgh") self.assertEmpty(q) #ack each message individually and check more are received for i in range(5): msg = msgs.pop() msg.complete(cumulative=False) - self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") + self.assertDataEquals(session, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) def test_subscribe_not_acquired(self): """ Test the not-acquired modes works as expected for a simple case """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 6): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) self.subscribe(queue = "q", destination = "a", acquire_mode = 1) self.subscribe(queue = "q", destination = "b", acquire_mode = 1) for i in range(6, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) #both subscribers should see all messages - qA = self.client.queue("a") - qB = self.client.queue("b") + qA = session.incoming("a") + qB = session.incoming("b") for i in range(1, 11): for q in [qA, qB]: msg = q.get(timeout = 1) - self.assertEquals("Message %s" % i, msg.content.body) + self.assertEquals("Message %s" % i, msg.body) msg.complete() #messages should still be on the queue: - self.assertEquals(10, channel.queue_query(queue = "q").message_count) + self.assertEquals(10, session.queue_query(queue = "q").message_count) def test_acquire(self): """ Test explicit acquire function """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) + + #use fanout for now: + session.exchange_bind(exchange="amq.fanout", queue="q") + session.message_transfer(destination="amq.fanout", message=Message("acquire me")) + #session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) - self.subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) - msg = self.client.queue("a").get(timeout = 1) + session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) + session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=1, value=0xFFFFFFFF) + msg = session.incoming("a").get(timeout = 1) + self.assertEquals("acquire me", msg.body) #message should still be on the queue: - self.assertEquals(1, channel.queue_query(queue = "q").message_count) + self.assertEquals(1, session.queue_query(queue = "q").message_count) - channel.message_acquire([msg.command_id, msg.command_id]) + response = session.message_acquire(RangedSet(msg.id)) #check that we get notification (i.e. message_acquired) - response = channel.control_queue.get(timeout=1) self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) #message should have been removed from the queue: - self.assertEquals(0, channel.queue_query(queue = "q").message_count) - msg.complete() - - + self.assertEquals(0, session.queue_query(queue = "q").message_count) + session.message_accept(RangedSet(msg.id)) def test_release(self): """ Test explicit release function """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "release me")) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) - self.subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) - msg = self.client.queue("a").get(timeout = 1) - channel.message_cancel(destination = "a") - channel.message_release([msg.command_id, msg.command_id]) - msg.complete() + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "release me")) + + session.message_subscribe(queue = "q", destination = "a") + session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=1, value=0xFFFFFFFF) + msg = session.incoming("a").get(timeout = 1) + self.assertEquals("release me", msg.body) + session.message_cancel(destination = "a") + session.message_release(RangedSet(msg.id)) #message should not have been removed from the queue: - self.assertEquals(1, channel.queue_query(queue = "q").message_count) + self.assertEquals(1, session.queue_query(queue = "q").message_count) def test_release_ordering(self): """ Test order of released messages is as expected """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range (1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "released message %s" % (i))) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "released message %s" % (i))) - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - channel.message_flow(unit = 0, value = 10, destination = "a") - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - queue = self.client.queue("a") + session.message_subscribe(queue = "q", destination = "a") + session.message_flow(unit = 0, value = 10, destination = "a") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + queue = session.incoming("a") first = queue.get(timeout = 1) - for i in range (2, 10): - self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + for i in range(2, 10): + msg = queue.get(timeout = 1) + self.assertEquals("released message %s" % (i), msg.body) + last = queue.get(timeout = 1) self.assertEmpty(queue) - channel.message_release([first.command_id, last.command_id]) - last.complete()#will re-allocate credit, as in window mode - for i in range (1, 11): - self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) + released = RangedSet() + released.add(first.id, last.id) + session.message_release(released) + + #TODO: may want to clean this up... + session.receiver._completed.add(first.id, last.id) + session.channel.session_completed(session.receiver._completed) + + for i in range(1, 11): + self.assertEquals("released message %s" % (i), queue.get(timeout = 1).body) def test_ranged_ack(self): """ Test acking of messages ranges """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) + delivery_properties = session.delivery_properties(routing_key="q") for i in range (1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message %s" % (i))) + session.message_transfer(message=Message(delivery_properties, "message %s" % (i))) - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - channel.message_flow(unit = 0, value = 10, destination = "a") - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - queue = self.client.queue("a") + session.message_subscribe(queue = "q", destination = "a") + session.message_flow(unit = 0, value = 10, destination = "a") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + queue = session.incoming("a") for i in range (1, 11): - self.assertEquals("message %s" % (i), queue.get(timeout = 1).content.body) + self.assertEquals("message %s" % (i), queue.get(timeout = 1).body) self.assertEmpty(queue) #ack all but the third message (command id 2) - channel.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=[0,1,3,6,7,7,8,9]) - channel.message_recover() + session.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=[0,1,3,6,7,7,8,9]) + session.message_recover() self.assertEquals("message 3", queue.get(timeout = 1).content.body) self.assertEmpty(queue) def test_subscribe_not_acquired_2(self): - channel = self.channel + session = self.session #publish some messages self.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) #consume some of them - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - channel.message_flow_mode(mode = 0, destination = "a") - channel.message_flow(unit = 0, value = 5, destination = "a") - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + session.message_flow_mode(mode = 0, destination = "a") + session.message_flow(unit = 0, value = 5, destination = "a") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - queue = self.client.queue("a") + queue = session.incoming("a") for i in range(1, 6): msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) + self.assertEquals("message-%d" % (i), msg.body) msg.complete() self.assertEmpty(queue) #now create a not-acquired subscriber - channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + session.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") #check it gets those not consumed - queue = self.client.queue("b") - channel.message_flow(unit = 0, value = 1, destination = "b") + queue = session.incoming("b") + session.message_flow(unit = 0, value = 1, destination = "b") for i in range(6, 11): msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) + self.assertEquals("message-%d" % (i), msg.body) msg.complete() - channel.message_flow(unit = 0, value = 1, destination = "b") + session.message_flow(unit = 0, value = 1, destination = "b") self.assertEmpty(queue) #check all 'browsed' messages are still on the queue - self.assertEqual(5, channel.queue_query(queue="q").message_count) + self.assertEqual(5, session.queue_query(queue="q").message_count) def test_subscribe_not_acquired_3(self): - channel = self.channel + session = self.session #publish some messages self.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) #create a not-acquired subscriber - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - channel.message_flow(unit = 0, value = 10, destination = "a") + session.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = 0, value = 10, destination = "a") #browse through messages - queue = self.client.queue("a") + queue = session.incoming("a") for i in range(1, 11): msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) + self.assertEquals("message-%d" % (i), msg.body) if (i % 2): #try to acquire every second message - channel.message_acquire([msg.command_id, msg.command_id]) + session.message_acquire([msg.command_id, msg.command_id]) #check that acquire succeeds - response = channel.control_queue.get(timeout=1) + response = session.control_queue.get(timeout=1) self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) msg.complete() self.assertEmpty(queue) #create a second not-acquired subscriber - channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - channel.message_flow(unit = 0, value = 1, destination = "b") + session.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = 0, value = 1, destination = "b") #check it gets those not consumed - queue = self.client.queue("b") + queue = session.incoming("b") for i in [2,4,6,8,10]: msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) + self.assertEquals("message-%d" % (i), msg.body) msg.complete() - channel.message_flow(unit = 0, value = 1, destination = "b") + session.message_flow(unit = 0, value = 1, destination = "b") self.assertEmpty(queue) #check all 'browsed' messages are still on the queue - self.assertEqual(5, channel.queue_query(queue="q").message_count) + self.assertEqual(5, session.queue_query(queue="q").message_count) def test_release_unacquired(self): - channel = self.channel + session = self.session #create queue - self.queue_declare(queue = "q", exclusive=True, auto_delete=True, durable=True) + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #send message - channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "my-message")) #create two 'browsers' - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - channel.message_flow(unit = 0, value = 10, destination = "a") - queueA = self.client.queue("a") - - channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - channel.message_flow(unit = 0, value = 10, destination = "b") - queueB = self.client.queue("b") + session.message_subscribe(queue = "q", destination = "a", acquire_mode=1) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = 0, value = 10, destination = "a") + queueA = session.incoming("a") + + session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = 0, value = 10, destination = "b") + queueB = session.incoming("b") #have each browser release the message msgA = queueA.get(timeout = 1) - channel.message_release([msgA.command_id, msgA.command_id]) + session.message_release(RangedSet(msgA.id)) msgB = queueB.get(timeout = 1) - channel.message_release([msgB.command_id, msgB.command_id]) + session.message_release(RangedSet(msgB.id)) #cancel browsers - channel.message_cancel(destination = "a") - channel.message_cancel(destination = "b") + session.message_cancel(destination = "a") + session.message_cancel(destination = "b") #create consumer - channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1, acquire_mode=0) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") - channel.message_flow(unit = 0, value = 10, destination = "c") - queueC = self.client.queue("c") + session.message_subscribe(queue = "q", destination = "c") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = 0, value = 10, destination = "c") + queueC = session.incoming("c") #consume the message then ack it msgC = queueC.get(timeout = 1) - msgC.complete() + session.message_accept(RangedSet(msgC.id)) #ensure there are no other messages self.assertEmpty(queueC) def test_no_size(self): self.queue_declare(queue = "q", exclusive=True, auto_delete=True) - ch = self.channel + ch = self.session ch.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) ch.message_subscribe(queue = "q", destination="d", confirm_mode = 0) ch.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "d") ch.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "d") - queue = self.client.queue("d") + queue = session.incoming("d") msg = queue.get(timeout = 3) - self.assertEquals("message-body", msg.content.body) + self.assertEquals("message-body", msg.body) - def assertDataEquals(self, channel, msg, expected): - self.assertEquals(expected, msg.content.body) + def assertDataEquals(self, session, msg, expected): + self.assertEquals(expected, msg.body) def assertEmpty(self, queue): try: diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 7b3590d11b..f192a2af90 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -28,66 +28,64 @@ class QueueTests(TestBase): """ Test that the purge method removes messages from the queue """ - channel = self.channel + session = self.session #setup, declare a queue and add some messages to it: - channel.exchange_declare(exchange="test-exchange", type="direct") - channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - channel.message_transfer(destination="test-exchange", content=Content("one", properties={'routing_key':"key"})) - channel.message_transfer(destination="test-exchange", content=Content("two", properties={'routing_key':"key"})) - channel.message_transfer(destination="test-exchange", content=Content("three", properties={'routing_key':"key"})) + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + session.message_transfer(content=Content("one", properties={'routing_key':"test-queue"})) + session.message_transfer(content=Content("two", properties={'routing_key':"test-queue"})) + session.message_transfer(content=Content("three", properties={'routing_key':"test-queue"})) #check that the queue now reports 3 messages: - channel.queue_declare(queue="test-queue") - reply = channel.queue_query(queue="test-queue") + session.queue_declare(queue="test-queue") + reply = session.queue_query(queue="test-queue") self.assertEqual(3, reply.message_count) #now do the purge, then test that three messages are purged and the count drops to 0 - channel.queue_purge(queue="test-queue"); - reply = channel.queue_query(queue="test-queue") + session.queue_purge(queue="test-queue"); + reply = session.queue_query(queue="test-queue") self.assertEqual(0, reply.message_count) #send a further message and consume it, ensuring that the other messages are really gone - channel.message_transfer(destination="test-exchange", content=Content("four", properties={'routing_key':"key"})) + session.message_transfer(content=Content("four", properties={'routing_key':"test-queue"})) self.subscribe(queue="test-queue", destination="tag") queue = self.client.queue("tag") msg = queue.get(timeout=1) self.assertEqual("four", msg.content.body) - #check error conditions (use new channels): - channel = self.client.channel(2) - channel.session_open() + #check error conditions (use new sessions): + session = self.client.session(2) + session.session_open() try: #queue specified but doesn't exist: - channel.queue_purge(queue="invalid-queue") + session.queue_purge(queue="invalid-queue") self.fail("Expected failure when purging non-existent queue") except Closed, e: self.assertChannelException(404, e.args[0]) - channel = self.client.channel(3) - channel.session_open() + session = self.client.session(3) + session.session_open() try: #queue not specified and none previously declared for channel: - channel.queue_purge() + session.queue_purge() self.fail("Expected failure when purging unspecified queue") except Closed, e: self.assertConnectionException(530, e.args[0]) #cleanup other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="test-exchange") + session = other.session(1) + session.session_open() + session.exchange_delete(exchange="test-exchange") def test_declare_exclusive(self): """ Test that the exclusive field is honoured in queue.declare """ - # TestBase.setUp has already opened channel(1) - c1 = self.channel + # TestBase.setUp has already opened session(1) + c1 = self.session # Here we open a second separate connection: other = self.connect() - c2 = other.channel(1) + c2 = other.session(1) c2.session_open() #declare an exclusive queue: @@ -104,13 +102,13 @@ class QueueTests(TestBase): """ Test that the passive field is honoured in queue.declare """ - channel = self.channel + session = self.session #declare an exclusive queue: - channel.queue_declare(queue="passive-queue-1", exclusive=True, auto_delete=True) - channel.queue_declare(queue="passive-queue-1", passive=True) + session.queue_declare(queue="passive-queue-1", exclusive=True, auto_delete=True) + session.queue_declare(queue="passive-queue-1", passive=True) try: #other connection should not be allowed to declare this: - channel.queue_declare(queue="passive-queue-2", passive=True) + session.queue_declare(queue="passive-queue-2", passive=True) self.fail("Expected passive declaration of non-existant queue to raise a channel exception") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -120,29 +118,29 @@ class QueueTests(TestBase): """ Test various permutations of the queue.bind method """ - channel = self.channel - channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) + session = self.session + session.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) #straightforward case, both exchange & queue exist so no errors expected: - channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + session.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") #use the queue name where the routing key is not specified: - channel.queue_bind(queue="queue-1", exchange="amq.direct") + session.queue_bind(queue="queue-1", exchange="amq.direct") #try and bind to non-existant exchange try: - channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + session.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") self.fail("Expected bind to non-existant exchange to fail") except Closed, e: self.assertChannelException(404, e.args[0]) - #need to reopen a channel: - channel = self.client.channel(2) - channel.session_open() + #need to reopen a session: + session = self.client.session(2) + session.session_open() #try and bind non-existant queue: try: - channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + session.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") self.fail("Expected bind of non-existant queue to fail") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -161,10 +159,10 @@ class QueueTests(TestBase): def unbind_test(self, exchange, routing_key="", args=None, headers={}): #bind two queues and consume from them - channel = self.channel + session = self.session - channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) - channel.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) + session.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) + session.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) self.subscribe(queue="queue-1", destination="queue-1") self.subscribe(queue="queue-2", destination="queue-2") @@ -172,18 +170,18 @@ class QueueTests(TestBase): queue1 = self.client.queue("queue-1") queue2 = self.client.queue("queue-2") - channel.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) - channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) + session.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + session.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) #send a message that will match both bindings - channel.message_transfer(destination=exchange, + session.message_transfer(destination=exchange, content=Content("one", properties={'routing_key':routing_key, 'application_headers':headers})) #unbind first queue - channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + session.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) #send another message - channel.message_transfer(destination=exchange, + session.message_transfer(destination=exchange, content=Content("two", properties={'routing_key':routing_key, 'application_headers':headers})) #check one queue has both messages and the other has only one @@ -205,26 +203,26 @@ class QueueTests(TestBase): """ Test core queue deletion behaviour """ - channel = self.channel + session = self.session #straight-forward case: - channel.queue_declare(queue="delete-me") - channel.message_transfer(content=Content("a", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("b", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("c", properties={'routing_key':"delete-me"})) - channel.queue_delete(queue="delete-me") + session.queue_declare(queue="delete-me") + session.message_transfer(content=Content("a", properties={'routing_key':"delete-me"})) + session.message_transfer(content=Content("b", properties={'routing_key':"delete-me"})) + session.message_transfer(content=Content("c", properties={'routing_key':"delete-me"})) + session.queue_delete(queue="delete-me") #check that it has gone be declaring passively try: - channel.queue_declare(queue="delete-me", passive=True) + session.queue_declare(queue="delete-me", passive=True) self.fail("Queue has not been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) #check attempted deletion of non-existant queue is handled correctly: - channel = self.client.channel(2) - channel.session_open() + session = self.client.session(2) + session.session_open() try: - channel.queue_delete(queue="i-dont-exist", if_empty=True) + session.queue_delete(queue="i-dont-exist", if_empty=True) self.fail("Expected delete of non-existant queue to fail") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -235,37 +233,37 @@ class QueueTests(TestBase): """ Test that if_empty field of queue_delete is honoured """ - channel = self.channel + session = self.session #create a queue and add a message to it (use default binding): - channel.queue_declare(queue="delete-me-2") - channel.queue_declare(queue="delete-me-2", passive=True) - channel.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) + session.queue_declare(queue="delete-me-2") + session.queue_declare(queue="delete-me-2", passive=True) + session.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) #try to delete, but only if empty: try: - channel.queue_delete(queue="delete-me-2", if_empty=True) + session.queue_delete(queue="delete-me-2", if_empty=True) self.fail("Expected delete if_empty to fail for non-empty queue") except Closed, e: self.assertChannelException(406, e.args[0]) #need new channel now: - channel = self.client.channel(2) - channel.session_open() + session = self.client.session(2) + session.session_open() #empty queue: - self.subscribe(channel, destination="consumer_tag", queue="delete-me-2") + self.subscribe(session, destination="consumer_tag", queue="delete-me-2") queue = self.client.queue("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.content.body) - channel.message_cancel(destination="consumer_tag") + session.message_cancel(destination="consumer_tag") #retry deletion on empty queue: - channel.queue_delete(queue="delete-me-2", if_empty=True) + session.queue_delete(queue="delete-me-2", if_empty=True) #check that it has gone by declaring passively: try: - channel.queue_declare(queue="delete-me-2", passive=True) + session.queue_declare(queue="delete-me-2", passive=True) self.fail("Queue has not been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -274,29 +272,29 @@ class QueueTests(TestBase): """ Test that if_unused field of queue_delete is honoured """ - channel = self.channel + session = self.channel #create a queue and register a consumer: - channel.queue_declare(queue="delete-me-3") - channel.queue_declare(queue="delete-me-3", passive=True) + session.queue_declare(queue="delete-me-3") + session.queue_declare(queue="delete-me-3", passive=True) self.subscribe(destination="consumer_tag", queue="delete-me-3") - #need new channel now: - channel2 = self.client.channel(2) - channel2.session_open() + #need new session now: + session2 = self.client.session(2) + session2.session_open() #try to delete, but only if empty: try: - channel2.queue_delete(queue="delete-me-3", if_unused=True) + session2.queue_delete(queue="delete-me-3", if_unused=True) self.fail("Expected delete if_unused to fail for queue with existing consumer") except Closed, e: self.assertChannelException(406, e.args[0]) - channel.message_cancel(destination="consumer_tag") - channel.queue_delete(queue="delete-me-3", if_unused=True) + session.message_cancel(destination="consumer_tag") + session.queue_delete(queue="delete-me-3", if_unused=True) #check that it has gone by declaring passively: try: - channel.queue_declare(queue="delete-me-3", passive=True) + session.queue_declare(queue="delete-me-3", passive=True) self.fail("Queue has not been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -306,31 +304,31 @@ class QueueTests(TestBase): """ Test auto-deletion (of non-exclusive queues) """ - channel = self.channel + session = self.session other = self.connect() - channel2 = other.channel(1) - channel2.session_open() + session2 = other.session(1) + session2.session_open() - channel.queue_declare(queue="auto-delete-me", auto_delete=True) + session.queue_declare(queue="auto-delete-me", auto_delete=True) - #consume from both channels - reply = channel.basic_consume(queue="auto-delete-me") - channel2.basic_consume(queue="auto-delete-me") + #consume from both sessions + reply = session.basic_consume(queue="auto-delete-me") + session2.basic_consume(queue="auto-delete-me") #implicit cancel - channel2.session_close() + session2.session_close() #check it is still there - channel.queue_declare(queue="auto-delete-me", passive=True) + session.queue_declare(queue="auto-delete-me", passive=True) #explicit cancel => queue is now unused again: - channel.basic_cancel(consumer_tag=reply.consumer_tag) + session.basic_cancel(consumer_tag=reply.consumer_tag) #NOTE: this assumes there is no timeout in use #check that it has gone be declaring passively try: - channel.queue_declare(queue="auto-delete-me", passive=True) + session.queue_declare(queue="auto-delete-me", passive=True) self.fail("Expected queue to have been deleted") except Closed, e: self.assertChannelException(404, e.args[0]) -- cgit v1.2.1 From 2b408e31ad0653ff8d50b12577817749d5033e43 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 7 Mar 2008 13:20:02 +0000 Subject: Altered management of delivery records to support separateion of completion (which drives flow control) and acceptance. Converted flow control python tests. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634661 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 4 --- qpid/python/tests_0-10/message.py | 60 +++++++++++++++++++++---------------- qpid/python/tests_0-10/queue.py | 62 ++++++++++++++++++--------------------- 3 files changed, 63 insertions(+), 63 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 5329c0f6e2..1b937563c6 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -52,8 +52,6 @@ tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward tests_0-10.message.MessageTests.test_consume_queue_errors tests_0-10.message.MessageTests.test_consume_unique_consumers -tests_0-10.message.MessageTests.test_credit_flow_bytes -tests_0-10.message.MessageTests.test_credit_flow_messages tests_0-10.message.MessageTests.test_no_size tests_0-10.message.MessageTests.test_qos_prefetch_count tests_0-10.message.MessageTests.test_qos_prefetch_size @@ -63,8 +61,6 @@ tests_0-10.message.MessageTests.test_recover_requeue tests_0-10.message.MessageTests.test_subscribe_not_acquired tests_0-10.message.MessageTests.test_subscribe_not_acquired_2 tests_0-10.message.MessageTests.test_subscribe_not_acquired_3 -tests_0-10.message.MessageTests.test_window_flow_bytes -tests_0-10.message.MessageTests.test_window_flow_messages tests_0-10.testlib.TestBaseTest.testAssertEmptyFail tests_0-10.testlib.TestBaseTest.testAssertEmptyPass tests_0-10.testlib.TestBaseTest.testMessageProperties diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 5f97d6c705..64e2bc44c4 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -464,10 +464,10 @@ class MessageTests(TestBase010): session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) session.message_subscribe(queue = "q", destination = "c") - session.message_flow_mode(mode = 0, destination = "c") + session.message_set_flow_mode(flow_mode = 0, destination = "c") #send batch of messages to queue for i in range(1, 11): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) session.message_flow(unit = 0, value = 5, destination = "c") @@ -494,13 +494,13 @@ class MessageTests(TestBase010): session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) session.message_subscribe(queue = "q", destination = "c") - session.message_flow_mode(mode = 0, destination = "c") + session.message_set_flow_mode(flow_mode = 0, destination = "c") #send batch of messages to queue for i in range(10): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 35 + msg_size = 21 #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = 1, value = msg_size*5, destination = "c") @@ -527,11 +527,11 @@ class MessageTests(TestBase010): session = self.session session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) - session.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) - session.message_flow_mode(mode = 1, destination = "c") + session.message_subscribe(queue = "q", destination = "c") + session.message_set_flow_mode(flow_mode = 1, destination = "c") #send batch of messages to queue for i in range(1, 11): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) session.message_flow(unit = 0, value = 5, destination = "c") @@ -539,13 +539,16 @@ class MessageTests(TestBase010): session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = session.incoming("c") - for i in range(1, 6): + for i in range(1, 6): msg = q.get(timeout = 1) + session.receiver._completed.add(msg.id)#TODO: this may be done automatically self.assertDataEquals(session, msg, "Message %d" % i) self.assertEmpty(q) #acknowledge messages and check more are received - msg.complete(cumulative=True) + #TODO: there may be a nicer way of doing this + session.channel.session_completed(session.receiver._completed) + for i in range(6, 11): self.assertDataEquals(session, q.get(timeout = 1), "Message %d" % i) self.assertEmpty(q) @@ -559,14 +562,14 @@ class MessageTests(TestBase010): session = self.session session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #create consumer (for now that defaults to infinite credit) - session.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) - session.message_flow_mode(mode = 1, destination = "c") + session.message_subscribe(queue = "q", destination = "c") + session.message_set_flow_mode(flow_mode = 1, destination = "c") #send batch of messages to queue for i in range(10): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 40 + msg_size = 19 #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = 1, value = msg_size*5, destination = "c") @@ -584,7 +587,9 @@ class MessageTests(TestBase010): #ack each message individually and check more are received for i in range(5): msg = msgs.pop() - msg.complete(cumulative=False) + #TODO: there may be a nicer way of doing this + session.receiver._completed.add(msg.id) + session.channel.session_completed(session.receiver._completed) self.assertDataEquals(session, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) @@ -595,13 +600,17 @@ class MessageTests(TestBase010): session = self.session session.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 6): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %s" % i)) - self.subscribe(queue = "q", destination = "a", acquire_mode = 1) - self.subscribe(queue = "q", destination = "b", acquire_mode = 1) + session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_subscribe(queue = "q", destination = "b", acquire_mode = 1) + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") for i in range(6, 11): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %s" % i)) #both subscribers should see all messages qA = session.incoming("a") @@ -610,8 +619,9 @@ class MessageTests(TestBase010): for q in [qA, qB]: msg = q.get(timeout = 1) self.assertEquals("Message %s" % i, msg.body) - msg.complete() + session.receiver._completed.add(msg.id) + session.channel.session_completed(session.receiver._completed) #messages should still be on the queue: self.assertEquals(10, session.queue_query(queue = "q").message_count) @@ -625,7 +635,7 @@ class MessageTests(TestBase010): #use fanout for now: session.exchange_bind(exchange="amq.fanout", queue="q") session.message_transfer(destination="amq.fanout", message=Message("acquire me")) - #session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) + #session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) @@ -724,11 +734,11 @@ class MessageTests(TestBase010): #publish some messages self.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "message-%d" % (i))) #consume some of them session.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - session.message_flow_mode(mode = 0, destination = "a") + session.message_set_flow_mode(flow_mode = 0, destination = "a") session.message_flow(unit = 0, value = 5, destination = "a") session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") @@ -762,7 +772,7 @@ class MessageTests(TestBase010): #publish some messages self.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): - session.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "message-%d" % (i))) #create a not-acquired subscriber session.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) @@ -861,7 +871,7 @@ class MessageTests(TestBase010): def assertEmpty(self, queue): try: extra = queue.get(timeout=1) - self.fail("Queue not empty, contains: " + extra.content.body) + self.fail("Queue not empty, contains: " + extra.body) except Empty: None class SizelessContent(Content): diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index f192a2af90..b972166325 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -18,10 +18,10 @@ # from qpid.client import Client, Closed from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.testlib import TestBase010 +from qpid.datatypes import Message -class QueueTests(TestBase): +class QueueTests(TestBase010): """Tests for 'methods' on the amqp queue 'class'""" def test_purge(self): @@ -31,9 +31,9 @@ class QueueTests(TestBase): session = self.session #setup, declare a queue and add some messages to it: session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) - session.message_transfer(content=Content("one", properties={'routing_key':"test-queue"})) - session.message_transfer(content=Content("two", properties={'routing_key':"test-queue"})) - session.message_transfer(content=Content("three", properties={'routing_key':"test-queue"})) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "one")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "two")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "three")) #check that the queue now reports 3 messages: session.queue_declare(queue="test-queue") @@ -46,15 +46,16 @@ class QueueTests(TestBase): self.assertEqual(0, reply.message_count) #send a further message and consume it, ensuring that the other messages are really gone - session.message_transfer(content=Content("four", properties={'routing_key':"test-queue"})) - self.subscribe(queue="test-queue", destination="tag") - queue = self.client.queue("tag") + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "four")) + session.message_subscribe(queue="test-queue", destination="tag") + session.message_flow(destination="tag", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="tag", unit=1, value=0xFFFFFFFF) + queue = session.incoming("tag") msg = queue.get(timeout=1) - self.assertEqual("four", msg.content.body) + self.assertEqual("four", msg.body) #check error conditions (use new sessions): - session = self.client.session(2) - session.session_open() + session = self.conn.session("error-checker") try: #queue specified but doesn't exist: session.queue_purge(queue="invalid-queue") @@ -62,8 +63,7 @@ class QueueTests(TestBase): except Closed, e: self.assertChannelException(404, e.args[0]) - session = self.client.session(3) - session.session_open() + session = self.conn.session("error-checker") try: #queue not specified and none previously declared for channel: session.queue_purge() @@ -71,12 +71,6 @@ class QueueTests(TestBase): except Closed, e: self.assertConnectionException(530, e.args[0]) - #cleanup - other = self.connect() - session = other.session(1) - session.session_open() - session.exchange_delete(exchange="test-exchange") - def test_declare_exclusive(self): """ Test that the exclusive field is honoured in queue.declare @@ -167,32 +161,32 @@ class QueueTests(TestBase): self.subscribe(queue="queue-1", destination="queue-1") self.subscribe(queue="queue-2", destination="queue-2") - queue1 = self.client.queue("queue-1") - queue2 = self.client.queue("queue-2") + queue1 = session.incoming("queue-1") + queue2 = session.incoming("queue-2") session.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) session.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) #send a message that will match both bindings session.message_transfer(destination=exchange, - content=Content("one", properties={'routing_key':routing_key, 'application_headers':headers})) + message=Message(session.delivery_properties(routing_key=routing_key, application_headers=headers), "one")) #unbind first queue session.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) #send another message session.message_transfer(destination=exchange, - content=Content("two", properties={'routing_key':routing_key, 'application_headers':headers})) + message=Message(session.delivery_properties(routing_key=routing_key, application_headers=headers), "two", )) #check one queue has both messages and the other has only one - self.assertEquals("one", queue1.get(timeout=1).content.body) + self.assertEquals("one", queue1.get(timeout=1).body) try: msg = queue1.get(timeout=1) - self.fail("Got extra message: %s" % msg.content.body) + self.fail("Got extra message: %s" % msg.body) except Empty: pass - self.assertEquals("one", queue2.get(timeout=1).content.body) - self.assertEquals("two", queue2.get(timeout=1).content.body) + self.assertEquals("one", queue2.get(timeout=1).body) + self.assertEquals("two", queue2.get(timeout=1).body) try: msg = queue2.get(timeout=1) self.fail("Got extra message: " + msg) @@ -207,9 +201,9 @@ class QueueTests(TestBase): #straight-forward case: session.queue_declare(queue="delete-me") - session.message_transfer(content=Content("a", properties={'routing_key':"delete-me"})) - session.message_transfer(content=Content("b", properties={'routing_key':"delete-me"})) - session.message_transfer(content=Content("c", properties={'routing_key':"delete-me"})) + session.message_transfer(message=Message("a", session.delivery_properties(routing_key="delete-me"))) + session.message_transfer(message=Message("b", session.delivery_properties(routing_key="delete-me"))) + session.message_transfer(message=Message("c", session.delivery_properties(routing_key="delete-me"))) session.queue_delete(queue="delete-me") #check that it has gone be declaring passively try: @@ -238,7 +232,7 @@ class QueueTests(TestBase): #create a queue and add a message to it (use default binding): session.queue_declare(queue="delete-me-2") session.queue_declare(queue="delete-me-2", passive=True) - session.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) + session.message_transfer(message=Message("message", session.delivery_properties(routing_key="delete-me-2"))) #try to delete, but only if empty: try: @@ -253,9 +247,9 @@ class QueueTests(TestBase): #empty queue: self.subscribe(session, destination="consumer_tag", queue="delete-me-2") - queue = self.client.queue("consumer_tag") + queue = session.incoming("consumer_tag") msg = queue.get(timeout=1) - self.assertEqual("message", msg.content.body) + self.assertEqual("message", msg.body) session.message_cancel(destination="consumer_tag") #retry deletion on empty queue: -- cgit v1.2.1 From 1326b8d12d5691ff34f697f6bcfead29166e479f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 7 Mar 2008 13:55:00 +0000 Subject: added timeouts to hello-010-world; switched to conditions rather than events for handling connection/session state; handle session exceptions git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634678 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 8 +++---- qpid/python/qpid/connection010.py | 30 +++++++++++++++----------- qpid/python/qpid/datatypes.py | 9 +++++++- qpid/python/qpid/delegates.py | 14 ++++++++----- qpid/python/qpid/queue.py | 6 ++++-- qpid/python/qpid/session.py | 44 +++++++++++++++++++++++++++++++-------- qpid/python/qpid/util.py | 27 +++++++++++++++++++++++- 7 files changed, 104 insertions(+), 34 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index ff01cf9ed7..8c98170873 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -29,13 +29,13 @@ ssn.message_transfer("is", None, None, Message(props, "more testing...")) ssn.message_transfer("a") ssn.message_transfer("test") -m1 = ssn.incoming("this").get() +m1 = ssn.incoming("this").get(timeout=10) print m1 -m2 = ssn.incoming("is").get() +m2 = ssn.incoming("is").get(timeout=10) print m2 -m3 = ssn.incoming("a").get() +m3 = ssn.incoming("a").get(timeout=10) print m3 -m4 = ssn.incoming("test").get() +m4 = ssn.incoming("test").get(timeout=10) print m4 ssn.message_accept(RangedSet(m1.id, m2.id, m3.id, m4.id)) diff --git a/qpid/python/qpid/connection010.py b/qpid/python/qpid/connection010.py index b25efd37a8..b17ceea534 100644 --- a/qpid/python/qpid/connection010.py +++ b/qpid/python/qpid/connection010.py @@ -18,7 +18,8 @@ # import datatypes, session -from threading import Thread, Event, RLock +from threading import Thread, Condition, RLock +from util import wait from framer import Closed from assembler import Assembler, Segment from codec010 import StringCodec @@ -47,16 +48,21 @@ class Connection(Assembler): Assembler.__init__(self, sock) self.spec = spec self.track = self.spec["track"] - self.delegate = delegate(self) + + self.lock = RLock() self.attached = {} self.sessions = {} - self.lock = RLock() + + self.condition = Condition() + self.opened = False + self.thread = Thread(target=self.run) self.thread.setDaemon(True) - self.opened = Event() - self.closed = Event() + self.channel_max = 65535 + self.delegate = delegate(self) + def attach(self, name, ch, delegate, force=False): self.lock.acquire() try: @@ -104,12 +110,13 @@ class Connection(Assembler): def session(self, name, timeout=None, delegate=session.client): self.lock.acquire() try: - ssn = self.attach(name, Channel(self, self.__channel()), delegate) + ch = Channel(self, self.__channel()) + ssn = self.attach(name, ch, delegate) ssn.channel.session_attach(name) - ssn.opened.wait(timeout) - if ssn.opened.isSet(): + if wait(ssn.condition, lambda: ssn.channel is not None, timeout): return ssn else: + self.detach(name, ch) raise Timeout() finally: self.lock.release() @@ -117,8 +124,7 @@ class Connection(Assembler): def start(self, timeout=None): self.delegate.start() self.thread.start() - self.opened.wait(timeout=timeout) - if not self.opened.isSet(): + if not wait(self.condition, lambda: self.opened, timeout): raise Timeout() def run(self): @@ -132,9 +138,9 @@ class Connection(Assembler): self.delegate.received(seg) def close(self, timeout=None): + if not self.opened: return Channel(self, 0).connection_close() - self.closed.wait(timeout=timeout) - if not self.closed.isSet(): + if not wait(self.condition, lambda: not self.opened, timeout): raise Timeout() self.thread.join(timeout=timeout) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 7158e0d75e..bce68aebcd 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -112,16 +112,23 @@ class RangedSet: return "RangedSet(%s)" % str(self.ranges) class Future: - def __init__(self, initial=None): + def __init__(self, initial=None, exception=Exception): self.value = initial + self._error = None self._set = threading.Event() + def error(self, error): + self._error = error + self._set.set() + def set(self, value): self.value = value self._set.set() def get(self, timeout=None): self._set.wait(timeout) + if self._error != None: + raise exception(self._error) return self.value def is_set(self): diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 4fdcc37384..83413b91ea 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -19,6 +19,7 @@ import connection010 import session +from util import notify class Delegate: @@ -49,7 +50,8 @@ class Delegate: self.connection.sock.close() def connection_close_ok(self, ch, close_ok): - self.connection.closed.set() + self.connection.opened = False + notify(self.connection.condition) def session_attach(self, ch, a): try: @@ -61,7 +63,7 @@ class Delegate: ch.session_detached(a.name) def session_attached(self, ch, a): - ch.session.opened.set() + notify(ch.session.condition) def session_detach(self, ch, d): self.connection.detach(d.name, ch) @@ -70,7 +72,7 @@ class Delegate: def session_detached(self, ch, d): ssn = self.connection.detach(d.name, ch) if ssn is not None: - ssn.closed.set() + notify(ch.session.condition) def session_command_point(self, ch, cp): ssn = ch.session @@ -91,8 +93,9 @@ class Server(Delegate): pass def connection_open(self, ch, open): - self.connection.opened.set() + self.connection.opened = True ch.connection_open_ok() + notify(self.connection.condition) class Client(Delegate): @@ -108,4 +111,5 @@ class Client(Delegate): ch.connection_open() def connection_open_ok(self, ch, open_ok): - self.connection.opened.set() + self.connection.opened = True + notify(self.connection.condition) diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index 00946a9156..ea8f00d091 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -35,10 +35,12 @@ class Queue(BaseQueue): def __init__(self, *args, **kwargs): BaseQueue.__init__(self, *args, **kwargs) + self.error = None self.listener = None self.thread = None - def close(self): + def close(self, error = None): + self.error = error self.put(Queue.END) def get(self, block = True, timeout = None): @@ -47,7 +49,7 @@ class Queue(BaseQueue): # this guarantees that any other waiting threads or any future # calls to get will also result in a Closed exception self.put(Queue.END) - raise Closed() + raise Closed(self.error) else: return result diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index c628762dac..b83bd1637f 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -17,13 +17,14 @@ # under the License. # -from threading import Event, RLock +from threading import Condition, RLock from invoker import Invoker from datatypes import RangedSet, Struct, Future from codec010 import StringCodec from assembler import Segment from queue import Queue from datatypes import Message +from util import wait from logging import getLogger class SessionDetached(Exception): pass @@ -34,6 +35,8 @@ def client(*args): def server(*args): return Server(*args) +class SessionException(Exception): pass + class Session(Invoker): def __init__(self, name, spec, sync=True, timeout=10, delegate=client): @@ -42,17 +45,22 @@ class Session(Invoker): self.sync = sync self.timeout = timeout self.channel = None - self.opened = Event() - self.closed = Event() + + self.condition = Condition() + + self.send_id = True self.receiver = Receiver(self) self.sender = Sender(self) - self.delegate = delegate(self) - self.send_id = True - self.results = {} + self.lock = RLock() self._incoming = {} + self.results = {} + self.exceptions = [] + self.assembly = None + self.delegate = delegate(self) + def incoming(self, destination): self.lock.acquire() try: @@ -66,7 +74,7 @@ class Session(Invoker): def close(self, timeout=None): self.channel.session_detach(self.name) - self.closed.wait(timeout=timeout) + wait(self.condition, lambda: self.channel is None, timeout) def resolve_method(self, name): cmd = self.spec.instructions.get(name) @@ -105,7 +113,7 @@ class Session(Invoker): type.segment_type, type.track, self.channel.id, sc.encoded) if type.result: - result = Future() + result = Future(exception=SessionException) self.results[self.sender.next_id] = result self.send(seg) @@ -234,9 +242,27 @@ class Delegate: self.session = session def execution_result(self, er): - future = self.session.results[er.command_id] + future = self.session.results.pop(er.command_id) future.set(er.value) + def execution_exception(self, ex): + self.session.lock.acquire() + try: + self.session.exceptions.append(ex) + excs = self.session.exceptions[:] + if len(excs) == 1: + error = excs[0] + else: + error = tuple(excs) + for id in self.session.results: + f = self.session.results.pop(id) + f.error(error) + + for q in self.session._incoming.values(): + q.close(error) + finally: + self.session.lock.release() + msg = getLogger("qpid.ssn.msg") class Client(Delegate): diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index e41dfc75fb..d03a9bd7e9 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -17,7 +17,7 @@ # under the License. # -import os, socket +import os, socket, time def connect(host, port): sock = socket.socket() @@ -40,3 +40,28 @@ def listen(host, port, predicate = lambda: True, bound = lambda: None): def mtime(filename): return os.stat(filename).st_mtime + +def wait(condition, predicate, timeout=None): + condition.acquire() + try: + passed = 0 + start = time.time() + while not predicate(): + if timeout is None: + condition.wait() + elif passed < timeout: + condition.wait(timeout - passed) + else: + return False + passed = time.time() - start + return True + finally: + condition.release() + +def notify(condition, action=lambda: None): + condition.acquire() + try: + action() + condition.notifyAll() + finally: + condition.release() -- cgit v1.2.1 From 791cd5a65e193ad4a1645065256a244ee238e9e4 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 7 Mar 2008 16:19:30 +0000 Subject: Converted some more tests to use new client git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634729 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 18 +---- qpid/python/tests_0-10/example.py | 44 ++++++------ qpid/python/tests_0-10/exchange.py | 135 +++++++++++++++++++++++++++++-------- qpid/python/tests_0-10/message.py | 58 ++++++++++++---- qpid/python/tests_0-10/queue.py | 39 +++++++---- 5 files changed, 201 insertions(+), 93 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 1b937563c6..3d00313d2d 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -11,20 +11,13 @@ tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_q tests_0-10.alternate_exchange.AlternateExchangeTests.test_queue_delete tests_0-10.alternate_exchange.AlternateExchangeTests.test_unroutable tests_0-10.exchange.DeclareMethodPassiveFieldNotFoundRuleTests.test -tests_0-10.exchange.DefaultExchangeRuleTests.testDefaultExchange tests_0-10.exchange.ExchangeTests.testHeadersBindNoMatchArg tests_0-10.exchange.HeadersExchangeTests.testMatchAll tests_0-10.exchange.HeadersExchangeTests.testMatchAny tests_0-10.exchange.MiscellaneousErrorsTests.testDifferentDeclaredType tests_0-10.exchange.MiscellaneousErrorsTests.testTypeNotKnown -tests_0-10.exchange.RecommendedTypesRuleTests.testDirect -tests_0-10.exchange.RecommendedTypesRuleTests.testFanout tests_0-10.exchange.RecommendedTypesRuleTests.testHeaders -tests_0-10.exchange.RecommendedTypesRuleTests.testTopic -tests_0-10.exchange.RequiredInstancesRuleTests.testAmqDirect -tests_0-10.exchange.RequiredInstancesRuleTests.testAmqFanOut tests_0-10.exchange.RequiredInstancesRuleTests.testAmqMatch -tests_0-10.exchange.RequiredInstancesRuleTests.testAmqTopic tests_0-10.dtx.DtxTests.test_bad_resume tests_0-10.dtx.DtxTests.test_end tests_0-10.dtx.DtxTests.test_end_suspend_and_fail @@ -47,6 +40,7 @@ tests_0-10.dtx.DtxTests.test_start_join_and_resume tests_0-10.dtx.DtxTests.test_suspend_resume tests_0-10.dtx.DtxTests.test_suspend_start_end_resume tests_0-10.message.MessageTests.test_acquire +tests_0-10.message.MessageTests.test_subscribe_not_acquired_3 tests_0-10.message.MessageTests.test_consume_exclusive tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward @@ -55,25 +49,17 @@ tests_0-10.message.MessageTests.test_consume_unique_consumers tests_0-10.message.MessageTests.test_no_size tests_0-10.message.MessageTests.test_qos_prefetch_count tests_0-10.message.MessageTests.test_qos_prefetch_size -tests_0-10.message.MessageTests.test_ranged_ack tests_0-10.message.MessageTests.test_recover tests_0-10.message.MessageTests.test_recover_requeue -tests_0-10.message.MessageTests.test_subscribe_not_acquired -tests_0-10.message.MessageTests.test_subscribe_not_acquired_2 -tests_0-10.message.MessageTests.test_subscribe_not_acquired_3 tests_0-10.testlib.TestBaseTest.testAssertEmptyFail tests_0-10.testlib.TestBaseTest.testAssertEmptyPass tests_0-10.testlib.TestBaseTest.testMessageProperties tests_0-10.queue.QueueTests.test_autodelete_shared -tests_0-10.queue.QueueTests.test_bind tests_0-10.queue.QueueTests.test_declare_exclusive tests_0-10.queue.QueueTests.test_declare_passive tests_0-10.queue.QueueTests.test_delete_ifempty tests_0-10.queue.QueueTests.test_delete_ifunused tests_0-10.queue.QueueTests.test_delete_simple tests_0-10.queue.QueueTests.test_purge -tests_0-10.queue.QueueTests.test_unbind_direct -tests_0-10.queue.QueueTests.test_unbind_fanout +tests_0-10.queue.QueueTests.test_bind tests_0-10.queue.QueueTests.test_unbind_headers -tests_0-10.queue.QueueTests.test_unbind_topic -tests_0-10.example.ExampleTest.test_example diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index da5ee2441f..fd10a8df4f 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -17,10 +17,10 @@ # under the License. # -from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.datatypes import Message, RangedSet +from qpid.testlib import TestBase010 -class ExampleTest (TestBase): +class ExampleTest (TestBase010): """ An example Qpid test, illustrating the unittest framework and the python Qpid client. The test class must inherit TestBase. The @@ -35,9 +35,9 @@ class ExampleTest (TestBase): """ # By inheriting TestBase, self.client is automatically connected - # and self.channel is automatically opened as channel(1) - # Other channel methods mimic the protocol. - channel = self.channel + # and self.session is automatically opened as session(1) + # Other session methods mimic the protocol. + session = self.session # Now we can send regular commands. If you want to see what the method # arguments mean or what other commands are available, you can use the @@ -57,30 +57,30 @@ class ExampleTest (TestBase): # interact with the server. # Here we use ordinal arguments. - self.exchange_declare(channel, 0, "test", "direct") + session.exchange_declare("test", "direct") # Here we use keyword arguments. - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") + session.queue_declare(session, queue="test-queue", exclusive=True, auto_delete=True) + session.exchange_bind(queue="test-queue", exchange="test", binding_key="key") - # Call Channel.basic_consume to register as a consumer. + # Call Session.subscribe to register as a consumer. # All the protocol methods return a message object. The message object # has fields corresponding to the reply method fields, plus a content # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. - channel.message_subscribe(queue="test-queue", destination="consumer_tag") - channel.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) - channel.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) + session.message_subscribe(queue="test-queue", destination="consumer_tag") + session.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) - # We can use the Client.queue(...) method to access the queue - # corresponding to our consumer_tag. - queue = self.client.queue("consumer_tag") + # We can use the session.incoming(...) method to access the messages + # delivered for our consumer_tag. + queue = session.incoming("consumer_tag") # Now lets publish a message and see if our consumer gets it. To do - # this we need to import the Content class. - sent = Content("Hello World!") - sent["routing_key"] = "key" - channel.message_transfer(destination="test", content=sent) + # this we need to import the Message class. + delivery_properties = session.delivery_properties(routing_key="key") + sent = Message(delivery_properties, "Hello World!") + session.message_transfer(destination="test", message=sent) # Now we'll wait for the message to arrive. We can use the timeout # argument in case the server hangs. By default queue.get() will wait @@ -88,8 +88,8 @@ class ExampleTest (TestBase): msg = queue.get(timeout=10) # And check that we got the right response with assertEqual - self.assertEqual(sent.body, msg.content.body) + self.assertEqual(sent.body, msg.body) # Now acknowledge the message. - msg.complete() + session.message_accept(RangedSet(msg.id)) diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index afa7f05a49..151807b045 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -23,12 +23,89 @@ Tests for exchange behaviour. Test classes ending in 'RuleTests' are derived from rules in amqp.xml. """ -import Queue, logging -from qpid.testlib import TestBase -from qpid.content import Content +import Queue, logging, traceback +from qpid.testlib import TestBase010 +from qpid.datatypes import Message from qpid.client import Closed +class TestHelper(TestBase010): + def setUp(self): + TestBase010.setUp(self) + self.queues = [] + self.exchanges = [] + + def tearDown(self): + try: + for ssn, q in self.queues: + ssn.queue_delete(queue=q) + for ssn, ex in self.exchanges: + ssn.exchange_delete(exchange=ex) + except: + print "Error on tearDown:" + print traceback.print_exc() + + def createMessage(self, key="", body=""): + return Message(self.session.delivery_properties(routing_key=key), body) + + def assertPublishGet(self, queue, exchange="", routing_key="", properties=None): + """ + Publish to exchange and assert queue.get() returns the same message. + """ + body = self.uniqueString() + dp=self.session.delivery_properties(routing_key=routing_key) + mp=self.session.message_properties(application_headers=properties) + self.session.message_transfer(destination=exchange, message=Message(dp, mp, body)) + msg = queue.get(timeout=1) + self.assertEqual(body, msg.body) + if (properties): + self.assertEqual(properties, msg.content['application_headers']) + + def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): + """ + Publish a message and consume it, assert it comes back intact. + Return the Queue object used to consume. + """ + self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) + + def assertEmpty(self, queue): + """Assert that the queue is empty""" + try: + queue.get(timeout=1) + self.fail("Queue is not empty.") + except Queue.Empty: None # Ignore + + def queue_declare(self, session=None, *args, **keys): + session = session or self.session + reply = session.queue_declare(*args, **keys) + self.queues.append((session, keys["queue"])) + return reply + + def exchange_declare(self, session=None, ticket=0, exchange='', + type='', passive=False, durable=False, + auto_delete=False, + arguments={}): + session = session or self.session + reply = session.exchange_declare(exchange=exchange, type=type, passive=passive,durable=durable, auto_delete=auto_delete, arguments=arguments) + self.exchanges.append((session,exchange)) + return reply + + def uniqueString(self): + """Generate a unique string, unique for this TestBase instance""" + if not "uniqueCounter" in dir(self): self.uniqueCounter = 1; + return "Test Message " + str(self.uniqueCounter) + + def consume(self, queueName): + """Consume from named queue returns the Queue object.""" + if not "uniqueTag" in dir(self): self.uniqueTag = 1 + else: self.uniqueTag += 1 + consumer_tag = "tag" + str(self.uniqueTag) + self.session.message_subscribe(queue=queueName, destination=consumer_tag) + self.session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) + self.session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + return self.session.incoming(consumer_tag) + + class StandardExchangeVerifier: """Verifies standard exchange behavior. @@ -37,7 +114,7 @@ class StandardExchangeVerifier: def verifyDirectExchange(self, ex): """Verify that ex behaves like a direct exchange.""" self.queue_declare(queue="q") - self.session.exchange_bind(queue="q", exchange=ex, routing_key="k") + self.session.exchange_bind(queue="q", exchange=ex, binding_key="k") self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") try: self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") @@ -55,16 +132,16 @@ class StandardExchangeVerifier: def verifyTopicExchange(self, ex): """Verify that ex behaves like a topic exchange""" self.queue_declare(queue="a") - self.session.exchange_bind(queue="a", exchange=ex, routing_key="a.#.b.*") + self.session.exchange_bind(queue="a", exchange=ex, binding_key="a.#.b.*") q = self.consume("a") self.assertPublishGet(q, ex, "a.b.x") self.assertPublishGet(q, ex, "a.x.b.x") self.assertPublishGet(q, ex, "a.x.x.b.x") # Shouldn't match - self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) - self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b.x.y"})) - self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"x.a.b.x"})) - self.session.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) + self.session.message_transfer(destination=ex, message=self.createMessage("a.b")) + self.session.message_transfer(destination=ex, message=self.createMessage("a.b.x.y")) + self.session.message_transfer(destination=ex, message=self.createMessage("x.a.b.x")) + self.session.message_transfer(destination=ex, message=self.createMessage("a.b")) self.assert_(q.empty()) def verifyHeadersExchange(self, ex): @@ -78,7 +155,7 @@ class StandardExchangeVerifier: self.assertEmpty(q); -class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): +class RecommendedTypesRuleTests(TestHelper, StandardExchangeVerifier): """ The server SHOULD implement these standard exchange types: topic, headers. @@ -106,7 +183,7 @@ class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): self.verifyHeadersExchange("h") -class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): +class RequiredInstancesRuleTests(TestHelper, StandardExchangeVerifier): """ The server MUST, in each virtual host, pre-declare an exchange instance for each standard exchange type that it implements, where the name of the @@ -124,7 +201,7 @@ class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): def testAmqMatch(self): self.verifyHeadersExchange("amq.match") -class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): +class DefaultExchangeRuleTests(TestHelper, StandardExchangeVerifier): """ The server MUST predeclare a direct exchange to act as the default exchange for content Publish methods and for default queue bindings. @@ -144,20 +221,20 @@ class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): # TODO aconway 2006-09-27: Fill in empty tests: -class DefaultAccessRuleTests(TestBase): +class DefaultAccessRuleTests(TestHelper): """ The server MUST NOT allow clients to access the default exchange except by specifying an empty exchange name in the Queue.Bind and content Publish methods. """ -class ExtensionsRuleTests(TestBase): +class ExtensionsRuleTests(TestHelper): """ The server MAY implement other exchange types as wanted. """ -class DeclareMethodMinimumRuleTests(TestBase): +class DeclareMethodMinimumRuleTests(TestHelper): """ The server SHOULD support a minimum of 16 exchanges per virtual host and ideally, impose no limit except as defined by available resources. @@ -168,7 +245,7 @@ class DeclareMethodMinimumRuleTests(TestBase): """ -class DeclareMethodTicketFieldValidityRuleTests(TestBase): +class DeclareMethodTicketFieldValidityRuleTests(TestHelper): """ The client MUST provide a valid access ticket giving "active" access to the realm in which the exchange exists or will be created, or "passive" @@ -179,7 +256,7 @@ class DeclareMethodTicketFieldValidityRuleTests(TestBase): """ -class DeclareMethodExchangeFieldReservedRuleTests(TestBase): +class DeclareMethodExchangeFieldReservedRuleTests(TestHelper): """ Exchange names starting with "amq." are reserved for predeclared and standardised exchanges. The client MUST NOT attempt to create an exchange @@ -189,7 +266,7 @@ class DeclareMethodExchangeFieldReservedRuleTests(TestBase): """ -class DeclareMethodTypeFieldTypedRuleTests(TestBase): +class DeclareMethodTypeFieldTypedRuleTests(TestHelper): """ Exchanges cannot be redeclared with different types. The client MUST not attempt to redeclare an existing exchange with a different type than used @@ -199,7 +276,7 @@ class DeclareMethodTypeFieldTypedRuleTests(TestBase): """ -class DeclareMethodTypeFieldSupportRuleTests(TestBase): +class DeclareMethodTypeFieldSupportRuleTests(TestHelper): """ The client MUST NOT attempt to create an exchange with a type that the server does not support. @@ -208,7 +285,7 @@ class DeclareMethodTypeFieldSupportRuleTests(TestBase): """ -class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): +class DeclareMethodPassiveFieldNotFoundRuleTests(TestHelper): """ If set, and the exchange does not already exist, the server MUST raise a channel exception with reply code 404 (not found). @@ -221,7 +298,7 @@ class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): self.assertChannelException(404, e.args[0]) -class DeclareMethodDurableFieldSupportRuleTests(TestBase): +class DeclareMethodDurableFieldSupportRuleTests(TestHelper): """ The server MUST support both durable and transient exchanges. @@ -229,7 +306,7 @@ class DeclareMethodDurableFieldSupportRuleTests(TestBase): """ -class DeclareMethodDurableFieldStickyRuleTests(TestBase): +class DeclareMethodDurableFieldStickyRuleTests(TestHelper): """ The server MUST ignore the durable field if the exchange already exists. @@ -237,7 +314,7 @@ class DeclareMethodDurableFieldStickyRuleTests(TestBase): """ -class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): +class DeclareMethodAutoDeleteFieldStickyRuleTests(TestHelper): """ The server MUST ignore the auto-delete field if the exchange already exists. @@ -246,7 +323,7 @@ class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): """ -class DeleteMethodTicketFieldValidityRuleTests(TestBase): +class DeleteMethodTicketFieldValidityRuleTests(TestHelper): """ The client MUST provide a valid access ticket giving "active" access rights to the exchange's access realm. @@ -256,18 +333,18 @@ class DeleteMethodTicketFieldValidityRuleTests(TestBase): """ -class DeleteMethodExchangeFieldExistsRuleTests(TestBase): +class DeleteMethodExchangeFieldExistsRuleTests(TestHelper): """ The client MUST NOT attempt to delete an exchange that does not exist. """ -class HeadersExchangeTests(TestBase): +class HeadersExchangeTests(TestHelper): """ Tests for headers exchange functionality. """ def setUp(self): - TestBase.setUp(self) + TestHelper.setUp(self) self.queue_declare(queue="q") self.q = self.consume("q") @@ -301,7 +378,7 @@ class HeadersExchangeTests(TestBase): self.assertEmpty(self.q) -class MiscellaneousErrorsTests(TestBase): +class MiscellaneousErrorsTests(TestHelper): """ Test some miscellaneous error conditions """ @@ -325,7 +402,7 @@ class MiscellaneousErrorsTests(TestBase): c2.session_open() c2.exchange_delete(exchange="test_different_declared_type_exchange") -class ExchangeTests(TestBase): +class ExchangeTests(TestHelper): def testHeadersBindNoMatchArg(self): self.session.queue_declare(queue="q", exclusive=True, auto_delete=True) try: diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 64e2bc44c4..aaefb52392 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -619,8 +619,10 @@ class MessageTests(TestBase010): for q in [qA, qB]: msg = q.get(timeout = 1) self.assertEquals("Message %s" % i, msg.body) + #TODO: tidy up completion session.receiver._completed.add(msg.id) + #TODO: tidy up completion session.channel.session_completed(session.receiver._completed) #messages should still be on the queue: self.assertEquals(10, session.queue_query(queue = "q").message_count) @@ -708,8 +710,9 @@ class MessageTests(TestBase010): """ Test acking of messages ranges """ - session = self.session - session.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session = self.conn.session("alternate-session", timeout=10) + + session.queue_declare(queue = "q", auto_delete=True) delivery_properties = session.delivery_properties(routing_key="q") for i in range (1, 11): session.message_transfer(message=Message(delivery_properties, "message %s" % (i))) @@ -718,26 +721,46 @@ class MessageTests(TestBase010): session.message_flow(unit = 0, value = 10, destination = "a") session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") queue = session.incoming("a") + ids = [] for i in range (1, 11): - self.assertEquals("message %s" % (i), queue.get(timeout = 1).body) + msg = queue.get(timeout = 1) + self.assertEquals("message %s" % (i), msg.body) + ids.append(msg.id) + self.assertEmpty(queue) - #ack all but the third message (command id 2) - session.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=[0,1,3,6,7,7,8,9]) - session.message_recover() - self.assertEquals("message 3", queue.get(timeout = 1).content.body) + #ack all but the fourth message (command id 2) + accepted = RangedSet() + accepted.add(ids[0], ids[2]) + accepted.add(ids[4], ids[9]) + session.message_accept(accepted) + + #subscribe from second session here to ensure queue is not + #auto-deleted when alternate session closes (no need to ack on these): + self.session.message_subscribe(queue = "q", destination = "checker") + + #now close the session, and see that the unacked messages are + #then redelivered to another subscriber: + session.close(timeout=10) + + session = self.session + session.message_flow(destination="checker", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=1, value=0xFFFFFFFF) + queue = session.incoming("checker") + + self.assertEquals("message 4", queue.get(timeout = 1).body) self.assertEmpty(queue) def test_subscribe_not_acquired_2(self): session = self.session #publish some messages - self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "message-%d" % (i))) #consume some of them - session.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) + session.message_subscribe(queue = "q", destination = "a") session.message_set_flow_mode(flow_mode = 0, destination = "a") session.message_flow(unit = 0, value = 5, destination = "a") session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") @@ -746,11 +769,15 @@ class MessageTests(TestBase010): for i in range(1, 6): msg = queue.get(timeout = 1) self.assertEquals("message-%d" % (i), msg.body) - msg.complete() + #complete and accept + session.message_accept(RangedSet(msg.id)) + #TODO: tidy up completion + session.receiver._completed.add(msg.id) + session.channel.session_completed(session.receiver._completed) self.assertEmpty(queue) #now create a not-acquired subscriber - session.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") #check it gets those not consumed @@ -759,7 +786,10 @@ class MessageTests(TestBase010): for i in range(6, 11): msg = queue.get(timeout = 1) self.assertEquals("message-%d" % (i), msg.body) - msg.complete() + session.message_release(RangedSet(msg.id)) + #TODO: tidy up completion + session.receiver._completed.add(msg.id) + session.channel.session_completed(session.receiver._completed) session.message_flow(unit = 0, value = 1, destination = "b") self.assertEmpty(queue) @@ -790,6 +820,10 @@ class MessageTests(TestBase010): #check that acquire succeeds response = session.control_queue.get(timeout=1) self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) + session.message_release(RangedSet(msg.id)) + session.channel._completed.add(msg.id) + session.channel.session_completed(session.channel._completed) + msg.complete() self.assertEmpty(queue) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index b972166325..38d7a3291b 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -58,7 +58,7 @@ class QueueTests(TestBase010): session = self.conn.session("error-checker") try: #queue specified but doesn't exist: - session.queue_purge(queue="invalid-queue") + session.queue_purge(queue="invalid-queue") self.fail("Expected failure when purging non-existent queue") except Closed, e: self.assertChannelException(404, e.args[0]) @@ -119,7 +119,7 @@ class QueueTests(TestBase010): session.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") #use the queue name where the routing key is not specified: - session.queue_bind(queue="queue-1", exchange="amq.direct") + session.exchange_bind(queue="queue-1", exchange="amq.direct") #try and bind to non-existant exchange try: @@ -151,32 +151,43 @@ class QueueTests(TestBase010): def test_unbind_headers(self): self.unbind_test(exchange="amq.match", args={ "x-match":"all", "a":"b"}, headers={"a":"b"}) - def unbind_test(self, exchange, routing_key="", args=None, headers={}): + def unbind_test(self, exchange, routing_key="", args=None, headers=None): #bind two queues and consume from them session = self.session session.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) session.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) - self.subscribe(queue="queue-1", destination="queue-1") - self.subscribe(queue="queue-2", destination="queue-2") + session.message_subscribe(queue="queue-1", destination="queue-1") + session.message_flow(destination="queue-1", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="queue-1", unit=1, value=0xFFFFFFFF) + session.message_subscribe(queue="queue-2", destination="queue-2") + session.message_flow(destination="queue-2", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="queue-2", unit=1, value=0xFFFFFFFF) queue1 = session.incoming("queue-1") queue2 = session.incoming("queue-2") - session.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) - session.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) - + session.exchange_bind(exchange=exchange, queue="queue-1", binding_key=routing_key, arguments=args) + session.exchange_bind(exchange=exchange, queue="queue-2", binding_key=routing_key, arguments=args) + + dp = session.delivery_properties(routing_key=routing_key) + if (headers): + mp = session.message_properties(application_headers=headers) + msg1 = Message(dp, mp, "one") + msg2 = Message(dp, mp, "two") + else: + msg1 = Message(dp, "one") + msg2 = Message(dp, "two") + #send a message that will match both bindings - session.message_transfer(destination=exchange, - message=Message(session.delivery_properties(routing_key=routing_key, application_headers=headers), "one")) + session.message_transfer(destination=exchange, message=msg1) #unbind first queue - session.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) + session.exchange_unbind(exchange=exchange, queue="queue-1", binding_key=routing_key) #send another message - session.message_transfer(destination=exchange, - message=Message(session.delivery_properties(routing_key=routing_key, application_headers=headers), "two", )) + session.message_transfer(destination=exchange, message=msg2) #check one queue has both messages and the other has only one self.assertEquals("one", queue1.get(timeout=1).body) @@ -320,7 +331,7 @@ class QueueTests(TestBase010): #NOTE: this assumes there is no timeout in use - #check that it has gone be declaring passively + #check that it has gone by declaring it passively try: session.queue_declare(queue="auto-delete-me", passive=True) self.fail("Expected queue to have been deleted") -- cgit v1.2.1 From b61f65b395e220c76ee207d1ce42ed474571d5e5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 7 Mar 2008 16:57:43 +0000 Subject: added session.sync(); session.auto_sync; made transfers not auto-complete; fixed bug in RangedSet git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634744 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 2 ++ qpid/python/hello-010-world | 6 +++++ qpid/python/qpid/connection010.py | 3 +-- qpid/python/qpid/datatypes.py | 7 ++++- qpid/python/qpid/delegates.py | 18 +++++++++++++ qpid/python/qpid/exceptions.py | 20 ++++++++++++++ qpid/python/qpid/session.py | 54 +++++++++++++++++++++++++++++--------- qpid/python/run-tests | 10 ++++++- qpid/python/server010 | 11 ++++++++ qpid/python/tests/connection010.py | 1 + qpid/python/tests/datatypes.py | 11 ++++++++ 11 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 qpid/python/qpid/exceptions.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 3d00313d2d..824967762b 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -63,3 +63,5 @@ tests_0-10.queue.QueueTests.test_delete_simple tests_0-10.queue.QueueTests.test_purge tests_0-10.queue.QueueTests.test_bind tests_0-10.queue.QueueTests.test_unbind_headers +tests_0-10.exchange.RecommendedTypesRuleTests.testTopic +tests_0-10.exchange.RequiredInstancesRuleTests.testAmqTopic diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 8c98170873..83e7c87675 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -38,6 +38,12 @@ print m3 m4 = ssn.incoming("test").get(timeout=10) print m4 +print ssn.sender._completed, ssn.sender.next_id +ssn.sync(10) +print ssn.sender.segments + +ssn.channel.session_flush(completed=True) + ssn.message_accept(RangedSet(m1.id, m2.id, m3.id, m4.id)) print ssn.queue_query("testing") diff --git a/qpid/python/qpid/connection010.py b/qpid/python/qpid/connection010.py index b17ceea534..022ef8e411 100644 --- a/qpid/python/qpid/connection010.py +++ b/qpid/python/qpid/connection010.py @@ -26,10 +26,9 @@ from codec010 import StringCodec from session import Session from invoker import Invoker from spec010 import Control, Command +from exceptions import * import delegates -class Timeout(Exception): pass - class ChannelBusy(Exception): pass class ChannelsBusy(Exception): pass diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index bce68aebcd..e9973b4cc8 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -67,10 +67,15 @@ class Range: return self.lower <= n and n <= self.upper def touches(self, r): + # XXX return (self.lower - 1 in r or self.upper + 1 in r or r.lower - 1 in self or - r.upper + 1 in self) + r.upper + 1 in self or + self.lower in r or + self.upper in r or + r.lower in self or + r.upper in self) def span(self, r): return Range(min(self.lower, r.lower), max(self.upper, r.upper)) diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 83413b91ea..a29d5c5265 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -20,6 +20,7 @@ import connection010 import session from util import notify +from datatypes import RangedSet class Delegate: @@ -79,6 +80,23 @@ class Delegate: ssn.receiver.next_id = cp.command_id ssn.receiver.next_offset = cp.command_offset + def session_completed(self, ch, cmp): + ch.session.sender.completed(cmp.commands) + notify(ch.session.condition) + + def session_flush(self, ch, f): + rcv = ch.session.receiver + if f.expected: + if rcv.next_id == None: + exp = None + else: + exp = RangedSet(rcv.next_id) + ch.session_expected(exp) + if f.confirmed: + ch.session_confirmed(rcv._completed) + if f.completed: + ch.session_completed(rcv._completed) + class Server(Delegate): def start(self): diff --git a/qpid/python/qpid/exceptions.py b/qpid/python/qpid/exceptions.py new file mode 100644 index 0000000000..2136793d3b --- /dev/null +++ b/qpid/python/qpid/exceptions.py @@ -0,0 +1,20 @@ +# +# 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. +# + +class Timeout(Exception): pass diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index b83bd1637f..7a84fa601d 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -17,14 +17,15 @@ # under the License. # -from threading import Condition, RLock +from threading import Condition, RLock, currentThread from invoker import Invoker from datatypes import RangedSet, Struct, Future from codec010 import StringCodec from assembler import Segment from queue import Queue from datatypes import Message -from util import wait +from util import wait, notify +from exceptions import * from logging import getLogger class SessionDetached(Exception): pass @@ -37,12 +38,14 @@ def server(*args): class SessionException(Exception): pass +INCOMPLETE = object() + class Session(Invoker): - def __init__(self, name, spec, sync=True, timeout=10, delegate=client): + def __init__(self, name, spec, auto_sync=True, timeout=10, delegate=client): self.name = name self.spec = spec - self.sync = sync + self.auto_sync = auto_sync self.timeout = timeout self.channel = None @@ -72,9 +75,29 @@ class Session(Invoker): finally: self.lock.release() + def error(self): + exc = self.exceptions[:] + if len(exc) == 1: + return exc[0] + else: + return tuple(exc) + + def sync(self, timeout=None): + if currentThread() == self.channel.connection.thread: + raise SessionException("deadlock detected") + self.channel.session_flush(completed=True) + last = self.sender.next_id - 1 + if not wait(self.condition, lambda: + last in self.sender._completed or self.exceptions, + timeout): + raise Timeout() + if self.exceptions: + raise SessionException(self.error()) + def close(self, timeout=None): self.channel.session_detach(self.name) - wait(self.condition, lambda: self.channel is None, timeout) + if not wait(self.condition, lambda: self.channel is None, timeout): + raise Timeout() def resolve_method(self, name): cmd = self.spec.instructions.get(name) @@ -132,10 +155,12 @@ class Session(Invoker): self.send(seg) if type.result: - if self.sync: + if self.auto_sync: return result.get(self.timeout) else: return result + elif self.auto_sync: + self.sync(self.timeout) def received(self, seg): self.receiver.received(seg) @@ -148,6 +173,7 @@ class Session(Invoker): self.assembly = None def dispatch(self, assembly): + segments = assembly[:] cmd = assembly.pop(0).decode(self.spec) args = [] @@ -168,8 +194,9 @@ class Session(Invoker): if cmd.type.result: self.execution_result(cmd.id, result) - for seg in assembly: - self.receiver.completed(seg) + if result is not INCOMPLETE: + for seg in segments: + self.receiver.completed(seg) def send(self, seg): self.sender.send(seg) @@ -212,6 +239,7 @@ class Sender: self.next_id = 0 self.next_offset = 0 self.segments = [] + self._completed = RangedSet() def send(self, seg): seg.id = self.next_id @@ -235,6 +263,8 @@ class Sender: del self.segments[idx] else: idx += 1 + for range in commands.ranges: + self._completed.add(range.lower, range.upper) class Delegate: @@ -249,17 +279,14 @@ class Delegate: self.session.lock.acquire() try: self.session.exceptions.append(ex) - excs = self.session.exceptions[:] - if len(excs) == 1: - error = excs[0] - else: - error = tuple(excs) + error = self.session.error() for id in self.session.results: f = self.session.results.pop(id) f.error(error) for q in self.session._incoming.values(): q.close(error) + notify(self.session.condition) finally: self.session.lock.release() @@ -274,3 +301,4 @@ class Client(Delegate): messages = self.session.incoming(cmd.destination) messages.put(m) msg.debug("RECV: %s", m) + return INCOMPLETE diff --git a/qpid/python/run-tests b/qpid/python/run-tests index 90c0200d01..7efe5523df 100755 --- a/qpid/python/run-tests +++ b/qpid/python/run-tests @@ -18,9 +18,17 @@ # under the License. # -import sys +import sys, logging from qpid.testlib import testrunner +if "-v" in sys.argv: + level = logging.DEBUG +else: + level = logging.WARN + +format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" +logging.basicConfig(level=level, format=format, datefmt='%H:%M:%S') + if not testrunner.run(): sys.exit(1) diff --git a/qpid/python/server010 b/qpid/python/server010 index ecf76577ec..2b2aa7f8d3 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -7,6 +7,16 @@ from qpid.spec010 import load from qpid.session import Client from qpid.datatypes import Message +import logging, sys + +if "-v" in sys.argv: + level = logging.DEBUG +else: + level = logging.WARN + +format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" +logging.basicConfig(level=level, format=format, datefmt='%H:%M:%S') + spec = load("../specs/amqp.0-10.xml") class Server: @@ -15,6 +25,7 @@ class Server: return delegates.Server(connection, self.session) def session(self, session): + session.auto_sync = False return SessionDelegate(session) class SessionDelegate(Client): diff --git a/qpid/python/tests/connection010.py b/qpid/python/tests/connection010.py index 8adf20fd78..e966ede377 100644 --- a/qpid/python/tests/connection010.py +++ b/qpid/python/tests/connection010.py @@ -39,6 +39,7 @@ class TestServer: return Server(connection, delegate=self.session) def session(self, session): + session.auto_sync = False return TestSession(session, self.queue) class TestSession(Delegate): diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index e22e250f61..7844cf4d10 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -89,3 +89,14 @@ class RangedSetTest(TestCase): assert 21 not in rs assert 20 in rs self.check(rs.ranges) + + def testAddSelf(self): + a = RangedSet() + a.add(0, 8) + self.check(a.ranges) + a.add(0, 8) + self.check(a.ranges) + assert len(a.ranges) == 1 + range = a.ranges[0] + assert range.lower == 0 + assert range.upper == 8 -- cgit v1.2.1 From c8c75599115f4c8e8d610f16aaee77f1aed9324c Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 7 Mar 2008 17:56:41 +0000 Subject: send an empty frame for an empty segment git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634763 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 2 -- qpid/python/qpid/assembler.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 824967762b..3d00313d2d 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -63,5 +63,3 @@ tests_0-10.queue.QueueTests.test_delete_simple tests_0-10.queue.QueueTests.test_purge tests_0-10.queue.QueueTests.test_bind tests_0-10.queue.QueueTests.test_unbind_headers -tests_0-10.exchange.RecommendedTypesRuleTests.testTopic -tests_0-10.exchange.RequiredInstancesRuleTests.testAmqTopic diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py index fe78baaceb..1a480698c8 100644 --- a/qpid/python/qpid/assembler.py +++ b/qpid/python/qpid/assembler.py @@ -96,7 +96,7 @@ class Assembler(Framer): remaining = segment.payload first = True - while remaining: + while first or remaining: payload = remaining[:self.max_payload] remaining = remaining[self.max_payload:] -- cgit v1.2.1 From 8f4d89dccf62bc6a0a41d9221049b136611e92dc Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 7 Mar 2008 19:07:32 +0000 Subject: Added acquire impl to final 0-10 codepath Converted some more python tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634780 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 4 +-- qpid/python/qpid/testlib.py | 2 +- qpid/python/run-tests | 2 +- qpid/python/tests_0-10/message.py | 51 +++++++++++++++++++++++++-------------- qpid/python/tests_0-10/queue.py | 24 ++++++++++++------ 5 files changed, 53 insertions(+), 30 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 3d00313d2d..3b2c560d8a 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -39,8 +39,6 @@ tests_0-10.dtx.DtxTests.test_start_join tests_0-10.dtx.DtxTests.test_start_join_and_resume tests_0-10.dtx.DtxTests.test_suspend_resume tests_0-10.dtx.DtxTests.test_suspend_start_end_resume -tests_0-10.message.MessageTests.test_acquire -tests_0-10.message.MessageTests.test_subscribe_not_acquired_3 tests_0-10.message.MessageTests.test_consume_exclusive tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward @@ -60,6 +58,6 @@ tests_0-10.queue.QueueTests.test_declare_passive tests_0-10.queue.QueueTests.test_delete_ifempty tests_0-10.queue.QueueTests.test_delete_ifunused tests_0-10.queue.QueueTests.test_delete_simple -tests_0-10.queue.QueueTests.test_purge tests_0-10.queue.QueueTests.test_bind tests_0-10.queue.QueueTests.test_unbind_headers +tests_0-10.queue.QueueTests.test_purge_empty_name \ No newline at end of file diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index e8e54b3a56..7e5a2a6b66 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -353,5 +353,5 @@ class TestBase010(unittest.TestCase): self.session = self.conn.session("test-session", timeout=10) def tearDown(self): - self.session.close(timeout=10) + if not self.session.error(): self.session.close(timeout=10) self.conn.close(timeout=10) diff --git a/qpid/python/run-tests b/qpid/python/run-tests index 7efe5523df..07162efd15 100755 --- a/qpid/python/run-tests +++ b/qpid/python/run-tests @@ -21,7 +21,7 @@ import sys, logging from qpid.testlib import testrunner -if "-v" in sys.argv: +if "-vv" in sys.argv: level = logging.DEBUG else: level = logging.WARN diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index aaefb52392..dd80e79d36 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -635,9 +635,7 @@ class MessageTests(TestBase010): session.queue_declare(queue = "q", exclusive=True, auto_delete=True) #use fanout for now: - session.exchange_bind(exchange="amq.fanout", queue="q") - session.message_transfer(destination="amq.fanout", message=Message("acquire me")) - #session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) @@ -647,12 +645,13 @@ class MessageTests(TestBase010): #message should still be on the queue: self.assertEquals(1, session.queue_query(queue = "q").message_count) - response = session.message_acquire(RangedSet(msg.id)) + transfers = RangedSet(msg.id) + response = session.message_acquire(transfers) #check that we get notification (i.e. message_acquired) - self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) + self.assert_(msg.id in response.transfers) #message should have been removed from the queue: self.assertEquals(0, session.queue_query(queue = "q").message_count) - session.message_accept(RangedSet(msg.id)) + session.message_accept(transfers) def test_release(self): @@ -800,12 +799,12 @@ class MessageTests(TestBase010): session = self.session #publish some messages - self.queue_declare(queue = "q", exclusive=True, auto_delete=True) + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range(1, 11): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "message-%d" % (i))) #create a not-acquired subscriber - session.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) + session.message_subscribe(queue = "q", destination = "a", acquire_mode=1) session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") session.message_flow(unit = 0, value = 10, destination = "a") @@ -816,19 +815,18 @@ class MessageTests(TestBase010): self.assertEquals("message-%d" % (i), msg.body) if (i % 2): #try to acquire every second message - session.message_acquire([msg.command_id, msg.command_id]) + response = session.message_acquire(RangedSet(msg.id)) #check that acquire succeeds - response = session.control_queue.get(timeout=1) - self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) - session.message_release(RangedSet(msg.id)) - session.channel._completed.add(msg.id) - session.channel.session_completed(session.channel._completed) - - msg.complete() + self.assert_(msg.id in response.transfers) + session.message_accept(RangedSet(msg.id)) + else: + session.message_release(RangedSet(msg.id)) + session.receiver._completed.add(msg.id) + session.channel.session_completed(session.receiver._completed) self.assertEmpty(queue) #create a second not-acquired subscriber - session.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) + session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") session.message_flow(unit = 0, value = 1, destination = "b") #check it gets those not consumed @@ -836,7 +834,9 @@ class MessageTests(TestBase010): for i in [2,4,6,8,10]: msg = queue.get(timeout = 1) self.assertEquals("message-%d" % (i), msg.body) - msg.complete() + session.message_release(RangedSet(msg.id)) + session.receiver._completed.add(msg.id) + session.channel.session_completed(session.receiver._completed) session.message_flow(unit = 0, value = 1, destination = "b") self.assertEmpty(queue) @@ -899,6 +899,21 @@ class MessageTests(TestBase010): msg = queue.get(timeout = 3) self.assertEquals("message-body", msg.body) + def test_empty_body(self): + session = self.session + session.queue_declare(queue="xyz", exclusive=True, auto_delete=True) + props = session.delivery_properties(routing_key="xyz") + session.message_transfer(message=Message(props, "")) + + consumer_tag = "tag1" + session.message_subscribe(queue="xyz", destination=consumer_tag) + session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) + queue = session.incoming(consumer_tag) + msg = queue.get(timeout=1) + self.assertEquals("", msg.body) + session.message_accept(RangedSet(msg.id)) + def assertDataEquals(self, session, msg, expected): self.assertEquals(expected, msg.body) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 38d7a3291b..97e17048d3 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -20,6 +20,7 @@ from qpid.client import Client, Closed from qpid.queue import Empty from qpid.testlib import TestBase010 from qpid.datatypes import Message +from qpid.session import SessionException class QueueTests(TestBase010): """Tests for 'methods' on the amqp queue 'class'""" @@ -54,22 +55,31 @@ class QueueTests(TestBase010): msg = queue.get(timeout=1) self.assertEqual("four", msg.body) - #check error conditions (use new sessions): - session = self.conn.session("error-checker") + def test_purge_queue_exists(self): + """ + Test that the correct exception is thrown is no queue exists + for the name specified in purge + """ + session = self.session try: #queue specified but doesn't exist: session.queue_purge(queue="invalid-queue") self.fail("Expected failure when purging non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) #not-found - session = self.conn.session("error-checker") + def test_purge_empty_name(self): + """ + Test that the correct exception is thrown is no queue name + is specified for purge + """ + session = self.session try: #queue not specified and none previously declared for channel: session.queue_purge() self.fail("Expected failure when purging unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) + except SessionException, e: + self.assertEquals(531, e.args[0].error_code) #illegal-argument def test_declare_exclusive(self): """ -- cgit v1.2.1 From dfddf64c560344cf78cabacfbb797fbf78f9017f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 7 Mar 2008 20:22:18 +0000 Subject: added support for maps git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@634803 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 29 +++++++++++++++++++--- qpid/python/qpid/delegates.py | 9 ++++--- qpid/python/qpid/spec010.py | 30 ++++++++++++++++++++--- qpid/python/tests/__init__.py | 1 + qpid/python/tests/codec010.py | 57 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 qpid/python/tests/codec010.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index f6539ffcee..27df9db974 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -20,11 +20,18 @@ from packer import Packer from datatypes import RangedSet +class CodecException(Exception): pass + class Codec(Packer): def __init__(self, spec): self.spec = spec + def write_void(self, v): + assert v == None + def read_void(self): + return None + def write_bit(self, b): if not b: raise ValueError(b) def read_bit(self): @@ -148,10 +155,26 @@ class Codec(Packer): self.write(b) def write_map(self, m): - self.write_uint32(0) #hack + sc = StringCodec(self.spec) + for k, v in m.items(): + type = self.spec.encoding(v.__class__) + if type == None: + raise CodecException("no encoding for %s" % v.__class__) + sc.write_str8(k) + sc.write_uint8(type.code) + type.encode(sc, v) + # XXX: need to put in count when CPP supports it + self.write_vbin32(sc.encoded) def read_map(self): - size = self.read_uint32() #hack - self.read(size) #hack + sc = StringCodec(self.spec, self.read_vbin32()) + result = {} + while sc.encoded: + k = sc.read_str8() + code = sc.read_uint8() + type = self.spec.types[code] + v = type.decode(sc) + result[k] = v + return result def write_array(self, a): pass diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index a29d5c5265..d1f615a3fa 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -17,8 +17,7 @@ # under the License. # -import connection010 -import session +import os, connection010, session from util import notify from datatypes import RangedSet @@ -117,12 +116,16 @@ class Server(Delegate): class Client(Delegate): + PROPERTIES = {"product": "qpid python client", + "version": "development", + "platform": os.name} + def start(self): self.connection.write_header(self.spec.major, self.spec.minor) self.connection.read_header() def connection_start(self, ch, start): - ch.connection_start_ok() + ch.connection_start_ok(client_properties=Client.PROPERTIES) def connection_tune(self, ch, tune): ch.connection_tune_ok() diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index c3f3e6ad57..b84be7a047 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -122,7 +122,6 @@ class Type(Named, Node): def register(self, node): Named.register(self, node) - node.types.append(self) Node.register(self) class Primitive(Coded, Type): @@ -133,6 +132,11 @@ class Primitive(Coded, Type): self.fixed = fixed self.variable = variable + def register(self, node): + Type.register(self, node) + if self.code is not None: + self.spec.types[self.code] = self + def is_present(self, value): if self.fixed == 0: return value @@ -265,7 +269,8 @@ class Struct(Composite): def register(self, node): Composite.register(self, node) - self.spec.structs[self.code] = self + if self.code is not None: + self.spec.structs[self.code] = self def __str__(self): fields = ",\n ".join(["%s: %s" % (f.name, f.type.qname) @@ -384,7 +389,6 @@ class Class(Named, Coded, Node): Named.__init__(self, name) Coded.__init__(self, code) Node.__init__(self, children) - self.types = [] self.controls = [] self.commands = [] @@ -441,6 +445,16 @@ class Exception(Named, Node): class Spec(Node): + ENCODINGS = { + basestring: "vbin16", + int: "int32", + long: "int64", + None.__class__: "void", + list: "list", + tuple: "list", + dict: "map" + } + def __init__(self, major, minor, port, children): Node.__init__(self, children) self.major = major @@ -448,7 +462,7 @@ class Spec(Node): self.port = port self.constants = [] self.classes = [] - self.types = [] + self.types = {} self.qname = None self.spec = self self.klass = None @@ -457,6 +471,14 @@ class Spec(Node): self.commands = {} self.structs = {} + def encoding(self, klass): + if Spec.ENCODINGS.has_key(klass): + return self.named[Spec.ENCODINGS[klass]] + for base in klass.__bases__: + result = self.encoding(base) + if result != None: + return result + class Implement: def __init__(self, handle): diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py index 8e9eeb44d6..521e2f15c9 100644 --- a/qpid/python/tests/__init__.py +++ b/qpid/python/tests/__init__.py @@ -27,3 +27,4 @@ from assembler import * from datatypes import * from connection010 import * from spec010 import * +from codec010 import * diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py new file mode 100644 index 0000000000..65d4525758 --- /dev/null +++ b/qpid/python/tests/codec010.py @@ -0,0 +1,57 @@ +# +# 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. +# + +from unittest import TestCase +from qpid.spec010 import load +from qpid.codec010 import StringCodec +from qpid.testlib import testrunner + +class CodecTest(TestCase): + + def setUp(self): + self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) + + def check(self, type, value): + t = self.spec[type] + sc = StringCodec(self.spec) + t.encode(sc, value) + decoded = t.decode(sc) + assert decoded == value + + def testMapString(self): + self.check("map", {"string": "this is a test"}) + + def testMapInt(self): + self.check("map", {"int": 3}) + + def testMapLong(self): + self.check("map", {"long": 2**32}) + + def testMapNone(self): + self.check("map", {"none": None}) + + def testMapNested(self): + self.check("map", {"map": {"string": "nested test"}}) + + def testMapAll(self): + self.check("map", {"string": "this is a test", + "int": 3, + "long": 2**32, + "none": None, + "map": {"string": "nested map"}}) -- cgit v1.2.1 From 6128ccc4ae46f1cfdc9d3354a05c563f14be9816 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 10 Mar 2008 17:47:06 +0000 Subject: Adjusted exception handling in c++ for final 0-10 path. Converted some more tests. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635618 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 5 --- qpid/python/tests_0-10/queue.py | 71 +++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 39 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 3b2c560d8a..8930f6e05b 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -54,10 +54,5 @@ tests_0-10.testlib.TestBaseTest.testAssertEmptyPass tests_0-10.testlib.TestBaseTest.testMessageProperties tests_0-10.queue.QueueTests.test_autodelete_shared tests_0-10.queue.QueueTests.test_declare_exclusive -tests_0-10.queue.QueueTests.test_declare_passive -tests_0-10.queue.QueueTests.test_delete_ifempty -tests_0-10.queue.QueueTests.test_delete_ifunused -tests_0-10.queue.QueueTests.test_delete_simple tests_0-10.queue.QueueTests.test_bind tests_0-10.queue.QueueTests.test_unbind_headers -tests_0-10.queue.QueueTests.test_purge_empty_name \ No newline at end of file diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 97e17048d3..dba732d415 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -99,7 +99,7 @@ class QueueTests(TestBase010): c2.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) self.fail("Expected second exclusive queue_declare to raise a channel exception") except Closed, e: - self.assertChannelException(405, e.args[0]) + self.assertEquals(405, e.args[0].error_code) def test_declare_passive(self): @@ -114,8 +114,8 @@ class QueueTests(TestBase010): #other connection should not be allowed to declare this: session.queue_declare(queue="passive-queue-2", passive=True) self.fail("Expected passive declaration of non-existant queue to raise a channel exception") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) #not-found def test_bind(self): @@ -136,7 +136,7 @@ class QueueTests(TestBase010): session.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") self.fail("Expected bind to non-existant exchange to fail") except Closed, e: - self.assertChannelException(404, e.args[0]) + self.assertEquals(404, e.args[0].error_code) #need to reopen a session: session = self.client.session(2) @@ -147,7 +147,7 @@ class QueueTests(TestBase010): session.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") self.fail("Expected bind of non-existant queue to fail") except Closed, e: - self.assertChannelException(404, e.args[0]) + self.assertEquals(404, e.args[0].error_code) def test_unbind_direct(self): self.unbind_test(exchange="amq.direct", routing_key="key") @@ -222,25 +222,28 @@ class QueueTests(TestBase010): #straight-forward case: session.queue_declare(queue="delete-me") - session.message_transfer(message=Message("a", session.delivery_properties(routing_key="delete-me"))) - session.message_transfer(message=Message("b", session.delivery_properties(routing_key="delete-me"))) - session.message_transfer(message=Message("c", session.delivery_properties(routing_key="delete-me"))) + session.message_transfer(message=Message(session.delivery_properties(routing_key="delete-me"), "a")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="delete-me"), "b")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="delete-me"), "c")) session.queue_delete(queue="delete-me") #check that it has gone be declaring passively try: session.queue_declare(queue="delete-me", passive=True) self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) + def test_delete_queue_exists(self): + """ + Test core queue deletion behaviour + """ #check attempted deletion of non-existant queue is handled correctly: - session = self.client.session(2) - session.session_open() + session = self.session try: session.queue_delete(queue="i-dont-exist", if_empty=True) self.fail("Expected delete of non-existant queue to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) @@ -253,21 +256,22 @@ class QueueTests(TestBase010): #create a queue and add a message to it (use default binding): session.queue_declare(queue="delete-me-2") session.queue_declare(queue="delete-me-2", passive=True) - session.message_transfer(message=Message("message", session.delivery_properties(routing_key="delete-me-2"))) + session.message_transfer(message=Message(session.delivery_properties(routing_key="delete-me-2"), "message")) #try to delete, but only if empty: try: session.queue_delete(queue="delete-me-2", if_empty=True) self.fail("Expected delete if_empty to fail for non-empty queue") - except Closed, e: - self.assertChannelException(406, e.args[0]) + except SessionException, e: + self.assertEquals(406, e.args[0].error_code) - #need new channel now: - session = self.client.session(2) - session.session_open() + #need new session now: + session = self.conn.session("replacement", 2) #empty queue: - self.subscribe(session, destination="consumer_tag", queue="delete-me-2") + session.message_subscribe(session, destination="consumer_tag", queue="delete-me-2") + session.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) queue = session.incoming("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.body) @@ -280,30 +284,29 @@ class QueueTests(TestBase010): try: session.queue_declare(queue="delete-me-2", passive=True) self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) def test_delete_ifunused(self): """ Test that if_unused field of queue_delete is honoured """ - session = self.channel + session = self.session #create a queue and register a consumer: session.queue_declare(queue="delete-me-3") session.queue_declare(queue="delete-me-3", passive=True) - self.subscribe(destination="consumer_tag", queue="delete-me-3") + session.message_subscribe(destination="consumer_tag", queue="delete-me-3") #need new session now: - session2 = self.client.session(2) - session2.session_open() + session2 = self.conn.session("replacement", 2) + #try to delete, but only if empty: try: session2.queue_delete(queue="delete-me-3", if_unused=True) self.fail("Expected delete if_unused to fail for queue with existing consumer") - except Closed, e: - self.assertChannelException(406, e.args[0]) - + except SessionException, e: + self.assertEquals(406, e.args[0].error_code) session.message_cancel(destination="consumer_tag") session.queue_delete(queue="delete-me-3", if_unused=True) @@ -311,8 +314,8 @@ class QueueTests(TestBase010): try: session.queue_declare(queue="delete-me-3", passive=True) self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) def test_autodelete_shared(self): @@ -345,7 +348,7 @@ class QueueTests(TestBase010): try: session.queue_declare(queue="auto-delete-me", passive=True) self.fail("Expected queue to have been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) -- cgit v1.2.1 From 1886bd80e9984a963dd0688ba2814db65a728cea Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 10 Mar 2008 19:12:09 +0000 Subject: renamed datatypes.Struct.type -> datatypes.Struct._type; this avoids naming conflicts with metadata-driven fields; moved argument validation -> datatypes.Struct and improved error checking; improved datatypes.Struct.__repr__ git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635660 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/assembler.py | 4 ++-- qpid/python/qpid/codec010.py | 27 +++++++++++++--------- qpid/python/qpid/connection010.py | 2 +- qpid/python/qpid/datatypes.py | 47 ++++++++++++++++++++++++++++++++------ qpid/python/qpid/delegates.py | 2 +- qpid/python/qpid/session.py | 11 +++++---- qpid/python/qpid/spec010.py | 45 ++++++++---------------------------- qpid/python/tests/connection010.py | 2 +- qpid/python/tests/spec010.py | 18 +++++++++------ qpid/python/tests_0-10/example.py | 2 +- qpid/python/tests_0-10/queue.py | 2 +- 11 files changed, 89 insertions(+), 73 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py index 1a480698c8..9f8bc4bd8a 100644 --- a/qpid/python/qpid/assembler.py +++ b/qpid/python/qpid/assembler.py @@ -46,9 +46,9 @@ class Segment: def decode_command(self, spec): sc = StringCodec(spec, self.payload) - cmd = sc.read_command() + hdr, cmd = sc.read_command() cmd.id = self.id - return cmd + return hdr, cmd def decode_header(self, spec): sc = StringCodec(spec, self.payload) diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 27df9db974..60adbdc2e4 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -18,7 +18,7 @@ # from packer import Packer -from datatypes import RangedSet +from datatypes import RangedSet, Struct class CodecException(Exception): pass @@ -184,27 +184,32 @@ class Codec(Packer): def read_struct32(self): size = self.read_uint32() code = self.read_uint16() - struct = self.spec.structs[code] - return struct.decode_fields(self) + type = self.spec.structs[code] + fields = type.decode_fields(self) + return Struct(type, **fields) def write_struct32(self, value): sc = StringCodec(self.spec) - sc.write_uint16(value.type.code) - value.type.encode_fields(sc, value) + sc.write_uint16(value._type.code) + value._type.encode_fields(sc, value) self.write_vbin32(sc.encoded) def read_control(self): cntrl = self.spec.controls[self.read_uint16()] return cntrl.decode(self) - def write_control(self, type, ctrl): + def write_control(self, ctrl): + type = ctrl._type self.write_uint16(type.code) type.encode(self, ctrl) def read_command(self): - cmd = self.spec.commands[self.read_uint16()] - return cmd.decode(self) - def write_command(self, type, cmd): - self.write_uint16(type.code) - type.encode(self, cmd) + type = self.spec.commands[self.read_uint16()] + hdr = self.spec["session.header"].decode(self) + cmd = type.decode(self) + return hdr, cmd + def write_command(self, hdr, cmd): + self.write_uint16(cmd._type.code) + hdr._type.encode(self, hdr) + cmd._type.encode(self, cmd) def read_size(self, width): if width > 0: diff --git a/qpid/python/qpid/connection010.py b/qpid/python/qpid/connection010.py index 022ef8e411..1235476b82 100644 --- a/qpid/python/qpid/connection010.py +++ b/qpid/python/qpid/connection010.py @@ -166,7 +166,7 @@ class Channel(Invoker): def invoke(self, type, args, kwargs): cntrl = type.new(args, kwargs) sc = StringCodec(self.connection.spec) - sc.write_control(type, cntrl) + sc.write_control(cntrl) self.connection.write_segment(Segment(True, True, type.segment_type, type.track, self.id, sc.encoded)) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index e9973b4cc8..45944a2494 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -21,15 +21,48 @@ import threading class Struct: - def __init__(self, fields): - self.__dict__ = fields + def __init__(self, _type, *args, **kwargs): + if len(args) > len(_type.fields): + raise TypeError("%s() takes at most %s arguments (%s given)" % + (_type.name, len(_type.fields), len(args))) - def __repr__(self): - return "Struct(%s)" % ", ".join(["%s=%r" % (k, v) - for k, v in self.__dict__.items()]) + self._type = _type + + idx = 0 + for field in _type.fields: + if idx < len(args): + arg = args[idx] + if kwargs.has_key(field.name): + raise TypeError("%s() got multiple values for keyword argument '%s'" % + (_type.name, field.name)) + elif kwargs.has_key(field.name): + arg = kwargs.pop(field.name) + else: + arg = field.default() + setattr(self, field.name, arg) + idx += 1 - def fields(self): - return self.__dict__ + if kwargs: + unexpected = kwargs.keys()[0] + raise TypeError("%s() got an unexpected keywoard argument '%s'" % + (_type.name, unexpected)) + + def __getitem__(self, name): + return getattr(self, name) + + def __setitem__(self, name, value): + if not hasattr(self, name): + raise AttributeError("'%s' object has no attribute '%s'" % + (self._type.name, name)) + setattr(self, name, value) + + def __repr__(self): + fields = [] + for f in self._type.fields: + v = self[f.name] + if f.type.is_present(v): + fields.append("%s=%r" % (f.name, v)) + return "%s(%s)" % (self._type.name, ", ".join(fields)) class Message: diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index d1f615a3fa..8de7141962 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -38,7 +38,7 @@ class Delegate: if seg.track == self.control: cntrl = seg.decode(self.spec) - attr = cntrl.type.qname.replace(".", "_") + attr = cntrl._type.qname.replace(".", "_") getattr(self, attr)(ch, cntrl) elif ssn is None: ch.session_detached() diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 7a84fa601d..8374b8cd3d 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -129,7 +129,8 @@ class Session(Invoker): cmd = type.new(args, kwargs) sc = StringCodec(self.spec) - sc.write_command(type, cmd) + hdr = Struct(self.spec["session.header"]) + sc.write_command(hdr, cmd) seg = Segment(True, (message == None or (message.headers == None and message.body == None)), @@ -174,10 +175,10 @@ class Session(Invoker): def dispatch(self, assembly): segments = assembly[:] - cmd = assembly.pop(0).decode(self.spec) + hdr, cmd = assembly.pop(0).decode(self.spec) args = [] - for st in cmd.type.segments: + for st in cmd._type.segments: if assembly: seg = assembly[0] if seg.type == st.segment_type: @@ -188,10 +189,10 @@ class Session(Invoker): assert len(assembly) == 0 - attr = cmd.type.qname.replace(".", "_") + attr = cmd._type.qname.replace(".", "_") result = getattr(self.delegate, attr)(cmd, *args) - if cmd.type.result: + if cmd._type.result: self.execution_result(cmd.id, result) if result is not INCOMPLETE: diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index b84be7a047..815c1d064a 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -188,34 +188,18 @@ class Composite(Type, Coded): self.pack = pack def new(self, args, kwargs): - if len(args) > len(self.fields): - raise TypeError("%s takes at most %s arguments (%s given)" % - (self.name, len(self.fields), len(self.args))) - - result = {"type": self} - - for a, f, in zip(args, self.fields): - result[f.name] = a - - for k, v in kwargs.items(): - f = self.named.get(k) - if f == None: - raise TypeError("%s got an unexpected keyword argument '%s'" % - (self.name, k)) - result[f.name] = v - - return datatypes.Struct(result) + return datatypes.Struct(self, *args, **kwargs) def decode(self, codec): codec.read_size(self.size) - return self.decode_fields(codec) + return datatypes.Struct(self, **self.decode_fields(codec)) def decode_fields(self, codec): flags = 0 for i in range(self.pack): flags |= (codec.read_uint8() << 8*i) - result = {"type": self} + result = {} for i in range(len(self.fields)): f = self.fields[i] @@ -223,7 +207,7 @@ class Composite(Type, Coded): result[f.name] = f.type.decode(codec) else: result[f.name] = None - return datatypes.Struct(result) + return result def encode(self, codec, value): sc = StringCodec(self.spec) @@ -231,12 +215,11 @@ class Composite(Type, Coded): codec.write_size(self.size, len(sc.encoded)) codec.write(sc.encoded) - def encode_fields(self, codec, value): - values = value.__dict__ + def encode_fields(self, codec, values): flags = 0 for i in range(len(self.fields)): f = self.fields[i] - if f.type.is_present(values.get(f.name)): + if f.type.is_present(values[f.name]): flags |= (0x1 << i) for i in range(self.pack): codec.write_uint8((flags >> 8*i) & 0xFF) @@ -253,6 +236,9 @@ class Field(Named, Node, Lookup): self.type = type self.exceptions = [] + def default(self): + return None + def register(self, node): Named.register(self, node) node.fields.append(self) @@ -328,23 +314,10 @@ class Command(Instruction): def register(self, node): Instruction.register(self, node) node.commands.append(self) - self.header = self.spec["session.header"] self.spec.commands[self.code] = self self.segment_type = self.spec["segment_type.command"].value self.track = self.spec["track.command"].value - def decode(self, codec): - hdr = self.header.decode(codec) - args = Instruction.decode(self, codec) - result = {} - result.update(hdr.fields()) - result.update(args.fields()) - return datatypes.Struct(result) - - def encode(self, codec, cmd): - self.header.encode(codec, cmd) - Instruction.encode(self, codec, cmd) - class Header(Segment, Node): def __init__(self, children): diff --git a/qpid/python/tests/connection010.py b/qpid/python/tests/connection010.py index e966ede377..a953e034a2 100644 --- a/qpid/python/tests/connection010.py +++ b/qpid/python/tests/connection010.py @@ -49,7 +49,7 @@ class TestSession(Delegate): self.queue = queue def queue_query(self, qq): - return qq.type.result.type.new((qq.queue,), {}) + return qq._type.result.type.new((qq.queue,), {}) def message_transfer(self, cmd, header, body): self.queue.put((cmd, header, body)) diff --git a/qpid/python/tests/spec010.py b/qpid/python/tests/spec010.py index 1c520ee323..4161dc060f 100644 --- a/qpid/python/tests/spec010.py +++ b/qpid/python/tests/spec010.py @@ -31,11 +31,11 @@ class SpecTest(TestCase): def testSessionHeader(self): hdr = self.spec["session.header"] sc = StringCodec(self.spec) - hdr.encode(sc, Struct({"sync": True})) + hdr.encode(sc, Struct(hdr, sync=True)) assert sc.encoded == "\x01\x01" sc = StringCodec(self.spec) - hdr.encode(sc, Struct({"sync": False})) + hdr.encode(sc, Struct(hdr, sync=False)) assert sc.encoded == "\x01\x00" def encdec(self, type, value): @@ -45,16 +45,20 @@ class SpecTest(TestCase): return decoded def testMessageProperties(self): - props = Struct({"content_length": 0xDEADBEEF, - "reply_to": - Struct({"exchange": "the exchange name", "routing_key": "the routing key"})}) - dec = self.encdec(self.spec["message.message_properties"], props) + mp = self.spec["message.message_properties"] + rt = self.spec["message.reply_to"] + + props = Struct(mp, content_length=0xDEADBEEF, + reply_to=Struct(rt, exchange="the exchange name", + routing_key="the routing key")) + dec = self.encdec(mp, props) assert props.content_length == dec.content_length assert props.reply_to.exchange == dec.reply_to.exchange assert props.reply_to.routing_key == dec.reply_to.routing_key def testMessageSubscribe(self): - cmd = Struct({"exclusive": True, "destination": "this is a test"}) + ms = self.spec["message.subscribe"] + cmd = Struct(ms, exclusive=True, destination="this is a test") dec = self.encdec(self.spec["message.subscribe"], cmd) assert cmd.exclusive == dec.exclusive assert cmd.destination == dec.destination diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index fd10a8df4f..1e140a285d 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -60,7 +60,7 @@ class ExampleTest (TestBase010): session.exchange_declare("test", "direct") # Here we use keyword arguments. - session.queue_declare(session, queue="test-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) session.exchange_bind(queue="test-queue", exchange="test", binding_key="key") # Call Session.subscribe to register as a consumer. diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index dba732d415..bacbc60bc1 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -269,7 +269,7 @@ class QueueTests(TestBase010): session = self.conn.session("replacement", 2) #empty queue: - session.message_subscribe(session, destination="consumer_tag", queue="delete-me-2") + session.message_subscribe(destination="consumer_tag", queue="delete-me-2") session.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) session.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) queue = session.incoming("consumer_tag") -- cgit v1.2.1 From bab481852390d5d23a9d4db2177250762e98f84c Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 10 Mar 2008 19:50:16 +0000 Subject: fixed datatypes.Struct.type -> datatypes.Struct._type rename omitted from server010 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635672 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/server010 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/server010 b/qpid/python/server010 index 2b2aa7f8d3..7b4b5762ca 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -37,7 +37,7 @@ class SessionDelegate(Client): print "Queue %s declared..." % qd.queue def queue_query(self, qq): - return qq.type.result.type.new((qq.queue,), {}) + return qq._type.result.type.new((qq.queue,), {}) def message_transfer(self, cmd, headers, body): m = Message(body) -- cgit v1.2.1 From aab8bdfcf3d279758124fc01213f33563a73040f Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 11 Mar 2008 12:24:46 +0000 Subject: Fixed broker to take application headers from final format message-properties struct Fixed headers exchange to recognise x-match even if sent as a string other than 32 bit sized Converted remaining python exchange tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635898 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 8 -------- qpid/python/tests_0-10/exchange.py | 37 ++++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 25 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 8930f6e05b..8b0e11b3f0 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -10,14 +10,6 @@ tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_e tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_queue tests_0-10.alternate_exchange.AlternateExchangeTests.test_queue_delete tests_0-10.alternate_exchange.AlternateExchangeTests.test_unroutable -tests_0-10.exchange.DeclareMethodPassiveFieldNotFoundRuleTests.test -tests_0-10.exchange.ExchangeTests.testHeadersBindNoMatchArg -tests_0-10.exchange.HeadersExchangeTests.testMatchAll -tests_0-10.exchange.HeadersExchangeTests.testMatchAny -tests_0-10.exchange.MiscellaneousErrorsTests.testDifferentDeclaredType -tests_0-10.exchange.MiscellaneousErrorsTests.testTypeNotKnown -tests_0-10.exchange.RecommendedTypesRuleTests.testHeaders -tests_0-10.exchange.RequiredInstancesRuleTests.testAmqMatch tests_0-10.dtx.DtxTests.test_bad_resume tests_0-10.dtx.DtxTests.test_end tests_0-10.dtx.DtxTests.test_end_suspend_and_fail diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 151807b045..be4f72c74c 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -27,6 +27,7 @@ import Queue, logging, traceback from qpid.testlib import TestBase010 from qpid.datatypes import Message from qpid.client import Closed +from qpid.session import SessionException class TestHelper(TestBase010): @@ -48,6 +49,11 @@ class TestHelper(TestBase010): def createMessage(self, key="", body=""): return Message(self.session.delivery_properties(routing_key=key), body) + def getApplicationHeaders(self, msg): + for h in msg.headers: + if hasattr(h, 'application_headers'): return getattr(h, 'application_headers') + return None + def assertPublishGet(self, queue, exchange="", routing_key="", properties=None): """ Publish to exchange and assert queue.get() returns the same message. @@ -59,7 +65,7 @@ class TestHelper(TestBase010): msg = queue.get(timeout=1) self.assertEqual(body, msg.body) if (properties): - self.assertEqual(properties, msg.content['application_headers']) + self.assertEqual(properties, self.getApplicationHeaders(msg)) def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None): """ @@ -294,8 +300,8 @@ class DeclareMethodPassiveFieldNotFoundRuleTests(TestHelper): try: self.session.exchange_declare(exchange="humpty_dumpty", passive=True) self.fail("Expected 404 for passive declaration of unknown exchange.") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) class DeclareMethodDurableFieldSupportRuleTests(TestHelper): @@ -352,7 +358,8 @@ class HeadersExchangeTests(TestHelper): self.assertPublishGet(self.q, exchange="amq.match", properties=headers) def myBasicPublish(self, headers): - self.session.message_transfer(destination="amq.match", content=Content("foobar", properties={'application_headers':headers})) + mp=self.session.message_properties(application_headers=headers) + self.session.message_transfer(destination="amq.match", message=Message(mp, "foobar")) def testMatchAll(self): self.session.exchange_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) @@ -386,21 +393,17 @@ class MiscellaneousErrorsTests(TestHelper): try: self.session.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") self.fail("Expected 503 for declaration of unknown exchange type.") - except Closed, e: - self.assertConnectionException(503, e.args[0]) + except SessionException, e: + self.assertEquals(503, e.args[0].error_code) def testDifferentDeclaredType(self): - self.session.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") + self.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") try: - self.session.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") + session = self.conn.session("alternate", 2) + session.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") self.fail("Expected 530 for redeclaration of exchange with different type.") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - #cleanup - other = self.connect() - c2 = other.channel(1) - c2.session_open() - c2.exchange_delete(exchange="test_different_declared_type_exchange") + except SessionException, e: + self.assertEquals(530, e.args[0].error_code) class ExchangeTests(TestHelper): def testHeadersBindNoMatchArg(self): @@ -408,5 +411,5 @@ class ExchangeTests(TestHelper): try: self.session.exchange_bind(queue="q", exchange="amq.match", arguments={"name":"fred" , "age":3} ) self.fail("Expected failure for missing x-match arg.") - except Closed, e: - self.assertConnectionException(541, e.args[0]) + except SessionException, e: + self.assertEquals(541, e.args[0].error_code) -- cgit v1.2.1 From 41519ccf105e29b5762f1e3b724a015a50128150 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 11 Mar 2008 14:29:17 +0000 Subject: added convenience API for turning on logging; added logging for controls and commands; made logging prettier git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635939 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 11 ++++++----- qpid/python/qpid/assembler.py | 4 ++-- qpid/python/qpid/connection010.py | 8 ++++++-- qpid/python/qpid/datatypes.py | 4 ++-- qpid/python/qpid/delegates.py | 10 +++++++--- qpid/python/qpid/framer.py | 8 ++++---- qpid/python/qpid/log.py | 28 ++++++++++++++++++++++++++++ qpid/python/qpid/session.py | 11 +++++++++-- qpid/python/run-tests | 8 ++++---- qpid/python/server010 | 10 +++++----- 10 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 qpid/python/qpid/log.py (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world index 83e7c87675..a15817cd9d 100755 --- a/qpid/python/hello-010-world +++ b/qpid/python/hello-010-world @@ -1,18 +1,19 @@ #!/usr/bin/env python -import sys, logging +import sys from qpid.connection010 import Connection from qpid.spec010 import load from qpid.util import connect from qpid.datatypes import Message, RangedSet +from qpid.log import enable, DEBUG, WARN if "-v" in sys.argv: - level = logging.DEBUG + level = DEBUG else: - level = logging.WARN + level = WARN -format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" -logging.basicConfig(level=level, format=format, datefmt='%H:%M:%S') +enable("qpid.io.ctl", level) +enable("qpid.io.cmd", level) spec = load("../specs/amqp.0-10.xml") conn = Connection(connect("0.0.0.0", spec.port), spec) diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py index 9f8bc4bd8a..92bb0aa0f8 100644 --- a/qpid/python/qpid/assembler.py +++ b/qpid/python/qpid/assembler.py @@ -89,7 +89,7 @@ class Assembler(Framer): if frame.isLastFrame(): self.fragments.pop(key) - log.debug("RECV: %s", seg) + log.debug("RECV %s", seg) return seg def write_segment(self, segment): @@ -115,4 +115,4 @@ class Assembler(Framer): payload) self.write_frame(frame) - log.debug("SENT: %s", segment) + log.debug("SENT %s", segment) diff --git a/qpid/python/qpid/connection010.py b/qpid/python/qpid/connection010.py index 1235476b82..59aa41c88d 100644 --- a/qpid/python/qpid/connection010.py +++ b/qpid/python/qpid/connection010.py @@ -27,6 +27,7 @@ from session import Session from invoker import Invoker from spec010 import Control, Command from exceptions import * +from logging import getLogger import delegates class ChannelBusy(Exception): pass @@ -149,6 +150,8 @@ class Connection(Assembler): def __repr__(self): return str(self) +log = getLogger("qpid.io.ctl") + class Channel(Invoker): def __init__(self, connection, id): @@ -164,11 +167,12 @@ class Channel(Invoker): return None def invoke(self, type, args, kwargs): - cntrl = type.new(args, kwargs) + ctl = type.new(args, kwargs) sc = StringCodec(self.connection.spec) - sc.write_control(cntrl) + sc.write_control(ctl) self.connection.write_segment(Segment(True, True, type.segment_type, type.track, self.id, sc.encoded)) + log.debug("SENT %s", ctl) def __str__(self): return "%s[%s]" % (self.connection, self.id) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 45944a2494..96ef94de8a 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -114,7 +114,7 @@ class Range: return Range(min(self.lower, r.lower), max(self.upper, r.upper)) def __repr__(self): - return "Range(%s, %s)" % (self.lower, self.upper) + return "%s-%s" % (self.lower, self.upper) class RangedSet: @@ -147,7 +147,7 @@ class RangedSet: self.add_range(Range(lower, upper)) def __repr__(self): - return "RangedSet(%s)" % str(self.ranges) + return str(self.ranges) class Future: def __init__(self, initial=None, exception=Exception): diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 8de7141962..9df2cd04bd 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -20,6 +20,9 @@ import os, connection010, session from util import notify from datatypes import RangedSet +from logging import getLogger + +log = getLogger("qpid.io.ctl") class Delegate: @@ -37,9 +40,10 @@ class Delegate: ch = ssn.channel if seg.track == self.control: - cntrl = seg.decode(self.spec) - attr = cntrl._type.qname.replace(".", "_") - getattr(self, attr)(ch, cntrl) + ctl = seg.decode(self.spec) + log.debug("RECV %s", ctl) + attr = ctl._type.qname.replace(".", "_") + getattr(self, attr)(ch, ctl) elif ssn is None: ch.session_detached() else: diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index 3cc200e3da..11fe385d46 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -88,7 +88,7 @@ class Framer(Packer): raise Closed() else: continue - raw.debug("SENT: %r", buf[:n]) + raw.debug("SENT %r", buf[:n]) buf = buf[n:] def read(self, n): @@ -109,7 +109,7 @@ class Framer(Packer): if len(s) == 0: raise Closed() data += s - raw.debug("RECV: %r", s) + raw.debug("RECV %r", s) return data def read_header(self): @@ -125,7 +125,7 @@ class Framer(Packer): self.write(frame.payload) # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ self.write("\xCE") - frm.debug("SENT: %s", frame) + frm.debug("SENT %s", frame) def read_frame(self): flags, type, size, track, channel = self.unpack(Frame.HEADER) @@ -136,5 +136,5 @@ class Framer(Packer): if end != "\xCE": raise FramingError() frame = Frame(flags, type, track, channel, payload) - frm.debug("RECV: %s", frame) + frm.debug("RECV %s", frame) return frame diff --git a/qpid/python/qpid/log.py b/qpid/python/qpid/log.py new file mode 100644 index 0000000000..1fd7d74136 --- /dev/null +++ b/qpid/python/qpid/log.py @@ -0,0 +1,28 @@ +# +# 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. +# + +from logging import getLogger, StreamHandler, Formatter +from logging import DEBUG, INFO, WARN, ERROR, CRITICAL + +def enable(name=None, level=WARN, file=None): + log = getLogger(name) + handler = StreamHandler(file) + handler.setFormatter(Formatter("%(asctime)s %(levelname)s %(message)s")) + log.addHandler(handler) + log.setLevel(level) diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 8374b8cd3d..4da6f883b4 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -28,6 +28,8 @@ from util import wait, notify from exceptions import * from logging import getLogger +log = getLogger("qpid.io.cmd") + class SessionDetached(Exception): pass def client(*args): @@ -142,6 +144,8 @@ class Session(Invoker): self.send(seg) + log.debug("SENT %s %s %s", seg.id, hdr, cmd) + if message != None: if message.headers != None: sc = StringCodec(self.spec) @@ -175,7 +179,10 @@ class Session(Invoker): def dispatch(self, assembly): segments = assembly[:] + hdr, cmd = assembly.pop(0).decode(self.spec) + log.debug("RECV %s %s %s", cmd.id, hdr, cmd) + args = [] for st in cmd._type.segments: @@ -291,7 +298,7 @@ class Delegate: finally: self.session.lock.release() -msg = getLogger("qpid.ssn.msg") +msg = getLogger("qpid.io.msg") class Client(Delegate): @@ -301,5 +308,5 @@ class Client(Delegate): m.id = cmd.id messages = self.session.incoming(cmd.destination) messages.put(m) - msg.debug("RECV: %s", m) + msg.debug("RECV %s", m) return INCOMPLETE diff --git a/qpid/python/run-tests b/qpid/python/run-tests index 07162efd15..84b76ebfc1 100755 --- a/qpid/python/run-tests +++ b/qpid/python/run-tests @@ -20,14 +20,14 @@ import sys, logging from qpid.testlib import testrunner +from qpid.log import enable, WARN, DEBUG if "-vv" in sys.argv: - level = logging.DEBUG + level = DEBUG else: - level = logging.WARN + level = WARN -format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" -logging.basicConfig(level=level, format=format, datefmt='%H:%M:%S') +enable("qpid", level) if not testrunner.run(): sys.exit(1) diff --git a/qpid/python/server010 b/qpid/python/server010 index 7b4b5762ca..0a75e2534e 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -6,16 +6,16 @@ from qpid.util import connect, listen from qpid.spec010 import load from qpid.session import Client from qpid.datatypes import Message +from qpid.log import enable, DEBUG, WARN -import logging, sys +import sys if "-v" in sys.argv: - level = logging.DEBUG + level = DEBUG else: - level = logging.WARN + level = WARN -format = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" -logging.basicConfig(level=level, format=format, datefmt='%H:%M:%S') +enable("qpid", level) spec = load("../specs/amqp.0-10.xml") -- cgit v1.2.1 From ed859ae39f0ac5cadf2e82f434dbfcfc4408c5a9 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 11 Mar 2008 15:34:20 +0000 Subject: Fixed headers exchange to allow unbind using binding key and not args Converted alternate exchange python tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635976 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 7 -- qpid/python/qpid/datatypes.py | 2 +- qpid/python/tests_0-10/alternate_exchange.py | 137 +++++++++++++-------------- qpid/python/tests_0-10/exchange.py | 1 + qpid/python/tests_0-10/queue.py | 15 ++- 5 files changed, 77 insertions(+), 85 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 8b0e11b3f0..20bc77c504 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,15 +1,10 @@ tests.codec.FieldTableTestCase.test_field_table_decode tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair -tests_0-10.query.QueryTests.test_exchange_bound_header tests_0-10.tx.TxTests.test_auto_rollback tests_0-10.tx.TxTests.test_commit tests_0-10.tx.TxTests.test_rollback tests_0-10.execution.ExecutionTests.test_flush -tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_exchange -tests_0-10.alternate_exchange.AlternateExchangeTests.test_delete_while_used_by_queue -tests_0-10.alternate_exchange.AlternateExchangeTests.test_queue_delete -tests_0-10.alternate_exchange.AlternateExchangeTests.test_unroutable tests_0-10.dtx.DtxTests.test_bad_resume tests_0-10.dtx.DtxTests.test_end tests_0-10.dtx.DtxTests.test_end_suspend_and_fail @@ -46,5 +41,3 @@ tests_0-10.testlib.TestBaseTest.testAssertEmptyPass tests_0-10.testlib.TestBaseTest.testMessageProperties tests_0-10.queue.QueueTests.test_autodelete_shared tests_0-10.queue.QueueTests.test_declare_exclusive -tests_0-10.queue.QueueTests.test_bind -tests_0-10.queue.QueueTests.test_unbind_headers diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 96ef94de8a..59299b6e04 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -44,7 +44,7 @@ class Struct: if kwargs: unexpected = kwargs.keys()[0] - raise TypeError("%s() got an unexpected keywoard argument '%s'" % + raise TypeError("%s() got an unexpected keyword argument '%s'" % (_type.name, unexpected)) def __getitem__(self, name): diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index 225b3cfb69..418a8c90ee 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -16,12 +16,13 @@ # specific language governing permissions and limitations # under the License. # -from qpid.client import Client, Closed +import traceback from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.datatypes import Message +from qpid.testlib import TestBase010 +from qpid.session import SessionException -class AlternateExchangeTests(TestBase): +class AlternateExchangeTests(TestBase010): """ Tests for the new mechanism for message returns introduced in 0-10 and available in 0-9 for preview @@ -31,36 +32,42 @@ class AlternateExchangeTests(TestBase): """ Test that unroutable messages are delivered to the alternate-exchange if specified """ - channel = self.channel + session = self.session #create an exchange with an alternate defined - channel.exchange_declare(exchange="secondary", type="fanout") - channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") + session.exchange_declare(exchange="secondary", type="fanout") + session.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages - channel.queue_declare(queue="returns", exclusive=True, auto_delete=True) - channel.queue_bind(queue="returns", exchange="secondary") - self.subscribe(destination="a", queue="returns") - returned = self.client.queue("a") + session.queue_declare(queue="returns", exclusive=True, auto_delete=True) + session.exchange_bind(queue="returns", exchange="secondary") + session.message_subscribe(destination="a", queue="returns") + session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=1, value=0xFFFFFFFF) + returned = session.incoming("a") #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages - channel.queue_declare(queue="processed", exclusive=True, auto_delete=True) - channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") - self.subscribe(destination="b", queue="processed") - processed = self.client.queue("b") + session.queue_declare(queue="processed", exclusive=True, auto_delete=True) + session.exchange_bind(queue="processed", exchange="primary", binding_key="my-key") + session.message_subscribe(destination="b", queue="processed") + session.message_flow(destination="b", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="b", unit=1, value=0xFFFFFFFF) + processed = session.incoming("b") #publish to the primary exchange #...one message that makes it to the 'processed' queue: - channel.message_transfer(destination="primary", content=Content("Good", properties={'routing_key':"my-key"})) + dp=self.session.delivery_properties(routing_key="my-key") + session.message_transfer(destination="primary", message=Message(dp, "Good")) #...and one that does not: - channel.message_transfer(destination="primary", content=Content("Bad", properties={'routing_key':"unused-key"})) + dp=self.session.delivery_properties(routing_key="unused-key") + session.message_transfer(destination="primary", message=Message(dp, "Bad")) #delete the exchanges - channel.exchange_delete(exchange="primary") - channel.exchange_delete(exchange="secondary") + session.exchange_delete(exchange="primary") + session.exchange_delete(exchange="secondary") #verify behaviour - self.assertEqual("Good", processed.get(timeout=1).content.body) - self.assertEqual("Bad", returned.get(timeout=1).content.body) + self.assertEqual("Good", processed.get(timeout=1).body) + self.assertEqual("Bad", returned.get(timeout=1).body) self.assertEmpty(processed) self.assertEmpty(returned) @@ -68,29 +75,32 @@ class AlternateExchangeTests(TestBase): """ Test that messages in a queue being deleted are delivered to the alternate-exchange if specified """ - channel = self.channel + session = self.session #set up a 'dead letter queue': - channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="deleted", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="dlq", queue="deleted") - self.subscribe(destination="dlq", queue="deleted") - dlq = self.client.queue("dlq") + session.exchange_declare(exchange="dlq", type="fanout") + session.queue_declare(queue="deleted", exclusive=True, auto_delete=True) + session.exchange_bind(exchange="dlq", queue="deleted") + session.message_subscribe(destination="dlq", queue="deleted") + session.message_flow(destination="dlq", unit=0, value=0xFFFFFFFF) + session.message_flow(destination="dlq", unit=1, value=0xFFFFFFFF) + dlq = session.incoming("dlq") #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="delete-me", alternate_exchange="dlq") + session.queue_declare(queue="delete-me", alternate_exchange="dlq") #send it some messages: - channel.message_transfer(content=Content("One", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("Two", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("Three", properties={'routing_key':"delete-me"})) + dp=self.session.delivery_properties(routing_key="delete-me") + session.message_transfer(message=Message(dp, "One")) + session.message_transfer(message=Message(dp, "Two")) + session.message_transfer(message=Message(dp, "Three")) #delete it: - channel.queue_delete(queue="delete-me") + session.queue_delete(queue="delete-me") #delete the dlq exchange: - channel.exchange_delete(exchange="dlq") + session.exchange_delete(exchange="dlq") #check the messages were delivered to the dlq: - self.assertEqual("One", dlq.get(timeout=1).content.body) - self.assertEqual("Two", dlq.get(timeout=1).content.body) - self.assertEqual("Three", dlq.get(timeout=1).content.body) + self.assertEqual("One", dlq.get(timeout=1).body) + self.assertEqual("Two", dlq.get(timeout=1).body) + self.assertEqual("Three", dlq.get(timeout=1).body) self.assertEmpty(dlq) def test_delete_while_used_by_queue(self): @@ -98,23 +108,19 @@ class AlternateExchangeTests(TestBase): Ensure an exchange still in use as an alternate-exchange for a queue can't be deleted """ - channel = self.channel - channel.exchange_declare(exchange="alternate", type="fanout") - channel.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") + session = self.session + session.exchange_declare(exchange="alternate", type="fanout") + + session = self.conn.session("alternate", 2) + session.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") try: - channel.exchange_delete(exchange="alternate") + session.exchange_delete(exchange="alternate") self.fail("Expected deletion of in-use alternate-exchange to fail") - except Closed, e: - #cleanup: - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="alternate") - channel.session_close() - other.close() - - self.assertConnectionException(530, e.args[0]) - + except SessionException, e: + session = self.session + session.queue_delete(queue="q") + session.exchange_delete(exchange="alternate") + self.assertEquals(530, e.args[0].error_code) def test_delete_while_used_by_exchange(self): @@ -122,25 +128,19 @@ class AlternateExchangeTests(TestBase): Ensure an exchange still in use as an alternate-exchange for another exchange can't be deleted """ - channel = self.channel - channel.exchange_declare(exchange="alternate", type="fanout") - channel.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") + session = self.session + session.exchange_declare(exchange="alternate", type="fanout") + + session = self.conn.session("alternate", 2) + session.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") try: - channel.exchange_delete(exchange="alternate") - #cleanup: - channel.exchange_delete(exchange="e") + session.exchange_delete(exchange="alternate") self.fail("Expected deletion of in-use alternate-exchange to fail") - except Closed, e: - #cleanup: - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="e") - channel.exchange_delete(exchange="alternate") - channel.session_close() - other.close() - - self.assertConnectionException(530, e.args[0]) + except SessionException, e: + session = self.session + session.exchange_delete(exchange="e") + session.exchange_delete(exchange="alternate") + self.assertEquals(530, e.args[0].error_code) def assertEmpty(self, queue): @@ -148,4 +148,3 @@ class AlternateExchangeTests(TestBase): msg = queue.get(timeout=1) self.fail("Queue not empty: " + msg) except Empty: None - diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index be4f72c74c..991da17ed4 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -45,6 +45,7 @@ class TestHelper(TestBase010): except: print "Error on tearDown:" print traceback.print_exc() + TestBase010.tearDown(self) def createMessage(self, key="", body=""): return Message(self.session.delivery_properties(routing_key=key), body) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index bacbc60bc1..39305d4554 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -126,27 +126,26 @@ class QueueTests(TestBase010): session.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) #straightforward case, both exchange & queue exist so no errors expected: - session.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") + session.exchange_bind(queue="queue-1", exchange="amq.direct", binding_key="key1") #use the queue name where the routing key is not specified: session.exchange_bind(queue="queue-1", exchange="amq.direct") #try and bind to non-existant exchange try: - session.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") + session.exchange_bind(queue="queue-1", exchange="an-invalid-exchange", binding_key="key1") self.fail("Expected bind to non-existant exchange to fail") - except Closed, e: + except SessionException, e: self.assertEquals(404, e.args[0].error_code) - #need to reopen a session: - session = self.client.session(2) - session.session_open() + def test_bind_queue_existence(self): + session = self.session #try and bind non-existant queue: try: - session.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") + session.exchange_bind(queue="queue-2", exchange="amq.direct", binding_key="key1") self.fail("Expected bind of non-existant queue to fail") - except Closed, e: + except SessionException, e: self.assertEquals(404, e.args[0].error_code) def test_unbind_direct(self): -- cgit v1.2.1 From 0f082126600ed881992b55456e6b8b4c625512a8 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 11 Mar 2008 16:21:44 +0000 Subject: Converted some more tests git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@635995 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 2 -- qpid/python/tests_0-10/message.py | 32 +++++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 20bc77c504..be61fed0a2 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -29,8 +29,6 @@ tests_0-10.dtx.DtxTests.test_suspend_start_end_resume tests_0-10.message.MessageTests.test_consume_exclusive tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward -tests_0-10.message.MessageTests.test_consume_queue_errors -tests_0-10.message.MessageTests.test_consume_unique_consumers tests_0-10.message.MessageTests.test_no_size tests_0-10.message.MessageTests.test_qos_prefetch_count tests_0-10.message.MessageTests.test_qos_prefetch_size diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index dd80e79d36..d176962b8b 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -18,10 +18,12 @@ # from qpid.client import Client, Closed from qpid.queue import Empty -from qpid.content import Content - from qpid.testlib import TestBase010 from qpid.datatypes import Message, RangedSet +from qpid.session import SessionException + +from qpid.content import Content + class MessageTests(TestBase010): """Tests for 'methods' on the amqp message 'class'""" @@ -120,26 +122,26 @@ class MessageTests(TestBase010): except Closed, e: self.assertChannelException(403, e.args[0]) - def test_consume_queue_errors(self): + def test_consume_queue_not_found(self): """ Test error conditions associated with the queue field of the consume method: """ session = self.session try: #queue specified but doesn't exist: - self.subscribe(queue="invalid-queue", destination="") + session.message_subscribe(queue="invalid-queue", destination="a") self.fail("Expected failure when consuming from non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) - session = self.client.session(2) - session.session_open() + def test_consume_queue_not_specified(self): + session = self.session try: #queue not specified and none previously declared for channel: - self.subscribe(session, queue="", destination="") + session.message_subscribe(destination="a") self.fail("Expected failure when consuming from unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) + except SessionException, e: + self.assertEquals(531, e.args[0].error_code) def test_consume_unique_consumers(self): """ @@ -150,12 +152,12 @@ class MessageTests(TestBase010): session.queue_declare(queue="test-queue-3", exclusive=True, auto_delete=True) #check that attempts to use duplicate tags are detected and prevented: - self.subscribe(destination="first", queue="test-queue-3") + session.message_subscribe(destination="first", queue="test-queue-3") try: - self.subscribe(destination="first", queue="test-queue-3") + session.message_subscribe(destination="first", queue="test-queue-3") self.fail("Expected consume request to fail due to non-unique tag") - except Closed, e: - self.assertConnectionException(530, e.args[0]) + except SessionException, e: + self.assertEquals(530, e.args[0].error_code) def test_cancel(self): """ -- cgit v1.2.1 From eea2a4ffb9682247360416437755059bce77afc3 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 11 Mar 2008 21:56:49 +0000 Subject: Enabled tx methods on final 0-10 path and converted tests accordingly Added read/write- uuid to codec010 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@636121 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 4 - qpid/python/qpid/codec010.py | 6 ++ qpid/python/qpid/testlib.py | 6 ++ qpid/python/tests_0-10/message.py | 32 +++--- qpid/python/tests_0-10/tx.py | 218 ++++++++++++++++++++------------------ 5 files changed, 143 insertions(+), 123 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index be61fed0a2..8ae41b1d87 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,9 +1,6 @@ tests.codec.FieldTableTestCase.test_field_table_decode tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair -tests_0-10.tx.TxTests.test_auto_rollback -tests_0-10.tx.TxTests.test_commit -tests_0-10.tx.TxTests.test_rollback tests_0-10.execution.ExecutionTests.test_flush tests_0-10.dtx.DtxTests.test_bad_resume tests_0-10.dtx.DtxTests.test_end @@ -26,7 +23,6 @@ tests_0-10.dtx.DtxTests.test_start_join tests_0-10.dtx.DtxTests.test_start_join_and_resume tests_0-10.dtx.DtxTests.test_suspend_resume tests_0-10.dtx.DtxTests.test_suspend_start_end_resume -tests_0-10.message.MessageTests.test_consume_exclusive tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward tests_0-10.message.MessageTests.test_no_size diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 60adbdc2e4..0e4244fb75 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -221,6 +221,12 @@ class Codec(Packer): attr = "write_uint%d" % (width*8) getattr(self, attr)(n) + def write_uuid(self, s): + self.pack("16s", s) + + def read_uuid(self): + return self.unpack("16s") + class StringCodec(Codec): diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 7e5a2a6b66..f633c4e77d 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -352,6 +352,12 @@ class TestBase010(unittest.TestCase): self.conn.start(timeout=10) self.session = self.conn.session("test-session", timeout=10) + def connect(self): + spec = testrunner.spec + conn = Connection(connect(testrunner.host, testrunner.port), spec) + conn.start(timeout=10) + return conn + def tearDown(self): if not self.session.error(): self.session.close(timeout=10) self.conn.close(timeout=10) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index d176962b8b..df9fde1593 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -96,31 +96,29 @@ class MessageTests(TestBase010): def test_consume_exclusive(self): """ - Test that the exclusive flag is honoured in the consume method + Test an exclusive consumer prevents other consumer being created """ session = self.session - #setup, declare a queue: session.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) - - #check that an exclusive consumer prevents other consumer being created: - self.subscribe(destination="first", queue="test-queue-2", exclusive=True) + session.message_subscribe(destination="first", queue="test-queue-2", exclusive=True) try: - self.subscribe(destination="second", queue="test-queue-2") + session.message_subscribe(destination="second", queue="test-queue-2") self.fail("Expected consume request to fail due to previous exclusive consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - #open new session and cleanup last consumer: - session = self.client.session(2) - session.session_open() + except SessionException, e: + self.assertEquals(403, e.args[0].error_code) - #check that an exclusive consumer cannot be created if a consumer already exists: - self.subscribe(session, destination="first", queue="test-queue-2") + def test_consume_exclusive2(self): + """ + Check that an exclusive consumer cannot be created if a consumer already exists: + """ + session = self.session + session.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) + session.message_subscribe(destination="first", queue="test-queue-2") try: - self.subscribe(destination="second", queue="test-queue-2", exclusive=True) + session.message_subscribe(destination="second", queue="test-queue-2", exclusive=True) self.fail("Expected exclusive consume request to fail due to previous consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) + except SessionException, e: + self.assertEquals(403, e.args[0].error_code) def test_consume_queue_not_found(self): """ diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 3fd1065af3..59298bad1b 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -18,10 +18,10 @@ # from qpid.client import Client, Closed from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.datatypes import Message, RangedSet +from qpid.testlib import testrunner, TestBase010 -class TxTests(TestBase): +class TxTests(TestBase010): """ Tests for 'methods' on the amqp tx 'class' """ @@ -30,202 +30,216 @@ class TxTests(TestBase): """ Test that commited publishes are delivered and commited acks are not re-delivered """ - channel2 = self.client.channel(2) - channel2.session_open() - self.perform_txn_work(channel2, "tx-commit-a", "tx-commit-b", "tx-commit-c") - channel2.tx_commit() - channel2.session_close() + session2 = self.conn.session("worker", 2) + self.perform_txn_work(session2, "tx-commit-a", "tx-commit-b", "tx-commit-c") + session2.tx_commit() + session2.close() - #use a different channel with new subscriptions to ensure + #use a different session with new subscriptions to ensure #there is no redelivery of acked messages: - channel = self.channel - channel.tx_select() + session = self.session + session.tx_select() - self.subscribe(channel, queue="tx-commit-a", destination="qa", confirm_mode=1) - queue_a = self.client.queue("qa") + self.subscribe(session, queue="tx-commit-a", destination="qa") + queue_a = session.incoming("qa") - self.subscribe(channel, queue="tx-commit-b", destination="qb", confirm_mode=1) - queue_b = self.client.queue("qb") + self.subscribe(session, queue="tx-commit-b", destination="qb") + queue_b = session.incoming("qb") - self.subscribe(channel, queue="tx-commit-c", destination="qc", confirm_mode=1) - queue_c = self.client.queue("qc") + self.subscribe(session, queue="tx-commit-c", destination="qc") + queue_c = session.incoming("qc") #check results for i in range(1, 5): msg = queue_c.get(timeout=1) - self.assertEqual("TxMessage %d" % i, msg.content.body) - msg.complete() + self.assertEqual("TxMessage %d" % i, msg.body) + session.message_accept(RangedSet(msg.id)) msg = queue_b.get(timeout=1) - self.assertEqual("TxMessage 6", msg.content.body) - msg.complete() + self.assertEqual("TxMessage 6", msg.body) + session.message_accept(RangedSet(msg.id)) msg = queue_a.get(timeout=1) - self.assertEqual("TxMessage 7", msg.content.body) - msg.complete() + self.assertEqual("TxMessage 7", msg.body) + session.message_accept(RangedSet(msg.id)) for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup - channel.tx_commit() + session.tx_commit() def test_auto_rollback(self): """ - Test that a channel closed with an open transaction is effectively rolled back + Test that a session closed with an open transaction is effectively rolled back """ - channel2 = self.client.channel(2) - channel2.session_open() - queue_a, queue_b, queue_c = self.perform_txn_work(channel2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") + session2 = self.conn.session("worker", 2) + queue_a, queue_b, queue_c, ignore = self.perform_txn_work(session2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None - channel2.session_close() - channel = self.channel - channel.tx_select() + session2.close() - self.subscribe(channel, queue="tx-autorollback-a", destination="qa", confirm_mode=1) - queue_a = self.client.queue("qa") + session = self.session + session.tx_select() - self.subscribe(channel, queue="tx-autorollback-b", destination="qb", confirm_mode=1) - queue_b = self.client.queue("qb") + self.subscribe(session, queue="tx-autorollback-a", destination="qa") + queue_a = session.incoming("qa") - self.subscribe(channel, queue="tx-autorollback-c", destination="qc", confirm_mode=1) - queue_c = self.client.queue("qc") + self.subscribe(session, queue="tx-autorollback-b", destination="qb") + queue_b = session.incoming("qb") + + self.subscribe(session, queue="tx-autorollback-c", destination="qc") + queue_c = session.incoming("qc") #check results for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - msg.complete() + self.assertEqual("Message %d" % i, msg.body) + session.message_accept(RangedSet(msg.id)) msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - msg.complete() + self.assertEqual("Message 6", msg.body) + session.message_accept(RangedSet(msg.id)) msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - msg.complete() + self.assertEqual("Message 7", msg.body) + session.message_accept(RangedSet(msg.id)) for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup - channel.tx_commit() + session.tx_commit() def test_rollback(self): """ Test that rolled back publishes are not delivered and rolled back acks are re-delivered """ - channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") + session = self.session + queue_a, queue_b, queue_c, consumed = self.perform_txn_work(session, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None - #stop subscriptions (ensures no delivery occurs during rollback as messages are requeued) - for d in ["sub_a", "sub_b", "sub_c"]: - channel.message_stop(destination=d) - - channel.tx_rollback() + session.tx_rollback() - #restart susbcriptions - for d in ["sub_a", "sub_b", "sub_c"]: - channel.message_flow(destination=d, unit=0, value=0xFFFFFFFF) - channel.message_flow(destination=d, unit=1, value=0xFFFFFFFF) + #need to release messages to get them redelivered now: + session.message_release(consumed) #check results for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - msg.complete() + self.assertEqual("Message %d" % i, msg.body) + session.message_accept(RangedSet(msg.id)) msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - msg.complete() + self.assertEqual("Message 6", msg.body) + session.message_accept(RangedSet(msg.id)) msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - msg.complete() + self.assertEqual("Message 7", msg.body) + session.message_accept(RangedSet(msg.id)) for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) + self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup - channel.tx_commit() + session.tx_commit() - def perform_txn_work(self, channel, name_a, name_b, name_c): + def perform_txn_work(self, session, name_a, name_b, name_c): """ Utility method that does some setup and some work under a transaction. Used for testing both commit and rollback """ #setup: - channel.queue_declare(queue=name_a, exclusive=True, auto_delete=True) - channel.queue_declare(queue=name_b, exclusive=True, auto_delete=True) - channel.queue_declare(queue=name_c, exclusive=True, auto_delete=True) + session.queue_declare(queue=name_a, exclusive=True, auto_delete=True) + session.queue_declare(queue=name_b, exclusive=True, auto_delete=True) + session.queue_declare(queue=name_c, exclusive=True, auto_delete=True) key = "my_key_" + name_b topic = "my_topic_" + name_c - channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) - channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) + session.exchange_bind(queue=name_b, exchange="amq.direct", binding_key=key) + session.exchange_bind(queue=name_c, exchange="amq.topic", binding_key=topic) + dp = session.delivery_properties(routing_key=name_a) for i in range(1, 5): - channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"msg%d" % i}, body="Message %d" % i)) + mp = session.message_properties(message_id="msg%d" % i) + session.message_transfer(message=Message(dp, mp, "Message %d" % i)) + + dp = session.delivery_properties(routing_key=key) + mp = session.message_properties(message_id="msg6") + session.message_transfer(destination="amq.direct", message=Message(dp, mp, "Message 6")) - channel.message_transfer(destination="amq.direct", - content=Content(properties={'routing_key':key, 'message_id':"msg6"}, body="Message 6")) - channel.message_transfer(destination="amq.topic", - content=Content(properties={'routing_key':topic, 'message_id':"msg7"}, body="Message 7")) + dp = session.delivery_properties(routing_key=topic) + mp = session.message_properties(message_id="msg7") + session.message_transfer(destination="amq.topic", message=Message(dp, mp, "Message 7")) - channel.tx_select() + session.tx_select() #consume and ack messages - self.subscribe(channel, queue=name_a, destination="sub_a", confirm_mode=1) - queue_a = self.client.queue("sub_a") + acked = RangedSet() + self.subscribe(session, queue=name_a, destination="sub_a") + queue_a = session.incoming("sub_a") for i in range(1, 5): msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) + acked.add(msg.id) + self.assertEqual("Message %d" % i, msg.body) - msg.complete() - - self.subscribe(channel, queue=name_b, destination="sub_b", confirm_mode=1) - queue_b = self.client.queue("sub_b") + self.subscribe(session, queue=name_b, destination="sub_b") + queue_b = session.incoming("sub_b") msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - msg.complete() + self.assertEqual("Message 6", msg.body) + acked.add(msg.id) - sub_c = self.subscribe(channel, queue=name_c, destination="sub_c", confirm_mode=1) - queue_c = self.client.queue("sub_c") + sub_c = self.subscribe(session, queue=name_c, destination="sub_c") + queue_c = session.incoming("sub_c") msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - msg.complete() + self.assertEqual("Message 7", msg.body) + acked.add(msg.id) + + session.message_accept(acked) + dp = session.delivery_properties(routing_key=topic) #publish messages for i in range(1, 5): - channel.message_transfer(destination="amq.topic", - content=Content(properties={'routing_key':topic, 'message_id':"tx-msg%d" % i}, - body="TxMessage %d" % i)) - - channel.message_transfer(destination="amq.direct", - content=Content(properties={'routing_key':key, 'message_id':"tx-msg6"}, - body="TxMessage 6")) - channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"tx-msg7"}, - body="TxMessage 7")) - return queue_a, queue_b, queue_c + mp = session.message_properties(message_id="tx-msg%d" % i) + session.message_transfer(destination="amq.topic", message=Message(dp, mp, "TxMessage %d" % i)) + + dp = session.delivery_properties(routing_key=key) + mp = session.message_properties(message_id="tx-msg6") + session.message_transfer(destination="amq.direct", message=Message(dp, mp, "TxMessage 6")) + + dp = session.delivery_properties(routing_key=name_a) + mp = session.message_properties(message_id="tx-msg7") + session.message_transfer(message=Message(dp, mp, "TxMessage 7")) + return queue_a, queue_b, queue_c, acked + + def subscribe(self, session=None, **keys): + session = session or self.session + consumer_tag = keys["destination"] + session.message_subscribe(**keys) + session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + + def complete(self, session, msg): + session.receiver._completed.add(msg.id)#TODO: this may be done automatically + session.channel.session_completed(session.receiver._completed) + -- cgit v1.2.1 From 59a860e074cc38f43b56fe63ff93bd05648dd58f Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 17 Mar 2008 12:17:55 +0000 Subject: Scope exclusive queues to sessions. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@637854 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 2 -- qpid/python/tests_0-10/alternate_exchange.py | 7 ++--- qpid/python/tests_0-10/queue.py | 25 +++++++-------- qpid/python/tests_0-10/tx.py | 46 ++++++++++++++++++++-------- 4 files changed, 47 insertions(+), 33 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 8ae41b1d87..70cd3d19ed 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -33,5 +33,3 @@ tests_0-10.message.MessageTests.test_recover_requeue tests_0-10.testlib.TestBaseTest.testAssertEmptyFail tests_0-10.testlib.TestBaseTest.testAssertEmptyPass tests_0-10.testlib.TestBaseTest.testMessageProperties -tests_0-10.queue.QueueTests.test_autodelete_shared -tests_0-10.queue.QueueTests.test_declare_exclusive diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index 418a8c90ee..c177c3deb7 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -111,14 +111,13 @@ class AlternateExchangeTests(TestBase010): session = self.session session.exchange_declare(exchange="alternate", type="fanout") - session = self.conn.session("alternate", 2) - session.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") + session2 = self.conn.session("alternate", 2) + session2.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") try: - session.exchange_delete(exchange="alternate") + session2.exchange_delete(exchange="alternate") self.fail("Expected deletion of in-use alternate-exchange to fail") except SessionException, e: session = self.session - session.queue_delete(queue="q") session.exchange_delete(exchange="alternate") self.assertEquals(530, e.args[0].error_code) diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 39305d4554..758794dd52 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -86,19 +86,17 @@ class QueueTests(TestBase010): Test that the exclusive field is honoured in queue.declare """ # TestBase.setUp has already opened session(1) - c1 = self.session + s1 = self.session # Here we open a second separate connection: - other = self.connect() - c2 = other.session(1) - c2.session_open() + s2 = self.conn.session("other", 2) #declare an exclusive queue: - c1.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) + s1.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) try: #other connection should not be allowed to declare this: - c2.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) + s2.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) self.fail("Expected second exclusive queue_declare to raise a channel exception") - except Closed, e: + except SessionException, e: self.assertEquals(405, e.args[0].error_code) @@ -322,24 +320,23 @@ class QueueTests(TestBase010): Test auto-deletion (of non-exclusive queues) """ session = self.session - other = self.connect() - session2 = other.session(1) - session2.session_open() + session2 =self.conn.session("other", 1) session.queue_declare(queue="auto-delete-me", auto_delete=True) #consume from both sessions - reply = session.basic_consume(queue="auto-delete-me") - session2.basic_consume(queue="auto-delete-me") + tag = "my-tag" + session.message_subscribe(queue="auto-delete-me", destination=tag) + session2.message_subscribe(queue="auto-delete-me", destination=tag) #implicit cancel - session2.session_close() + session2.close() #check it is still there session.queue_declare(queue="auto-delete-me", passive=True) #explicit cancel => queue is now unused again: - session.basic_cancel(consumer_tag=reply.consumer_tag) + session.message_cancel(destination=tag) #NOTE: this assumes there is no timeout in use diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 59298bad1b..5aef2b00e8 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -30,23 +30,30 @@ class TxTests(TestBase010): """ Test that commited publishes are delivered and commited acks are not re-delivered """ + session = self.session + + #declare queues and create subscribers in the checking session + #to ensure that the queues are not auto-deleted too early: + self.declare_queues(["tx-commit-a", "tx-commit-b", "tx-commit-c"]) + session.message_subscribe(queue="tx-commit-a", destination="qa") + session.message_subscribe(queue="tx-commit-b", destination="qb") + session.message_subscribe(queue="tx-commit-c", destination="qc") + + #use a separate session for actual work session2 = self.conn.session("worker", 2) self.perform_txn_work(session2, "tx-commit-a", "tx-commit-b", "tx-commit-c") session2.tx_commit() session2.close() - #use a different session with new subscriptions to ensure - #there is no redelivery of acked messages: - session = self.session session.tx_select() - self.subscribe(session, queue="tx-commit-a", destination="qa") + self.enable_flow("qa") queue_a = session.incoming("qa") - self.subscribe(session, queue="tx-commit-b", destination="qb") + self.enable_flow("qb") queue_b = session.incoming("qb") - self.subscribe(session, queue="tx-commit-c", destination="qc") + self.enable_flow("qc") queue_c = session.incoming("qc") #check results @@ -76,6 +83,12 @@ class TxTests(TestBase010): """ Test that a session closed with an open transaction is effectively rolled back """ + session = self.session + self.declare_queues(["tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c"]) + session.message_subscribe(queue="tx-autorollback-a", destination="qa") + session.message_subscribe(queue="tx-autorollback-b", destination="qb") + session.message_subscribe(queue="tx-autorollback-c", destination="qc") + session2 = self.conn.session("worker", 2) queue_a, queue_b, queue_c, ignore = self.perform_txn_work(session2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") @@ -87,16 +100,15 @@ class TxTests(TestBase010): session2.close() - session = self.session session.tx_select() - self.subscribe(session, queue="tx-autorollback-a", destination="qa") + self.enable_flow("qa") queue_a = session.incoming("qa") - self.subscribe(session, queue="tx-autorollback-b", destination="qb") + self.enable_flow("qb") queue_b = session.incoming("qb") - self.subscribe(session, queue="tx-autorollback-c", destination="qc") + self.enable_flow("qc") queue_c = session.incoming("qc") #check results @@ -169,9 +181,7 @@ class TxTests(TestBase010): commit and rollback """ #setup: - session.queue_declare(queue=name_a, exclusive=True, auto_delete=True) - session.queue_declare(queue=name_b, exclusive=True, auto_delete=True) - session.queue_declare(queue=name_c, exclusive=True, auto_delete=True) + self.declare_queues([name_a, name_b, name_c]) key = "my_key_" + name_b topic = "my_topic_" + name_c @@ -232,6 +242,11 @@ class TxTests(TestBase010): session.message_transfer(message=Message(dp, mp, "TxMessage 7")) return queue_a, queue_b, queue_c, acked + def declare_queues(self, names, session=None): + session = session or self.session + for n in names: + session.queue_declare(queue=n, auto_delete=True) + def subscribe(self, session=None, **keys): session = session or self.session consumer_tag = keys["destination"] @@ -239,6 +254,11 @@ class TxTests(TestBase010): session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + def enable_flow(self, tag, session=None): + session = session or self.session + session.message_flow(destination=tag, unit=0, value=0xFFFFFFFF) + session.message_flow(destination=tag, unit=1, value=0xFFFFFFFF) + def complete(self, session, msg): session.receiver._completed.add(msg.id)#TODO: this may be done automatically session.channel.session_completed(session.receiver._completed) -- cgit v1.2.1 From dca5c9779cf1671156931a7c50641162908b0ce0 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Sat, 22 Mar 2008 23:05:17 +0000 Subject: Made dom query results addable per QPID-870. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@640117 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/dom.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py index 7c759dbdd5..df2b88322a 100644 --- a/qpid/python/mllib/dom.py +++ b/qpid/python/mllib/dom.py @@ -185,7 +185,24 @@ class Comment(Leaf): ## Query Classes ## ########################################################################### -class View: +class Adder: + + def __add__(self, other): + return Sum(self, other) + +class Sum(Adder): + + def __init__(self, left, right): + self.left = left + self.right = right + + def __iter__(self): + for x in self.left: + yield x + for x in self.right: + yield x + +class View(Adder): def __init__(self, source): self.source = source -- cgit v1.2.1 From 1b2e86e1f3835be232044b82e87c16e1bd5190be Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Tue, 25 Mar 2008 20:30:01 +0000 Subject: QPID-877: applied patch from Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@640970 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/managementdata.py | 8 +- qpid/python/qpid/management.py | 137 ++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 38 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index 5b13594994..adff05a710 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -111,6 +111,10 @@ class ManagementData: finally: self.lock.release () + def ctrlHandler (self, context, op, data): + if op == self.mclient.CTRL_BROKER_INFO: + pass + def configHandler (self, context, className, list, timestamps): self.dataHandler (0, className, list, timestamps); @@ -149,7 +153,7 @@ class ManagementData: self.client.start ({"LOGIN": username, "PASSWORD": password}) self.channel = self.client.channel (1) - self.mclient = managementClient (self.spec, None, self.configHandler, + self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, self.instHandler, self.methodReply) self.mclient.schemaListener (self.schemaHandler) self.mch = managementChannel (self.channel, self.mclient.topicCb, self.mclient.replyCb) @@ -194,6 +198,8 @@ class ManagementData: return "False" else: return "True" + elif typecode == 14: + return str (UUID (bytes=value)) return "*type-error*" def getObjIndex (self, className, config): diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index b5d992cf5d..33679cf0da 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -69,12 +69,14 @@ class managementChannel: opens a session and performs all of the declarations and bindings needed to participate in the management protocol. """ response = ch.session_open (detached_lifetime=300) + self.sessionId = response.session_id self.topicName = "mgmt-" + base64.urlsafe_b64encode (response.session_id) self.replyName = "reply-" + base64.urlsafe_b64encode (response.session_id) self.qpidChannel = ch self.tcb = topicCb self.rcb = replyCb self.context = cbContext + self.reqsOutstanding = 0 ch.queue_declare (queue=self.topicName, exclusive=1, auto_delete=1) ch.queue_declare (queue=self.replyName, exclusive=1, auto_delete=1) @@ -114,6 +116,10 @@ class managementClient: network. It implements the management protocol and manages the management schemas as advertised by the various management agents in the network. """ + CTRL_BROKER_INFO = 1 + CTRL_SCHEMA_LOADED = 2 + CTRL_USER = 3 + #======================================================== # User API - interacts with the class's user #======================================================== @@ -144,7 +150,7 @@ class managementClient: """ Register a new channel. """ self.channels.append (channel) codec = Codec (StringIO (), self.spec) - self.setHeader (codec, ord ('H')) + self.setHeader (codec, ord ('B')) msg = Content (codec.stream.getvalue ()) msg["content_type"] = "application/octet-stream" msg["routing_key"] = "agent" @@ -161,6 +167,22 @@ class managementClient: """ Invoke a method on a managed object. """ self.method (channel, userSequence, objId, className, methodName, args) + def getObjects (self, channel, userSequence, className): + """ Request immediate content from broker """ + codec = Codec (StringIO (), self.spec) + self.setHeader (codec, ord ('G'), userSequence) + ft = {} + ft["_class"] = className + codec.encode_table (ft) + msg = Content (codec.stream.getvalue ()) + msg["content_type"] = "application/octet-stream" + msg["routing_key"] = "agent" + msg["reply_to"] = self.spec.struct ("reply_to") + msg["reply_to"]["exchange_name"] = "amq.direct" + msg["reply_to"]["routing_key"] = channel.replyName + channel.send ("qpid.management", msg) + + #======================================================== # Channel API - interacts with registered channel objects #======================================================== @@ -182,9 +204,11 @@ class managementClient: return if hdr[0] == 'm': - self.handleMethodReply (ch, codec) - elif hdr[0] == 'I': - self.handleInit (ch, codec) + self.handleMethodReply (ch, codec, hdr[1]) + elif hdr[0] == 'z': + self.handleCommandComplete (ch, codec, hdr[1]) + elif hdr[0] == 'b': + self.handleBrokerResponse (ch, codec) elif hdr[0] == 'p': self.handlePackageInd (ch, codec) elif hdr[0] == 'q': @@ -196,14 +220,13 @@ class managementClient: #======================================================== # Internal Functions #======================================================== - def setHeader (self, codec, opcode, cls = 0): + def setHeader (self, codec, opcode, seq = 0): """ Compose the header of a management message. """ codec.encode_octet (ord ('A')) codec.encode_octet (ord ('M')) - codec.encode_octet (ord ('0')) codec.encode_octet (ord ('1')) codec.encode_octet (opcode) - codec.encode_octet (cls) + codec.encode_long (seq) def checkHeader (self, codec): """ Check the header of a management message and extract the opcode and @@ -215,14 +238,11 @@ class managementClient: if octet != 'M': return None octet = chr (codec.decode_octet ()) - if octet != '0': - return None - octet = chr (codec.decode_octet ()) if octet != '1': return None opcode = chr (codec.decode_octet ()) - cls = chr (codec.decode_octet ()) - return (opcode, cls) + seq = codec.decode_long () + return (opcode, seq) def encodeValue (self, codec, value, typecode): """ Encode, into the codec, a value based on its typecode. """ @@ -252,6 +272,8 @@ class managementClient: codec.encode_float (float (value)) elif typecode == 13: # DOUBLE codec.encode_double (double (value)) + elif typecode == 14: # UUID + codec.encode_uuid (value) else: raise ValueError ("Invalid type code: %d" % typecode) @@ -283,14 +305,24 @@ class managementClient: data = codec.decode_float () elif typecode == 13: # DOUBLE data = codec.decode_double () + elif typecode == 14: # UUID + data = codec.decode_uuid () else: raise ValueError ("Invalid type code: %d" % typecode) return data - def handleMethodReply (self, ch, codec): - sequence = codec.decode_long () - status = codec.decode_long () - sText = codec.decode_shortstr () + def incOutstanding (self, ch): + ch.reqsOutstanding = ch.reqsOutstanding + 1 + + def decOutstanding (self, ch): + ch.reqsOutstanding = ch.reqsOutstanding - 1 + if ch.reqsOutstanding == 0: + if self.ctrlCb != None: + self.ctrlCb (ch.context, self.CTRL_SCHEMA_LOADED, None) + + def handleMethodReply (self, ch, codec, sequence): + status = codec.decode_long () + sText = codec.decode_shortstr () data = self.seqMgr.release (sequence) if data == None: @@ -317,15 +349,27 @@ class managementClient: if self.methodCb != None: self.methodCb (ch.context, userSequence, status, sText, args) - def handleInit (self, ch, codec): - len = codec.decode_short () - data = codec.decode_raw (len) + def handleCommandComplete (self, ch, codec, seq): + code = codec.decode_long () + text = codec.decode_shortstr () + data = (seq, code, text) + context = self.seqMgr.release (seq) + if context == "outstanding": + self.decOutstanding (ch) + elif self.ctrlCb != None: + self.ctrlCb (ch.context, self.CTRL_USER, data) + + def handleBrokerResponse (self, ch, codec): if self.ctrlCb != None: - self.ctrlCb (ch.context, len, data) + uuid = codec.decode_uuid () + data = (uuid, ch.sessionId) + self.ctrlCb (ch.context, self.CTRL_BROKER_INFO, data) # Send a package request sendCodec = Codec (StringIO (), self.spec) - self.setHeader (sendCodec, ord ('P')) + seq = self.seqMgr.reserve ("outstanding") + self.setHeader (sendCodec, ord ('P'), seq) + self.incOutstanding (ch) smsg = Content (sendCodec.stream.getvalue ()) smsg["content_type"] = "application/octet-stream" smsg["routing_key"] = "agent" @@ -341,7 +385,9 @@ class managementClient: # Send a class request sendCodec = Codec (StringIO (), self.spec) - self.setHeader (sendCodec, ord ('Q')) + seq = self.seqMgr.reserve ("outstanding") + self.setHeader (sendCodec, ord ('Q'), seq) + self.incOutstanding (ch) sendCodec.encode_shortstr (pname) smsg = Content (sendCodec.stream.getvalue ()) smsg["content_type"] = "application/octet-stream" @@ -362,6 +408,7 @@ class managementClient: # Send a schema request sendCodec = Codec (StringIO (), self.spec) self.setHeader (sendCodec, ord ('S')) + self.incOutstanding (ch) sendCodec.encode_shortstr (pname) sendCodec.encode_shortstr (cname) sendCodec.encode_bin128 (hash) @@ -373,8 +420,9 @@ class managementClient: smsg["reply_to"]["routing_key"] = ch.replyName ch.send ("qpid.management", smsg) - def parseSchema (self, ch, cls, codec): + def parseSchema (self, ch, codec): """ Parse a received schema-description message. """ + self.decOutstanding (ch) packageName = codec.decode_shortstr () className = codec.decode_shortstr () hash = codec.decode_bin128 () @@ -495,7 +543,7 @@ class managementClient: def parseContent (self, ch, cls, codec): """ Parse a received content message. """ - if cls == 'C' and self.configCb == None: + if (cls == 'C' or cls == 'B') and self.configCb == None: return if cls == 'I' and self.instCb == None: return @@ -516,23 +564,39 @@ class managementClient: timestamps.append (codec.decode_longlong ()) # Delete Time schemaClass = self.schema[classKey] - for element in schemaClass[cls][:]: - tc = element[1] - name = element[0] - data = self.decodeValue (codec, tc) - row.append ((name, data)) - - if cls == 'C': + if cls == 'C' or cls == 'B': + for element in schemaClass['C'][:]: + tc = element[1] + name = element[0] + data = self.decodeValue (codec, tc) + row.append ((name, data)) + + if cls == 'I' or cls == 'B': + if cls == 'B': + start = 1 + else: + start = 0 + for element in schemaClass['I'][start:]: + tc = element[1] + name = element[0] + data = self.decodeValue (codec, tc) + row.append ((name, data)) + + if cls == 'C' or cls == 'B': self.configCb (ch.context, classKey, row, timestamps) elif cls == 'I': self.instCb (ch.context, classKey, row, timestamps) - def parse (self, ch, codec, opcode, cls): + def parse (self, ch, codec, opcode, seq): """ Parse a message received from the topic queue. """ if opcode == 's': - self.parseSchema (ch, cls, codec) - elif opcode == 'C': - self.parseContent (ch, cls, codec) + self.parseSchema (ch, codec) + elif opcode == 'c': + self.parseContent (ch, 'C', codec) + elif opcode == 'i': + self.parseContent (ch, 'I', codec) + elif opcode == 'g': + self.parseContent (ch, 'B', codec) else: raise ValueError ("Unknown opcode: %c" % opcode); @@ -540,8 +604,7 @@ class managementClient: """ Invoke a method on an object """ codec = Codec (StringIO (), self.spec) sequence = self.seqMgr.reserve ((userSequence, classId, methodName)) - self.setHeader (codec, ord ('M')) - codec.encode_long (sequence) # Method sequence id + self.setHeader (codec, ord ('M'), sequence) codec.encode_longlong (objId) # ID of object # Encode args according to schema -- cgit v1.2.1 From acbb09499a53795a57d6c59822942ab8de814456 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 26 Mar 2008 18:38:35 +0000 Subject: Update to dtx inline with latest spec: * Updated dtx handling in c++ broker to take account of separation of completion and acceptance. * Added final dtx method defs to extra xml fragment and implemented appropriate handlers in c++ broker. * Converted dtx python tests (recover test still requires some work on decoding arrays). * Allow creation of structs without type codes through a python session method. * Fixed exception handling in python client for commands with results. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@641464 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 20 -- qpid/python/qpid/datatypes.py | 3 +- qpid/python/qpid/session.py | 5 +- qpid/python/qpid/spec010.py | 2 + qpid/python/tests_0-10/dtx.py | 505 +++++++++++++++++++++------------------ 5 files changed, 276 insertions(+), 259 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 70cd3d19ed..58cad3344b 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -2,27 +2,7 @@ tests.codec.FieldTableTestCase.test_field_table_decode tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair tests_0-10.execution.ExecutionTests.test_flush -tests_0-10.dtx.DtxTests.test_bad_resume -tests_0-10.dtx.DtxTests.test_end -tests_0-10.dtx.DtxTests.test_end_suspend_and_fail -tests_0-10.dtx.DtxTests.test_end_unknown_xid -tests_0-10.dtx.DtxTests.test_forget_xid_on_completion -tests_0-10.dtx.DtxTests.test_get_timeout -tests_0-10.dtx.DtxTests.test_implicit_end -tests_0-10.dtx.DtxTests.test_invalid_commit_one_phase_false -tests_0-10.dtx.DtxTests.test_invalid_commit_one_phase_true tests_0-10.dtx.DtxTests.test_recover -tests_0-10.dtx.DtxTests.test_select_required -tests_0-10.dtx.DtxTests.test_set_timeout -tests_0-10.dtx.DtxTests.test_simple_commit -tests_0-10.dtx.DtxTests.test_simple_prepare_commit -tests_0-10.dtx.DtxTests.test_simple_prepare_rollback -tests_0-10.dtx.DtxTests.test_simple_rollback -tests_0-10.dtx.DtxTests.test_start_already_known -tests_0-10.dtx.DtxTests.test_start_join -tests_0-10.dtx.DtxTests.test_start_join_and_resume -tests_0-10.dtx.DtxTests.test_suspend_resume -tests_0-10.dtx.DtxTests.test_suspend_start_end_resume tests_0-10.message.MessageTests.test_consume_no_local tests_0-10.message.MessageTests.test_consume_no_local_awkward tests_0-10.message.MessageTests.test_no_size diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 59299b6e04..0893269174 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -154,6 +154,7 @@ class Future: self.value = initial self._error = None self._set = threading.Event() + self.exception = exception def error(self, error): self._error = error @@ -166,7 +167,7 @@ class Future: def get(self, timeout=None): self._set.wait(timeout) if self._error != None: - raise exception(self._error) + raise self.exception(self._error) return self.value def is_set(self): diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 4da6f883b4..bbe2b326d6 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -110,6 +110,8 @@ class Session(Invoker): for st in self.spec.structs.values(): if st.name == name: return st + if self.spec.structs_by_name.has_key(name): + return self.spec.structs_by_name[name] return None def invoke(self, type, args, kwargs): @@ -289,8 +291,9 @@ class Delegate: self.session.exceptions.append(ex) error = self.session.error() for id in self.session.results: - f = self.session.results.pop(id) + f = self.session.results[id] f.error(error) + self.session.results.clear() for q in self.session._incoming.values(): q.close(error) diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 815c1d064a..4eb03008d0 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -257,6 +257,7 @@ class Struct(Composite): Composite.register(self, node) if self.code is not None: self.spec.structs[self.code] = self + self.spec.structs_by_name[self.name] = self def __str__(self): fields = ",\n ".join(["%s: %s" % (f.name, f.type.qname) @@ -443,6 +444,7 @@ class Spec(Node): self.controls = {} self.commands = {} self.structs = {} + self.structs_by_name = {} def encoding(self, klass): if Spec.ENCODINGS.has_key(klass): diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index f84f91c75a..2483c6f16d 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -18,12 +18,13 @@ # from qpid.client import Client, Closed from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase +from qpid.datatypes import Message, RangedSet +from qpid.session import SessionException +from qpid.testlib import TestBase010 from struct import pack, unpack from time import sleep -class DtxTests(TestBase): +class DtxTests(TestBase010): """ Tests for the amqp dtx related classes. @@ -43,15 +44,15 @@ class DtxTests(TestBase): tx_counter = 0 def reset_channel(self): - self.channel.session_close() - self.channel = self.client.channel(self.channel.id + 1) - self.channel.session_open() + self.session.close() + self.session = self.conn.session("dtx-session", 1) def test_simple_commit(self): """ Test basic one-phase commit behaviour. """ - channel = self.channel + guard = self.keepQueuesAlive(["queue-a", "queue-b"]) + session = self.session tx = self.xid("my-xid") self.txswap(tx, "commit") @@ -60,9 +61,9 @@ class DtxTests(TestBase): self.assertMessageCount(0, "queue-b") #commit - self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).status) + self.assertEqual(self.XA_OK, session.dtx_commit(xid=tx, one_phase=True).status) - #should close and reopen channel to ensure no unacked messages are held + #should close and reopen session to ensure no unacked messages are held self.reset_channel() #check result @@ -74,19 +75,20 @@ class DtxTests(TestBase): """ Test basic two-phase commit behaviour. """ - channel = self.channel + guard = self.keepQueuesAlive(["queue-a", "queue-b"]) + session = self.session tx = self.xid("my-xid") self.txswap(tx, "prepare-commit") #prepare - self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) + self.assertEqual(self.XA_OK, session.dtx_prepare(xid=tx).status) #neither queue should have any messages accessible self.assertMessageCount(0, "queue-a") self.assertMessageCount(0, "queue-b") #commit - self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).status) + self.assertEqual(self.XA_OK, session.dtx_commit(xid=tx, one_phase=False).status) self.reset_channel() @@ -100,7 +102,8 @@ class DtxTests(TestBase): """ Test basic rollback behaviour. """ - channel = self.channel + guard = self.keepQueuesAlive(["queue-a", "queue-b"]) + session = self.session tx = self.xid("my-xid") self.txswap(tx, "rollback") @@ -109,7 +112,7 @@ class DtxTests(TestBase): self.assertMessageCount(0, "queue-b") #rollback - self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + self.assertEqual(self.XA_OK, session.dtx_rollback(xid=tx).status) self.reset_channel() @@ -122,19 +125,20 @@ class DtxTests(TestBase): """ Test basic rollback behaviour after the transaction has been prepared. """ - channel = self.channel + guard = self.keepQueuesAlive(["queue-a", "queue-b"]) + session = self.session tx = self.xid("my-xid") self.txswap(tx, "prepare-rollback") #prepare - self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) + self.assertEqual(self.XA_OK, session.dtx_prepare(xid=tx).status) #neither queue should have any messages accessible self.assertMessageCount(0, "queue-a") self.assertMessageCount(0, "queue-b") #rollback - self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + self.assertEqual(self.XA_OK, session.dtx_rollback(xid=tx).status) self.reset_channel() @@ -148,17 +152,17 @@ class DtxTests(TestBase): check that an error is flagged if select is not issued before start or end """ - channel = self.channel + session = self.session tx = self.xid("dummy") try: - channel.dtx_demarcation_start(xid=tx) + session.dtx_start(xid=tx) #if we get here we have failed, but need to do some cleanup: - channel.dtx_demarcation_end(xid=tx) - channel.dtx_coordination_rollback(xid=tx) - self.fail("Channel not selected for use with dtx, expected exception!") - except Closed, e: - self.assertConnectionException(503, e.args[0]) + session.dtx_end(xid=tx) + session.dtx_rollback(xid=tx) + self.fail("Session not selected for use with dtx, expected exception!") + except SessionException, e: + self.assertEquals(503, e.args[0].error_code) def test_start_already_known(self): """ @@ -166,36 +170,35 @@ class DtxTests(TestBase): transaction that is already known is not allowed (unless the join flag is set). """ - #create two channels on different connection & select them for use with dtx: - channel1 = self.channel - channel1.dtx_demarcation_select() + #create two sessions on different connection & select them for use with dtx: + session1 = self.session + session1.dtx_select() other = self.connect() - channel2 = other.channel(1) - channel2.session_open() - channel2.dtx_demarcation_select() + session2 = other.session("other", 0) + session2.dtx_select() #create a xid tx = self.xid("dummy") - #start work on one channel under that xid: - channel1.dtx_demarcation_start(xid=tx) + #start work on one session under that xid: + session1.dtx_start(xid=tx) #then start on the other without the join set failed = False try: - channel2.dtx_demarcation_start(xid=tx) - except Closed, e: + session2.dtx_start(xid=tx) + except SessionException, e: failed = True error = e #cleanup: if not failed: - channel2.dtx_demarcation_end(xid=tx) + session2.dtx_end(xid=tx) other.close() - channel1.dtx_demarcation_end(xid=tx) - channel1.dtx_coordination_rollback(xid=tx) + session1.dtx_end(xid=tx) + session1.dtx_rollback(xid=tx) #verification: - if failed: self.assertConnectionException(503, e.args[0]) + if failed: self.assertEquals(503, error.args[0].error_code) else: self.fail("Xid already known, expected exception!") def test_forget_xid_on_completion(self): @@ -205,69 +208,69 @@ class DtxTests(TestBase): """ #do some transactional work & complete the transaction self.test_simple_commit() - # channel has been reset, so reselect for use with dtx - self.channel.dtx_demarcation_select() + # session has been reset, so reselect for use with dtx + self.session.dtx_select() #start association for the same xid as the previously completed txn tx = self.xid("my-xid") - self.channel.dtx_demarcation_start(xid=tx) - self.channel.dtx_demarcation_end(xid=tx) - self.channel.dtx_coordination_rollback(xid=tx) + self.session.dtx_start(xid=tx) + self.session.dtx_end(xid=tx) + self.session.dtx_rollback(xid=tx) def test_start_join_and_resume(self): """ Ensure the correct error is signalled when both the join and resume flags are set on starting an association between a - channel and a transcation. + session and a transcation. """ - channel = self.channel - channel.dtx_demarcation_select() + session = self.session + session.dtx_select() tx = self.xid("dummy") try: - channel.dtx_demarcation_start(xid=tx, join=True, resume=True) + session.dtx_start(xid=tx, join=True, resume=True) #failed, but need some cleanup: - channel.dtx_demarcation_end(xid=tx) - channel.dtx_coordination_rollback(xid=tx) + session.dtx_end(xid=tx) + session.dtx_rollback(xid=tx) self.fail("Join and resume both set, expected exception!") - except Closed, e: - self.assertConnectionException(503, e.args[0]) + except SessionException, e: + self.assertEquals(503, e.args[0].error_code) def test_start_join(self): """ - Verify 'join' behaviour, where a channel is associated with a - transaction that is already associated with another channel. + Verify 'join' behaviour, where a session is associated with a + transaction that is already associated with another session. """ - #create two channels & select them for use with dtx: - channel1 = self.channel - channel1.dtx_demarcation_select() + guard = self.keepQueuesAlive(["one", "two"]) + #create two sessions & select them for use with dtx: + session1 = self.session + session1.dtx_select() - channel2 = self.client.channel(2) - channel2.session_open() - channel2.dtx_demarcation_select() + session2 = self.conn.session("second", 2) + session2.dtx_select() #setup - channel1.queue_declare(queue="one", exclusive=True, auto_delete=True) - channel1.queue_declare(queue="two", exclusive=True, auto_delete=True) - channel1.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) - channel1.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + session1.queue_declare(queue="one", auto_delete=True) + session1.queue_declare(queue="two", auto_delete=True) + session1.message_transfer(self.createMessage(session1, "one", "a", "DtxMessage")) + session1.message_transfer(self.createMessage(session1, "two", "b", "DtxMessage")) #create a xid tx = self.xid("dummy") - #start work on one channel under that xid: - channel1.dtx_demarcation_start(xid=tx) + #start work on one session under that xid: + session1.dtx_start(xid=tx) #then start on the other with the join flag set - channel2.dtx_demarcation_start(xid=tx, join=True) + session2.dtx_start(xid=tx, join=True) - #do work through each channel - self.swap(channel1, "one", "two")#swap 'a' from 'one' to 'two' - self.swap(channel2, "two", "one")#swap 'b' from 'two' to 'one' + #do work through each session + self.swap(session1, "one", "two")#swap 'a' from 'one' to 'two' + self.swap(session2, "two", "one")#swap 'b' from 'two' to 'one' - #mark end on both channels - channel1.dtx_demarcation_end(xid=tx) - channel2.dtx_demarcation_end(xid=tx) + #mark end on both sessions + session1.dtx_end(xid=tx) + session2.dtx_end(xid=tx) #commit and check - channel1.dtx_coordination_commit(xid=tx, one_phase=True) + session1.dtx_commit(xid=tx, one_phase=True) self.assertMessageCount(1, "one") self.assertMessageCount(1, "two") self.assertMessageId("a", "two") @@ -278,27 +281,27 @@ class DtxTests(TestBase): """ Test suspension and resumption of an association """ - channel = self.channel - channel.dtx_demarcation_select() + session = self.session + session.dtx_select() #setup - channel.queue_declare(queue="one", exclusive=True, auto_delete=True) - channel.queue_declare(queue="two", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) - channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + session.queue_declare(queue="one", exclusive=True, auto_delete=True) + session.queue_declare(queue="two", exclusive=True, auto_delete=True) + session.message_transfer(self.createMessage(session, "one", "a", "DtxMessage")) + session.message_transfer(self.createMessage(session, "two", "b", "DtxMessage")) tx = self.xid("dummy") - channel.dtx_demarcation_start(xid=tx) - self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' - channel.dtx_demarcation_end(xid=tx, suspend=True) + session.dtx_start(xid=tx) + self.swap(session, "one", "two")#swap 'a' from 'one' to 'two' + session.dtx_end(xid=tx, suspend=True) - channel.dtx_demarcation_start(xid=tx, resume=True) - self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' - channel.dtx_demarcation_end(xid=tx) + session.dtx_start(xid=tx, resume=True) + self.swap(session, "two", "one")#swap 'b' from 'two' to 'one' + session.dtx_end(xid=tx) #commit and check - channel.dtx_coordination_commit(xid=tx, one_phase=True) + session.dtx_commit(xid=tx, one_phase=True) self.assertMessageCount(1, "one") self.assertMessageCount(1, "two") self.assertMessageId("a", "two") @@ -310,27 +313,27 @@ class DtxTests(TestBase): done on another transaction when the first transaction is suspended """ - channel = self.channel - channel.dtx_demarcation_select() + session = self.session + session.dtx_select() #setup - channel.queue_declare(queue="one", exclusive=True, auto_delete=True) - channel.queue_declare(queue="two", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) - channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) + session.queue_declare(queue="one", exclusive=True, auto_delete=True) + session.queue_declare(queue="two", exclusive=True, auto_delete=True) + session.message_transfer(self.createMessage(session, "one", "a", "DtxMessage")) + session.message_transfer(self.createMessage(session, "two", "b", "DtxMessage")) tx = self.xid("dummy") - channel.dtx_demarcation_start(xid=tx) - self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' - channel.dtx_demarcation_end(xid=tx, suspend=True) + session.dtx_start(xid=tx) + self.swap(session, "one", "two")#swap 'a' from 'one' to 'two' + session.dtx_end(xid=tx, suspend=True) - channel.dtx_demarcation_start(xid=tx, resume=True) - self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' - channel.dtx_demarcation_end(xid=tx) + session.dtx_start(xid=tx, resume=True) + self.swap(session, "two", "one")#swap 'b' from 'two' to 'one' + session.dtx_end(xid=tx) #commit and check - channel.dtx_coordination_commit(xid=tx, one_phase=True) + session.dtx_commit(xid=tx, one_phase=True) self.assertMessageCount(1, "one") self.assertMessageCount(1, "two") self.assertMessageId("a", "two") @@ -340,24 +343,23 @@ class DtxTests(TestBase): """ Verify that the correct error is signalled if the suspend and fail flag are both set when disassociating a transaction from - the channel + the session """ - channel = self.channel - channel.dtx_demarcation_select() + session = self.session + session.dtx_select() tx = self.xid("suspend_and_fail") - channel.dtx_demarcation_start(xid=tx) + session.dtx_start(xid=tx) try: - channel.dtx_demarcation_end(xid=tx, suspend=True, fail=True) + session.dtx_end(xid=tx, suspend=True, fail=True) self.fail("Suspend and fail both set, expected exception!") - except Closed, e: - self.assertConnectionException(503, e.args[0]) + except SessionException, e: + self.assertEquals(503, e.args[0].error_code) #cleanup other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.dtx_coordination_rollback(xid=tx) - channel.session_close() + session = other.session("cleanup", 1) + session.dtx_rollback(xid=tx) + session.close() other.close() @@ -365,51 +367,51 @@ class DtxTests(TestBase): """ Verifies that the correct exception is thrown when an attempt is made to end the association for a xid not previously - associated with the channel + associated with the session """ - channel = self.channel - channel.dtx_demarcation_select() + session = self.session + session.dtx_select() tx = self.xid("unknown-xid") try: - channel.dtx_demarcation_end(xid=tx) + session.dtx_end(xid=tx) self.fail("Attempted to end association with unknown xid, expected exception!") - except Closed, e: + except SessionException, e: #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming... - self.assertConnectionException(503, e.args[0]) + self.assertEquals(503, e.args[0].error_code) def test_end(self): """ Verify that the association is terminated by end and subsequent operations are non-transactional """ - channel = self.client.channel(2) - channel.session_open() - channel.queue_declare(queue="tx-queue", exclusive=True, auto_delete=True) + guard = self.keepQueuesAlive(["tx-queue"]) + session = self.conn.session("alternate", 1) + session.queue_declare(queue="tx-queue", exclusive=True, auto_delete=True) #publish a message under a transaction - channel.dtx_demarcation_select() + session.dtx_select() tx = self.xid("dummy") - channel.dtx_demarcation_start(xid=tx) - channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"one"}, body="DtxMessage")) - channel.dtx_demarcation_end(xid=tx) + session.dtx_start(xid=tx) + session.message_transfer(self.createMessage(session, "tx-queue", "one", "DtxMessage")) + session.dtx_end(xid=tx) #now that association with txn is ended, publish another message - channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"two"}, body="DtxMessage")) + session.message_transfer(self.createMessage(session, "tx-queue", "two", "DtxMessage")) #check the second message is available, but not the first self.assertMessageCount(1, "tx-queue") - self.subscribe(channel, queue="tx-queue", destination="results", confirm_mode=1) - msg = self.client.queue("results").get(timeout=1) - self.assertEqual("two", msg.content['message_id']) - channel.message_cancel(destination="results") - #ack the message then close the channel - msg.complete() - channel.session_close() - - channel = self.channel + self.subscribe(session, queue="tx-queue", destination="results") + msg = session.incoming("results").get(timeout=1) + self.assertEqual("two", self.getMessageProperty(msg, 'correlation_id')) + session.message_cancel(destination="results") + #ack the message then close the session + session.message_accept(RangedSet(msg.id)) + session.close() + + session = self.session #commit the transaction and check that the first message (and #only the first message) is then delivered - channel.dtx_coordination_commit(xid=tx, one_phase=True) + session.dtx_commit(xid=tx, one_phase=True) self.assertMessageCount(1, "tx-queue") self.assertMessageId("one", "tx-queue") @@ -419,27 +421,26 @@ class DtxTests(TestBase): transaction in question has already been prepared. """ other = self.connect() - tester = other.channel(1) - tester.session_open() + tester = other.session("tester", 1) tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - tester.dtx_demarcation_select() + tester.dtx_select() tx = self.xid("dummy") - tester.dtx_demarcation_start(xid=tx) - tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - tester.dtx_demarcation_end(xid=tx) - tester.dtx_coordination_prepare(xid=tx) + tester.dtx_start(xid=tx) + tester.message_transfer(self.createMessage(tester, "dummy", "dummy", "whatever")) + tester.dtx_end(xid=tx) + tester.dtx_prepare(xid=tx) failed = False try: - tester.dtx_coordination_commit(xid=tx, one_phase=True) - except Closed, e: + tester.dtx_commit(xid=tx, one_phase=True) + except SessionException, e: failed = True error = e if failed: - self.channel.dtx_coordination_rollback(xid=tx) - self.assertConnectionException(503, e.args[0]) + self.session.dtx_rollback(xid=tx) + self.assertEquals(503, error.args[0].error_code) else: - tester.session_close() + tester.close() other.close() self.fail("Invalid use of one_phase=True, expected exception!") @@ -453,99 +454,99 @@ class DtxTests(TestBase): transaction in question has already been prepared. """ other = self.connect() - tester = other.channel(1) - tester.session_open() + tester = other.session("tester", 1) tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - tester.dtx_demarcation_select() + tester.dtx_select() tx = self.xid("dummy") - tester.dtx_demarcation_start(xid=tx) - tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - tester.dtx_demarcation_end(xid=tx) + tester.dtx_start(xid=tx) + tester.message_transfer(self.createMessage(tester, "dummy", "dummy", "whatever")) + tester.dtx_end(xid=tx) failed = False try: - tester.dtx_coordination_commit(xid=tx, one_phase=False) - except Closed, e: + tester.dtx_commit(xid=tx, one_phase=False) + except SessionException, e: failed = True error = e if failed: - self.channel.dtx_coordination_rollback(xid=tx) - self.assertConnectionException(503, e.args[0]) + self.session.dtx_rollback(xid=tx) + self.assertEquals(503, error.args[0].error_code) else: - tester.session_close() + tester.close() other.close() self.fail("Invalid use of one_phase=False, expected exception!") def test_implicit_end(self): """ - Test that an association is implicitly ended when the channel + Test that an association is implicitly ended when the session is closed (whether by exception or explicit client request) and the transaction in question is marked as rollback only. """ - channel1 = self.channel - channel2 = self.client.channel(2) - channel2.session_open() + session1 = self.session + session2 = self.conn.session("other", 2) #setup: - channel2.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) + session2.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + session2.message_transfer(self.createMessage(session2, "dummy", "a", "whatever")) tx = self.xid("dummy") - channel2.dtx_demarcation_select() - channel2.dtx_demarcation_start(xid=tx) - channel2.message_subscribe(queue="dummy", destination="dummy", confirm_mode=1) - channel2.message_flow(destination="dummy", unit=0, value=1) - channel2.message_flow(destination="dummy", unit=1, value=0xFFFFFFFF) - self.client.queue("dummy").get(timeout=1).complete() - channel2.message_cancel(destination="dummy") - channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - channel2.session_close() + session2.dtx_select() + session2.dtx_start(xid=tx) + session2.message_subscribe(queue="dummy", destination="dummy") + session2.message_flow(destination="dummy", unit=0, value=1) + session2.message_flow(destination="dummy", unit=1, value=0xFFFFFFFF) + msg = session2.incoming("dummy").get(timeout=1) + session2.message_accept(RangedSet(msg.id)) + session2.message_cancel(destination="dummy") + session2.message_transfer(self.createMessage(session2, "dummy", "b", "whatever")) + session2.close() - self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).status) - channel1.dtx_coordination_rollback(xid=tx) + self.assertEqual(self.XA_RBROLLBACK, session1.dtx_prepare(xid=tx).status) + session1.dtx_rollback(xid=tx) def test_get_timeout(self): """ Check that get-timeout returns the correct value, (and that a transaction with a timeout can complete normally) """ - channel = self.channel + session = self.session tx = self.xid("dummy") - channel.dtx_demarcation_select() - channel.dtx_demarcation_start(xid=tx) - self.assertEqual(0, channel.dtx_coordination_get_timeout(xid=tx).timeout) - channel.dtx_coordination_set_timeout(xid=tx, timeout=60) - self.assertEqual(60, channel.dtx_coordination_get_timeout(xid=tx).timeout) - self.assertEqual(self.XA_OK, channel.dtx_demarcation_end(xid=tx).status) - self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) + session.dtx_select() + session.dtx_start(xid=tx) + self.assertEqual(0, session.dtx_get_timeout(xid=tx).timeout) + session.dtx_set_timeout(xid=tx, timeout=60) + self.assertEqual(60, session.dtx_get_timeout(xid=tx).timeout) + self.assertEqual(self.XA_OK, session.dtx_end(xid=tx).status) + self.assertEqual(self.XA_OK, session.dtx_rollback(xid=tx).status) def test_set_timeout(self): """ Test the timeout of a transaction results in the expected behaviour """ - #open new channel to allow self.channel to be used in checking te queue - channel = self.client.channel(2) - channel.session_open() + + guard = self.keepQueuesAlive(["queue-a", "queue-b"]) + #open new session to allow self.session to be used in checking the queue + session = self.conn.session("worker", 1) #setup: tx = self.xid("dummy") - channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':"timeout"}, body="DtxMessage")) - - channel.dtx_demarcation_select() - channel.dtx_demarcation_start(xid=tx) - self.swap(channel, "queue-a", "queue-b") - channel.dtx_coordination_set_timeout(xid=tx, timeout=2) + session.queue_declare(queue="queue-a", auto_delete=True) + session.queue_declare(queue="queue-b", auto_delete=True) + session.message_transfer(self.createMessage(session, "queue-a", "timeout", "DtxMessage")) + + session.dtx_select() + session.dtx_start(xid=tx) + self.swap(session, "queue-a", "queue-b") + session.dtx_set_timeout(xid=tx, timeout=2) sleep(3) #check that the work has been rolled back already self.assertMessageCount(1, "queue-a") self.assertMessageCount(0, "queue-b") self.assertMessageId("timeout", "queue-a") #check the correct codes are returned when we try to complete the txn - self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_demarcation_end(xid=tx).status) - self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_coordination_rollback(xid=tx).status) + self.assertEqual(self.XA_RBTIMEOUT, session.dtx_end(xid=tx).status) + self.assertEqual(self.XA_RBTIMEOUT, session.dtx_rollback(xid=tx).status) @@ -553,28 +554,29 @@ class DtxTests(TestBase): """ Test basic recover behaviour """ - channel = self.channel + session = self.session - channel.dtx_demarcation_select() - channel.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + session.dtx_select() + session.queue_declare(queue="dummy", exclusive=True, auto_delete=True) prepared = [] for i in range(1, 10): tx = self.xid("tx%s" % (i)) - channel.dtx_demarcation_start(xid=tx) - channel.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="message%s" % (i))) - channel.dtx_demarcation_end(xid=tx) + session.dtx_start(xid=tx) + session.message_transfer(self.createMessage(session, "dummy", "message%s" % (i), "message%s" % (i))) + session.dtx_end(xid=tx) if i in [2, 5, 6, 8]: - channel.dtx_coordination_prepare(xid=tx) + session.dtx_prepare(xid=tx) prepared.append(tx) else: - channel.dtx_coordination_rollback(xid=tx) + session.dtx_rollback(xid=tx) - xids = channel.dtx_coordination_recover().in_doubt + xids = session.dtx_recover().in_doubt + print "xids=%s" % xids #rollback the prepared transactions returned by recover for x in xids: - channel.dtx_coordination_rollback(xid=x) + session.dtx_rollback(xid=x) #validate against the expected list of prepared transactions actual = set(xids) @@ -585,61 +587,90 @@ class DtxTests(TestBase): missing = expected.difference(actual) extra = actual.difference(expected) for x in missing: - channel.dtx_coordination_rollback(xid=x) + session.dtx_rollback(xid=x) self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) def test_bad_resume(self): """ Test that a resume on a session not selected for use with dtx fails """ - channel = self.channel + session = self.session try: - channel.dtx_demarcation_start(resume=True) - except Closed, e: - self.assertConnectionException(503, e.args[0]) + session.dtx_start(resume=True) + except SessionException, e: + self.assertEquals(503, e.args[0].error_code) def xid(self, txid): DtxTests.tx_counter += 1 branchqual = "v%s" % DtxTests.tx_counter - return pack('!LBB', 0, len(txid), len(branchqual)) + txid + branchqual - + return self.session.xid(format=0, global_id=txid, branch_id=branchqual) + def txswap(self, tx, id): - channel = self.channel + session = self.session #declare two queues: - channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) + session.queue_declare(queue="queue-a", auto_delete=True) + session.queue_declare(queue="queue-b", auto_delete=True) + #put message with specified id on one queue: - channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':id}, body="DtxMessage")) + dp=session.delivery_properties(routing_key="queue-a") + mp=session.message_properties(correlation_id=id) + session.message_transfer(message=Message(dp, mp, "DtxMessage")) #start the transaction: - channel.dtx_demarcation_select() - self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_start(xid=tx).status) + session.dtx_select() + self.assertEqual(self.XA_OK, self.session.dtx_start(xid=tx).status) #'swap' the message from one queue to the other, under that transaction: - self.swap(self.channel, "queue-a", "queue-b") + self.swap(self.session, "queue-a", "queue-b") #mark the end of the transactional work: - self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_end(xid=tx).status) + self.assertEqual(self.XA_OK, self.session.dtx_end(xid=tx).status) - def swap(self, channel, src, dest): + def swap(self, session, src, dest): #consume from src: - channel.message_subscribe(destination="temp-swap", queue=src, confirm_mode=1) - channel.message_flow(destination="temp-swap", unit=0, value=1) - channel.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) - msg = self.client.queue("temp-swap").get(timeout=1) - channel.message_cancel(destination="temp-swap") - msg.complete(); - - #re-publish to dest - channel.message_transfer(content=Content(properties={'routing_key':dest, 'message_id':msg.content['message_id']}, - body=msg.content.body)) + session.message_subscribe(destination="temp-swap", queue=src) + session.message_flow(destination="temp-swap", unit=0, value=1) + session.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) + msg = session.incoming("temp-swap").get(timeout=1) + session.message_cancel(destination="temp-swap") + session.message_accept(RangedSet(msg.id)) + #todo: also complete at this point? + + #re-publish to dest: + dp=session.delivery_properties(routing_key=dest) + mp=session.message_properties(correlation_id=self.getMessageProperty(msg, 'correlation_id')) + session.message_transfer(message=Message(dp, mp, msg.body)) def assertMessageCount(self, expected, queue): - self.assertEqual(expected, self.channel.queue_query(queue=queue).message_count) + self.assertEqual(expected, self.session.queue_query(queue=queue).message_count) def assertMessageId(self, expected, queue): - self.channel.message_subscribe(queue=queue, destination="results") - self.channel.message_flow(destination="results", unit=0, value=1) - self.channel.message_flow(destination="results", unit=1, value=0xFFFFFFFF) - self.assertEqual(expected, self.client.queue("results").get(timeout=1).content['message_id']) - self.channel.message_cancel(destination="results") + self.session.message_subscribe(queue=queue, destination="results") + self.session.message_flow(destination="results", unit=0, value=1) + self.session.message_flow(destination="results", unit=1, value=0xFFFFFFFF) + self.assertEqual(expected, self.getMessageProperty(self.session.incoming("results").get(timeout=1), 'correlation_id')) + self.session.message_cancel(destination="results") + + def getMessageProperty(self, msg, prop): + for h in msg.headers: + if hasattr(h, prop): return getattr(h, prop) + return None + + def keepQueuesAlive(self, names): + session = self.conn.session("nasty", 99) + for n in names: + session.queue_declare(queue=n, auto_delete=True) + session.message_subscribe(destination=n, queue=n) + return session + + def createMessage(self, session, key, id, body): + dp=session.delivery_properties(routing_key=key) + mp=session.message_properties(correlation_id=id) + session.message_transfer(message=Message(dp, mp, body)) + + def subscribe(self, session=None, **keys): + session = session or self.session + consumer_tag = keys["destination"] + session.message_subscribe(**keys) + session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) -- cgit v1.2.1 From 1735aedd7604c28326f66b2ea912bb265647b394 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Thu, 27 Mar 2008 20:51:42 +0000 Subject: QPID-883: applying patch supplied by Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@641976 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/managementdata.py | 3 +- qpid/python/qpid/management.py | 131 ++++++++++++++++++++++++--- qpid/python/tests_0-10_preview/__init__.py | 1 + qpid/python/tests_0-10_preview/management.py | 89 ++++++++++++++++++ 4 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 qpid/python/tests_0-10_preview/management.py (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index adff05a710..badd8cd9d1 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -156,8 +156,7 @@ class ManagementData: self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, self.instHandler, self.methodReply) self.mclient.schemaListener (self.schemaHandler) - self.mch = managementChannel (self.channel, self.mclient.topicCb, self.mclient.replyCb) - self.mclient.addChannel (self.mch) + self.mch = self.mclient.addChannel (self.channel) def close (self): self.mclient.removeChannel (self.mch) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 33679cf0da..b3bc068166 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -26,12 +26,12 @@ import base64 import socket from threading import Thread from message import Message -from time import sleep +from time import time from qpid.client import Client from qpid.content import Content from cStringIO import StringIO from codec import Codec, EOF -from threading import Lock +from threading import Lock, Condition class SequenceManager: @@ -61,10 +61,29 @@ class SequenceManager: return data +class mgmtObject (object): + """ Generic object that holds the contents of a management object with its + attributes set as object attributes. """ + + def __init__ (self, classKey, timestamps, row): + self.classKey = classKey + self.timestamps = timestamps + for cell in row: + setattr (self, cell[0], cell[1]) + +class methodResult: + """ Object that contains the result of a method call """ + + def __init__ (self, status, sText, args): + self.status = status + self.statusText = sText + for arg in args: + setattr (self, arg, args[arg]) + class managementChannel: """ This class represents a connection to an AMQP broker. """ - def __init__ (self, ch, topicCb, replyCb, cbContext=None): + def __init__ (self, ch, topicCb, replyCb, cbContext): """ Given a channel on an established AMQP broker connection, this method opens a session and performs all of the declarations and bindings needed to participate in the management protocol. """ @@ -120,10 +139,12 @@ class managementClient: CTRL_SCHEMA_LOADED = 2 CTRL_USER = 3 + SYNC_TIME = 10.0 + #======================================================== # User API - interacts with the class's user #======================================================== - def __init__ (self, amqpSpec, ctrlCb, configCb, instCb, methodCb=None): + def __init__ (self, amqpSpec, ctrlCb=None, configCb=None, instCb=None, methodCb=None): self.spec = amqpSpec self.ctrlCb = ctrlCb self.configCb = configCb @@ -135,6 +156,10 @@ class managementClient: self.seqMgr = SequenceManager () self.schema = {} self.packages = {} + self.cv = Condition () + self.syncInFlight = False + self.syncSequence = 0 + self.syncResult = None def schemaListener (self, schemaCb): """ Optionally register a callback to receive details of the schema of @@ -146,9 +171,11 @@ class managementClient: in the network. """ self.eventCb = eventCb - def addChannel (self, channel): + def addChannel (self, channel, cbContext=None): """ Register a new channel. """ - self.channels.append (channel) + mch = managementChannel (channel, self.topicCb, self.replyCb, cbContext) + + self.channels.append (mch) codec = Codec (StringIO (), self.spec) self.setHeader (codec, ord ('B')) msg = Content (codec.stream.getvalue ()) @@ -156,12 +183,13 @@ class managementClient: msg["routing_key"] = "agent" msg["reply_to"] = self.spec.struct ("reply_to") msg["reply_to"]["exchange_name"] = "amq.direct" - msg["reply_to"]["routing_key"] = channel.replyName - channel.send ("qpid.management", msg) + msg["reply_to"]["routing_key"] = mch.replyName + mch.send ("qpid.management", msg) + return mch - def removeChannel (self, channel): + def removeChannel (self, mch): """ Remove a previously added channel from management. """ - self.channels.remove (channel) + self.channels.remove (mch) def callMethod (self, channel, userSequence, objId, className, methodName, args=None): """ Invoke a method on a managed object. """ @@ -182,6 +210,55 @@ class managementClient: msg["reply_to"]["routing_key"] = channel.replyName channel.send ("qpid.management", msg) + def syncWaitForStable (self, channel): + """ Synchronous (blocking) call to wait for schema stability on a channel """ + self.cv.acquire () + self.syncInFlight = True + starttime = time () + while channel.reqsOutstanding != 0: + self.cv.wait (self.SYNC_TIME) + if time () - starttime > self.SYNC_TIME: + self.cv.release () + raise RuntimeError ("Timed out waiting for response on channel") + self.cv.release () + + def syncCallMethod (self, channel, objId, className, methodName, args=None): + """ Synchronous (blocking) method call """ + self.cv.acquire () + self.syncInFlight = True + self.syncResult = None + self.syncSequence = self.seqMgr.reserve ("sync") + self.cv.release () + self.callMethod (channel, self.syncSequence, objId, className, methodName, args) + self.cv.acquire () + starttime = time () + while self.syncInFlight: + self.cv.wait (self.SYNC_TIME) + if time () - starttime > self.SYNC_TIME: + self.cv.release () + raise RuntimeError ("Timed out waiting for response on channel") + result = self.syncResult + self.cv.release () + return result + + def syncGetObjects (self, channel, className): + """ Synchronous (blocking) get call """ + self.cv.acquire () + self.syncInFlight = True + self.syncResult = [] + self.syncSequence = self.seqMgr.reserve ("sync") + self.cv.release () + self.getObjects (channel, self.syncSequence, className) + self.cv.acquire () + starttime = time () + while self.syncInFlight: + self.cv.wait (self.SYNC_TIME) + if time () - starttime > self.SYNC_TIME: + self.cv.release () + raise RuntimeError ("Timed out waiting for response on channel") + result = self.syncResult + self.cv.release () + return result #======================================================== # Channel API - interacts with registered channel objects @@ -312,10 +389,18 @@ class managementClient: return data def incOutstanding (self, ch): + self.cv.acquire () ch.reqsOutstanding = ch.reqsOutstanding + 1 + self.cv.release () def decOutstanding (self, ch): + self.cv.acquire () ch.reqsOutstanding = ch.reqsOutstanding - 1 + if ch.reqsOutstanding == 0 and self.syncInFlight: + self.syncInFlight = False + self.cv.notify () + self.cv.release () + if ch.reqsOutstanding == 0: if self.ctrlCb != None: self.ctrlCb (ch.context, self.CTRL_SCHEMA_LOADED, None) @@ -330,6 +415,7 @@ class managementClient: (userSequence, classId, methodName) = data args = {} + context = self.seqMgr.release (userSequence) if status == 0: schemaClass = self.schema[classId] @@ -346,7 +432,13 @@ class managementClient: if arg[2].find("O") != -1: args[arg[0]] = self.decodeValue (codec, arg[1]) - if self.methodCb != None: + if context == "sync" and userSequence == self.syncSequence: + self.cv.acquire () + self.syncInFlight = False + self.syncResult = methodResult (status, sText, args) + self.cv.notify () + self.cv.release () + elif self.methodCb != None: self.methodCb (ch.context, userSequence, status, sText, args) def handleCommandComplete (self, ch, codec, seq): @@ -356,6 +448,11 @@ class managementClient: context = self.seqMgr.release (seq) if context == "outstanding": self.decOutstanding (ch) + elif context == "sync" and seq == self.syncSequence: + self.cv.acquire () + self.syncInFlight = False + self.cv.notify () + self.cv.release () elif self.ctrlCb != None: self.ctrlCb (ch.context, self.CTRL_USER, data) @@ -541,9 +638,9 @@ class managementClient: if self.schemaCb != None: self.schemaCb (ch.context, classKey, configs, insts, methods, events) - def parseContent (self, ch, cls, codec): + def parseContent (self, ch, cls, codec, seq=0): """ Parse a received content message. """ - if (cls == 'C' or cls == 'B') and self.configCb == None: + if (cls == 'C' or (cls == 'B' and seq == 0)) and self.configCb == None: return if cls == 'I' and self.instCb == None: return @@ -582,8 +679,12 @@ class managementClient: data = self.decodeValue (codec, tc) row.append ((name, data)) - if cls == 'C' or cls == 'B': + if cls == 'C' or (cls == 'B' and seq != self.syncSequence): self.configCb (ch.context, classKey, row, timestamps) + elif cls == 'B' and seq == self.syncSequence: + if timestamps[2] == 0: + obj = mgmtObject (classKey, timestamps, row) + self.syncResult.append (obj) elif cls == 'I': self.instCb (ch.context, classKey, row, timestamps) @@ -596,7 +697,7 @@ class managementClient: elif opcode == 'i': self.parseContent (ch, 'I', codec) elif opcode == 'g': - self.parseContent (ch, 'B', codec) + self.parseContent (ch, 'B', codec, seq) else: raise ValueError ("Unknown opcode: %c" % opcode); diff --git a/qpid/python/tests_0-10_preview/__init__.py b/qpid/python/tests_0-10_preview/__init__.py index fe96d9e122..f0acf9c632 100644 --- a/qpid/python/tests_0-10_preview/__init__.py +++ b/qpid/python/tests_0-10_preview/__init__.py @@ -25,6 +25,7 @@ from dtx import * from example import * from exchange import * from execution import * +from management import * from message import * from query import * from queue import * diff --git a/qpid/python/tests_0-10_preview/management.py b/qpid/python/tests_0-10_preview/management.py new file mode 100644 index 0000000000..de6161ae96 --- /dev/null +++ b/qpid/python/tests_0-10_preview/management.py @@ -0,0 +1,89 @@ +# +# 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. +# + +from qpid.datatypes import Message, RangedSet +from qpid.testlib import TestBase +from qpid.management import managementChannel, managementClient + +class ManagementTest (TestBase): + """ + Tests for the management hooks + """ + + def test_broker_connectivity (self): + """ + Call the "echo" method on the broker to verify it is alive and talking. + """ + channel = self.client.channel(2) + + mc = managementClient (channel.spec) + mch = mc.addChannel (channel) + + mc.syncWaitForStable (mch) + brokers = mc.syncGetObjects (mch, "broker") + self.assertEqual (len (brokers), 1) + broker = brokers[0] + args = {} + body = "Echo Message Body" + args["body"] = body + + for seq in range (1, 5): + args["sequence"] = seq + res = mc.syncCallMethod (mch, broker.id, broker.classKey, "echo", args) + self.assertEqual (res.status, 0) + self.assertEqual (res.statusText, "OK") + self.assertEqual (res.sequence, seq) + self.assertEqual (res.body, body) + + def test_system_object (self): + channel = self.client.channel(2) + + mc = managementClient (channel.spec) + mch = mc.addChannel (channel) + + mc.syncWaitForStable (mch) + systems = mc.syncGetObjects (mch, "system") + self.assertEqual (len (systems), 1) + + def test_standard_exchanges (self): + channel = self.client.channel(2) + + mc = managementClient (channel.spec) + mch = mc.addChannel (channel) + + mc.syncWaitForStable (mch) + exchanges = mc.syncGetObjects (mch, "exchange") + exchange = self.findExchange (exchanges, "") + self.assertEqual (exchange.type, "direct") + exchange = self.findExchange (exchanges, "amq.direct") + self.assertEqual (exchange.type, "direct") + exchange = self.findExchange (exchanges, "amq.topic") + self.assertEqual (exchange.type, "topic") + exchange = self.findExchange (exchanges, "amq.fanout") + self.assertEqual (exchange.type, "fanout") + exchange = self.findExchange (exchanges, "amq.match") + self.assertEqual (exchange.type, "headers") + exchange = self.findExchange (exchanges, "qpid.management") + self.assertEqual (exchange.type, "topic") + + def findExchange (self, exchanges, name): + for exchange in exchanges: + if exchange.name == name: + return exchange + return None -- cgit v1.2.1 From 5aece25f4521f4eea76a5e9250fcfd6a637beac1 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Fri, 28 Mar 2008 20:53:44 +0000 Subject: QPID-885: patch from Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@642375 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 342 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100755 qpid/python/commands/qpid-config (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config new file mode 100755 index 0000000000..be0fc5a67f --- /dev/null +++ b/qpid/python/commands/qpid-config @@ -0,0 +1,342 @@ +#!/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 os +import getopt +import sys +import socket +import qpid +from threading import Condition +from qpid.management import managementClient +from qpid.peer import Closed +from qpid.client import Client +from time import sleep + +defspecpath = "/usr/share/amqp/amqp.0-10-preview.xml" +specpath = defspecpath +recursive = False +host = "localhost" + +def Usage (): + print "Usage: qpid-config [OPTIONS]" + print " qpid-config [OPTIONS] exchanges [filter-string]" + print " qpid-config [OPTIONS] queues [filter-string]" + print " qpid-config [OPTIONS] add exchange [durable]" + print " qpid-config [OPTIONS] del exchange " + print " qpid-config [OPTIONS] add queue [durable]" + print " qpid-config [OPTIONS] del queue " + print " qpid-config [OPTIONS] bind [binding-key]" + print " qpid-config [OPTIONS] unbind [binding-key]" + print + print "Options:" + print " -b show bindings" + print " -a default: localhost" + print " broker-addr is in the form: hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000" + print " -s default:", defspecpath + print + sys.exit (1) + +class Broker: + def __init__ (self, text): + colon = text.find (":") + if colon == -1: + host = text + self.port = 5672 + else: + host = text[:colon] + self.port = int (text[colon+1:]) + self.host = socket.gethostbyname (host) + + def name (self): + return self.host + ":" + str (self.port) + +class BrokerManager: + def __init__ (self): + self.dest = None + self.src = None + self.broker = None + + def SetBroker (self, broker): + self.broker = broker + + def ConnectToBroker (self): + try: + self.spec = qpid.spec.load (specpath) + self.client = Client (self.broker.host, self.broker.port, self.spec) + self.client.start ({"LOGIN":"guest","PASSWORD":"guest"}) + self.channel = self.client.channel (1) + self.mclient = managementClient (self.spec) + self.mchannel = self.mclient.addChannel (self.channel) + except socket.error, e: + print "Connect Error:", e + exit (1) + + def Overview (self): + self.ConnectToBroker () + mc = self.mclient + mch = self.mchannel + mc.syncWaitForStable (mch) + exchanges = mc.syncGetObjects (mch, "exchange") + queues = mc.syncGetObjects (mch, "queue") + print "Total Exchanges: %d" % len (exchanges) + etype = {} + for ex in exchanges: + if ex.type not in etype: + etype[ex.type] = 1 + else: + etype[ex.type] = etype[ex.type] + 1 + for typ in etype: + print "%15s: %d" % (typ, etype[typ]) + + print + print " Total Queues: %d" % len (queues) + durable = 0 + for queue in queues: + if queue.durable: + durable = durable + 1 + print " durable: %d" % durable + print " non-durable: %d" % (len (queues) - durable) + + def ExchangeList (self, filter): + self.ConnectToBroker () + mc = self.mclient + mch = self.mchannel + mc.syncWaitForStable (mch) + exchanges = mc.syncGetObjects (mch, "exchange") + print "Type Bindings Exchange Name" + print "=============================================" + for ex in exchanges: + if self.match (ex.name, filter): + print "%-10s%5d %s" % (ex.type, ex.bindings, ex.name) + + def ExchangeListRecurse (self, filter): + self.ConnectToBroker () + mc = self.mclient + mch = self.mchannel + mc.syncWaitForStable (mch) + exchanges = mc.syncGetObjects (mch, "exchange") + bindings = mc.syncGetObjects (mch, "binding") + queues = mc.syncGetObjects (mch, "queue") + for ex in exchanges: + if self.match (ex.name, filter): + print "Exchange '%s' (%s)" % (ex.name, ex.type) + for bind in bindings: + if bind.exchangeRef == ex.id: + qname = "" + queue = self.findById (queues, bind.queueRef) + if queue != None: + qname = queue.name + print " bind [%s] => %s" % (bind.bindingKey, qname) + + + def QueueList (self, filter): + self.ConnectToBroker () + mc = self.mclient + mch = self.mchannel + mc.syncWaitForStable (mch) + queues = mc.syncGetObjects (mch, "queue") + print "Durable AutoDel Excl Bindings Queue Name" + print "===============================================================" + for q in queues: + if self.match (q.name, filter): + print "%4c%9c%7c%10d %s" % (tf (q.durable), tf (q.autoDelete), tf (q.exclusive), + q.bindings, q.name) + + def QueueListRecurse (self, filter): + self.ConnectToBroker () + mc = self.mclient + mch = self.mchannel + mc.syncWaitForStable (mch) + exchanges = mc.syncGetObjects (mch, "exchange") + bindings = mc.syncGetObjects (mch, "binding") + queues = mc.syncGetObjects (mch, "queue") + for queue in queues: + if self.match (queue.name, filter): + print "Queue '%s'" % queue.name + for bind in bindings: + if bind.queueRef == queue.id: + ename = "" + ex = self.findById (exchanges, bind.exchangeRef) + if ex != None: + ename = ex.name + if ename == "": + ename = "''" + print " bind [%s] => %s" % (bind.bindingKey, ename) + + def AddExchange (self, args): + if len (args) < 2: + Usage () + self.ConnectToBroker () + etype = args[0] + ename = args[1] + _durable = False + if len (args) > 2 and args[2] == "durable": + _durable = True + + try: + self.channel.exchange_declare (exchange=ename, type=etype, durable=_durable) + except Closed, e: + print "Failed:", e + + def DelExchange (self, args): + if len (args) < 1: + Usage () + self.ConnectToBroker () + ename = args[0] + + try: + self.channel.exchange_delete (exchange=ename) + except Closed, e: + print "Failed:", e + + def AddQueue (self, args): + if len (args) < 1: + Usage () + self.ConnectToBroker () + qname = args[0] + _durable = False + if len (args) > 1 and args[1] == "durable": + _durable = True + + try: + self.channel.queue_declare (queue=qname, durable=_durable) + except Closed, e: + print "Failed:", e + + def DelQueue (self, args): + if len (args) < 1: + Usage () + self.ConnectToBroker () + qname = args[0] + + try: + self.channel.queue_delete (queue=qname) + except Closed, e: + print "Failed:", e + + def Bind (self, args): + if len (args) < 2: + Usage () + self.ConnectToBroker () + ename = args[0] + qname = args[1] + key = "" + if len (args) > 2: + key = args[2] + + try: + self.channel.queue_bind (queue=qname, exchange=ename, routing_key=key) + except Closed, e: + print "Failed:", e + + def Unbind (self, args): + if len (args) < 2: + Usage () + self.ConnectToBroker () + ename = args[0] + qname = args[1] + key = "" + if len (args) > 2: + key = args[2] + + try: + self.channel.queue_unbind (queue=qname, exchange=ename, routing_key=key) + except Closed, e: + print "Failed:", e + + def findById (self, items, id): + for item in items: + if item.id == id: + return item + return None + + def match (self, name, filter): + if filter == "": + return True + if name.find (filter) == -1: + return False + return True + +def tf (bool): + if bool: + return 'Y' + return 'N' + +## +## Main Program +## + +try: + (optlist, cargs) = getopt.getopt (sys.argv[1:], "s:a:b") +except: + Usage () + +for opt in optlist: + if opt[0] == "-s": + specpath = opt[1] + if opt[0] == "-b": + recursive = True + if opt[0] == "-a": + host = opt[1] + +nargs = len (cargs) +bm = BrokerManager () +bm.SetBroker (Broker (host)) + +if nargs == 0: + bm.Overview () +else: + cmd = cargs[0] + modifier = "" + if nargs > 1: + modifier = cargs[1] + if cmd[0] == 'e': + if recursive: + bm.ExchangeListRecurse (modifier) + else: + bm.ExchangeList (modifier) + elif cmd[0] == 'q': + if recursive: + bm.QueueListRecurse (modifier) + else: + bm.QueueList (modifier) + elif cmd == "add": + if modifier == "exchange": + bm.AddExchange (cargs[2:]) + elif modifier == "queue": + bm.AddQueue (cargs[2:]) + else: + Usage () + elif cmd == "del": + if modifier == "exchange": + bm.DelExchange (cargs[2:]) + elif modifier == "queue": + bm.DelQueue (cargs[2:]) + else: + Usage () + elif cmd == "bind": + bm.Bind (cargs[1:]) + elif cmd == "unbind": + bm.Unbind (cargs[1:]) + else: + Usage () + -- cgit v1.2.1 From 9630e4ee3cd6cd14f1582f86adfa269cc1e3fa24 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 31 Mar 2008 12:51:36 +0000 Subject: Re-introduced old 'no-local' behaviour for exclusive queues via a proprietary arg to queue.declare. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@642981 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 2 -- qpid/python/qpid/testlib.py | 7 +++++++ qpid/python/tests_0-10/dtx.py | 7 ------- qpid/python/tests_0-10/message.py | 31 +++++++++++++++---------------- 4 files changed, 22 insertions(+), 25 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index 58cad3344b..cc1e57bca0 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -3,8 +3,6 @@ tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair tests.codec.FieldTableTestCase.test_field_table_name_value_pair tests_0-10.execution.ExecutionTests.test_flush tests_0-10.dtx.DtxTests.test_recover -tests_0-10.message.MessageTests.test_consume_no_local -tests_0-10.message.MessageTests.test_consume_no_local_awkward tests_0-10.message.MessageTests.test_no_size tests_0-10.message.MessageTests.test_qos_prefetch_count tests_0-10.message.MessageTests.test_qos_prefetch_size diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index f633c4e77d..b6cf6d7446 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -361,3 +361,10 @@ class TestBase010(unittest.TestCase): def tearDown(self): if not self.session.error(): self.session.close(timeout=10) self.conn.close(timeout=10) + + def subscribe(self, session=None, **keys): + session = session or self.session + consumer_tag = keys["destination"] + session.message_subscribe(**keys) + session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 2483c6f16d..042df521ae 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -667,10 +667,3 @@ class DtxTests(TestBase010): dp=session.delivery_properties(routing_key=key) mp=session.message_properties(correlation_id=id) session.message_transfer(message=Message(dp, mp, body)) - - def subscribe(self, session=None, **keys): - session = session or self.session - consumer_tag = keys["destination"] - session.message_subscribe(**keys) - session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) - session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index df9fde1593..2b1b446e7a 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -28,33 +28,33 @@ from qpid.content import Content class MessageTests(TestBase010): """Tests for 'methods' on the amqp message 'class'""" - def test_consume_no_local(self): + def test_no_local(self): """ Test that the no_local flag is honoured in the consume method """ session = self.session - #setup, declare two queues: + #setup, declare two queues one of which excludes delivery of locally sent messages session.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) - session.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True) - #establish two consumers one of which excludes delivery of locally sent messages + session.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True, arguments={'no-local':'true'}) + #establish two consumers self.subscribe(destination="local_included", queue="test-queue-1a") - self.subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) + self.subscribe(destination="local_excluded", queue="test-queue-1b") #send a message - session.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) - session.message_transfer(content=Content(properties={'routing_key' : "test-queue-1b"}, body="consume_no_local")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1a"), "deliver-me")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1b"), "dont-deliver-me")) #check the queues of the two consumers excluded = session.incoming("local_excluded") included = session.incoming("local_included") msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.body) + self.assertEqual("deliver-me", msg.body) try: excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") except Empty: None - def test_consume_no_local_awkward(self): + def test_no_local_awkward(self): """ If an exclusive queue gets a no-local delivered to it, that @@ -67,19 +67,18 @@ class MessageTests(TestBase010): session = self.session #setup: - session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True, arguments={'no-local':'true'}) #establish consumer which excludes delivery of locally sent messages - self.subscribe(destination="local_excluded", queue="test-queue", no_local=True) + self.subscribe(destination="local_excluded", queue="test-queue") #send a 'local' message - session.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="local")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "local")) #send a non local message other = self.connect() - session2 = other.session(1) - session2.session_open() - session2.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="foreign")) - session2.session_close() + session2 = other.session("my-session", 1) + session2.message_transfer(message=Message(session2.delivery_properties(routing_key="test-queue"), "foreign")) + session2.close() other.close() #check that the second message only is delivered -- cgit v1.2.1 From f765080d69339ff978a20ff697173c362a333f41 Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Fri, 4 Apr 2008 18:14:42 +0000 Subject: Patch from Ted Ross (see QPID-902): This patch contains the following improvements for management:\n1) Schema display cleaned up in the python mgmt-cli\n2) Locking added automatically to management object accessors (manual locking removed from broker/Queue.cpp)\n3) Schemas are now pre-registered with the management agent using a package initializer. This allows management consoles to get schema information for a class even if no instances of the class exist. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@644806 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mgmt-cli/managementdata.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index badd8cd9d1..1524e2c919 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -451,8 +451,9 @@ class ManagementData: rows = [] sorted = self.schema.keys () sorted.sort () - for className in sorted: - tuple = self.schema[className] + for classKey in sorted: + tuple = self.schema[classKey] + className = classKey[0] + "." + classKey[1] row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) rows.append (row) self.disp.table ("Classes in Schema:", -- cgit v1.2.1 From c89fe1d8ef23cb6f3f2c60623dfdac08216baa06 Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Tue, 8 Apr 2008 19:29:08 +0000 Subject: Patch from Ted Ross: QPID-907: Management Improvements for C++ Broker and Store git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@646045 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 110 +++++++++++++++++++++------------ qpid/python/mgmt-cli/managementdata.py | 6 ++ qpid/python/qpid/management.py | 4 ++ 3 files changed, 81 insertions(+), 39 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index be0fc5a67f..3fd8e93c63 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -30,28 +30,43 @@ from qpid.peer import Closed from qpid.client import Client from time import sleep -defspecpath = "/usr/share/amqp/amqp.0-10-preview.xml" -specpath = defspecpath -recursive = False -host = "localhost" +_defspecpath = "/usr/share/amqp/amqp.0-10-preview.xml" +_specpath = _defspecpath +_recursive = False +_host = "localhost" +_durable = False +_fileCount = 8 +_fileSize = 24 + +FILECOUNT = "qpid.file_count" +FILESIZE = "qpid.file_size" def Usage (): print "Usage: qpid-config [OPTIONS]" print " qpid-config [OPTIONS] exchanges [filter-string]" print " qpid-config [OPTIONS] queues [filter-string]" - print " qpid-config [OPTIONS] add exchange [durable]" + print " qpid-config [OPTIONS] add exchange [AddExchangeOptions]" print " qpid-config [OPTIONS] del exchange " - print " qpid-config [OPTIONS] add queue [durable]" + print " qpid-config [OPTIONS] add queue [AddQueueOptions]" print " qpid-config [OPTIONS] del queue " print " qpid-config [OPTIONS] bind [binding-key]" print " qpid-config [OPTIONS] unbind [binding-key]" print print "Options:" - print " -b show bindings" - print " -a default: localhost" + print " -b [ --bindings ] Show bindings in queue or exchange list" + print " -a [ --broker-addr ] Address (localhost) Address of qpidd broker" print " broker-addr is in the form: hostname | ip-address [:]" print " ex: localhost, 10.1.1.7:10000, broker-host:10000" - print " -s default:", defspecpath + print " -s [ --spec-file] Path (" + _defspecpath + ")" + print " AMQP specification file" + print + print "Add Queue Options:" + print " --durable Queue is durable" + print " --file-count N (8) Number of files in queue's persistence journal" + print " --file-size N (24) File size in pages (64Kib/page)" + print + print "Add Exchange Options:" + print " --durable Exchange is durable" print sys.exit (1) @@ -80,7 +95,7 @@ class BrokerManager: def ConnectToBroker (self): try: - self.spec = qpid.spec.load (specpath) + self.spec = qpid.spec.load (_specpath) self.client = Client (self.broker.host, self.broker.port, self.spec) self.client.start ({"LOGIN":"guest","PASSWORD":"guest"}) self.channel = self.client.channel (1) @@ -109,12 +124,12 @@ class BrokerManager: print print " Total Queues: %d" % len (queues) - durable = 0 + _durable = 0 for queue in queues: if queue.durable: - durable = durable + 1 - print " durable: %d" % durable - print " non-durable: %d" % (len (queues) - durable) + _durable = _durable + 1 + print " durable: %d" % _durable + print " non-durable: %d" % (len (queues) - _durable) def ExchangeList (self, filter): self.ConnectToBroker () @@ -153,13 +168,25 @@ class BrokerManager: mc = self.mclient mch = self.mchannel mc.syncWaitForStable (mch) - queues = mc.syncGetObjects (mch, "queue") - print "Durable AutoDel Excl Bindings Queue Name" - print "===============================================================" + queues = mc.syncGetObjects (mch, "queue") + journals = mc.syncGetObjects (mch, "journal") + print " Store Size" + print "Durable AutoDel Excl Bindings (files x file pages) Queue Name" + print "===========================================================================================" for q in queues: if self.match (q.name, filter): - print "%4c%9c%7c%10d %s" % (tf (q.durable), tf (q.autoDelete), tf (q.exclusive), - q.bindings, q.name) + args = q.arguments + if q.durable and FILESIZE in args and FILECOUNT in args: + fs = int (args[FILESIZE]) + fc = int (args[FILECOUNT]) + print "%4c%9c%7c%10d%11dx%-14d%s" % \ + (YN (q.durable), YN (q.autoDelete), + YN (q.exclusive), q.bindings, fc, fs, q.name) + else: + if not _durable: + print "%4c%9c%7c%10d %s" % \ + (YN (q.durable), YN (q.autoDelete), + YN (q.exclusive), q.bindings, q.name) def QueueListRecurse (self, filter): self.ConnectToBroker () @@ -188,9 +215,6 @@ class BrokerManager: self.ConnectToBroker () etype = args[0] ename = args[1] - _durable = False - if len (args) > 2 and args[2] == "durable": - _durable = True try: self.channel.exchange_declare (exchange=ename, type=etype, durable=_durable) @@ -212,13 +236,14 @@ class BrokerManager: if len (args) < 1: Usage () self.ConnectToBroker () - qname = args[0] - _durable = False - if len (args) > 1 and args[1] == "durable": - _durable = True + qname = args[0] + declArgs = {} + if _durable: + declArgs[FILECOUNT] = _fileCount + declArgs[FILESIZE] = _fileSize try: - self.channel.queue_declare (queue=qname, durable=_durable) + self.channel.queue_declare (queue=qname, durable=_durable, arguments=declArgs) except Closed, e: print "Failed:", e @@ -276,7 +301,7 @@ class BrokerManager: return False return True -def tf (bool): +def YN (bool): if bool: return 'Y' return 'N' @@ -286,21 +311,28 @@ def tf (bool): ## try: - (optlist, cargs) = getopt.getopt (sys.argv[1:], "s:a:b") + longOpts = ("durable", "spec-file=", "bindings", "broker-addr=", "file-count=", "file-size=") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:a:b", longOpts) except: Usage () for opt in optlist: - if opt[0] == "-s": - specpath = opt[1] - if opt[0] == "-b": - recursive = True - if opt[0] == "-a": - host = opt[1] + if opt[0] == "-s" or opt[0] == "--spec-file": + _specpath = opt[1] + if opt[0] == "-b" or opt[0] == "--bindings": + _recursive = True + if opt[0] == "-a" or opt[0] == "--broker-addr": + _host = opt[1] + if opt[0] == "--durable": + _durable = True + if opt[0] == "--file-count": + _fileCount = int (opt[1]) + if opt[0] == "--file-size": + _fileSize = int (opt[1]) nargs = len (cargs) -bm = BrokerManager () -bm.SetBroker (Broker (host)) +bm = BrokerManager () +bm.SetBroker (Broker (_host)) if nargs == 0: bm.Overview () @@ -310,12 +342,12 @@ else: if nargs > 1: modifier = cargs[1] if cmd[0] == 'e': - if recursive: + if _recursive: bm.ExchangeListRecurse (modifier) else: bm.ExchangeList (modifier) elif cmd[0] == 'q': - if recursive: + if _recursive: bm.QueueListRecurse (modifier) else: bm.QueueList (modifier) diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py index 1524e2c919..00fc0ec09e 100644 --- a/qpid/python/mgmt-cli/managementdata.py +++ b/qpid/python/mgmt-cli/managementdata.py @@ -199,6 +199,8 @@ class ManagementData: return "True" elif typecode == 14: return str (UUID (bytes=value)) + elif typecode == 15: + return str (value) return "*type-error*" def getObjIndex (self, className, config): @@ -268,6 +270,10 @@ class ManagementData: return "float" elif typecode == 13: return "double" + elif typecode == 14: + return "uuid" + elif typecode == 15: + return "field-table" else: raise ValueError ("Invalid type code: %d" % typecode) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index b3bc068166..b08566ee4f 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -351,6 +351,8 @@ class managementClient: codec.encode_double (double (value)) elif typecode == 14: # UUID codec.encode_uuid (value) + elif typecode == 15: # FTABLE + codec.encode_table (value) else: raise ValueError ("Invalid type code: %d" % typecode) @@ -384,6 +386,8 @@ class managementClient: data = codec.decode_double () elif typecode == 14: # UUID data = codec.decode_uuid () + elif typecode == 15: # FTABLE + data = codec.decode_table () else: raise ValueError ("Invalid type code: %d" % typecode) return data -- cgit v1.2.1 From b99e87811df8dbbe8a6c3e6b0ce5df44d0f20772 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Tue, 8 Apr 2008 21:51:17 +0000 Subject: QPID-908 from tross + corrected spec location -s. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@646093 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 266 ++++++++++++++++++++++++++++++++++++++++ qpid/python/qpid/management.py | 6 +- 2 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 qpid/python/commands/qpid-route (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route new file mode 100644 index 0000000000..3f29760ef5 --- /dev/null +++ b/qpid/python/commands/qpid-route @@ -0,0 +1,266 @@ +#!/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 getopt +import sys +import socket +import qpid +from qpid.management import managementClient +from qpid.peer import Closed +from qpid.client import Client + +def Usage (): + print "Usage: qpid-route [OPTIONS] add " + print " qpid-route [OPTIONS] del " + print " qpid-route [OPTIONS] list " + #print " qpid-route [OPTIONS] load " + print " qpid-route [OPTIONS] flush " + print + print "Options:" + print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10-preview.xml)" + print " -v [ --verbose ] Verbose output" + print " -q [ --quiet ] Quiet output, don't print duplicate warnings" + print + print " dest-broker and src-broker are in the form: hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000" + print + #print " If loading the route configuration from a file, the input file has one line per route" + #print " in the form:" + #print + #print " " + #print + sys.exit (1) + +_specpath = "/usr/share/amqp/amqp.0-10-preview.xml" +_verbose = False +_quiet = False + +class Broker: + def __init__ (self, text): + colon = text.find (":") + if colon == -1: + host = text + self.port = 5672 + else: + host = text[:colon] + self.port = int (text[colon+1:]) + self.host = socket.gethostbyname (host) + + def name (self): + return self.host + ":" + str (self.port) + +class RouteManager: + def __init__ (self, destBroker): + self.dest = Broker (destBroker) + self.src = None + + def ConnectToBroker (self): + broker = self.dest + if _verbose: + print "Connecting to broker: %s:%d" % (broker.host, broker.port) + try: + self.spec = qpid.spec.load (_specpath) + self.client = Client (broker.host, broker.port, self.spec) + self.client.start ({"LOGIN":"guest","PASSWORD":"guest"}) + self.channel = self.client.channel (1) + self.mclient = managementClient (self.spec) + self.mch = self.mclient.addChannel (self.channel) + self.mclient.syncWaitForStable (self.mch) + except socket.error, e: + print "Connect Error:", e + exit (1) + + def getLink (self): + links = self.mclient.syncGetObjects (self.mch, "link") + for link in links: + if link.address == self.src.name (): + return link + return None + + def AddRoute (self, srcBroker, exchange, routingKey): + self.src = Broker (srcBroker) + mc = self.mclient + + brokers = mc.syncGetObjects (self.mch, "broker") + broker = brokers[0] + + link = self.getLink () + if link == None: + if _verbose: + print "Inter-broker link not found, creating..." + + connectArgs = {} + connectArgs["host"] = self.src.host + connectArgs["port"] = self.src.port + res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) + if _verbose: + print "Connect method returned:", res.status, res.statusText + link = self.getLink () + + if link == None: + print "Protocol Error - Missing link ID" + exit (1) + + bridges = mc.syncGetObjects (self.mch, "bridge") + for bridge in bridges: + if bridge.linkRef == link.id and bridge.dest == exchange and bridge.key == routingKey: + if not _quiet: + print "Duplicate Route - ignoring: %s(%s)" % (exchange, routingKey) + exit (1) + exit (0) + + if _verbose: + print "Creating inter-broker binding..." + bridgeArgs = {} + bridgeArgs["src"] = "src" + bridgeArgs["dest"] = exchange + bridgeArgs["key"] = routingKey + bridgeArgs["src_is_queue"] = 0 + bridgeArgs["src_is_local"] = 0 + res = mc.syncCallMethod (self.mch, link.id, link.classKey, "bridge", bridgeArgs) + if _verbose: + print "Bridge method returned:", res.status, res.statusText + + def DelRoute (self, srcBroker, exchange, routingKey): + self.src = Broker (srcBroker) + mc = self.mclient + + link = self.getLink () + if link == None: + if not _quiet: + print "No link found from %s to %s" % (self.src.name(), self.dest.name()) + exit (1) + exit (0) + + bridges = mc.syncGetObjects (self.mch, "bridge") + for bridge in bridges: + if bridge.linkRef == link.id and bridge.dest == exchange and bridge.key == routingKey: + if _verbose: + print "Closing bridge..." + res = mc.syncCallMethod (self.mch, bridge.id, bridge.classKey, "close") + if res.status != 0: + print "Error closing bridge: %d - %s" % (res.status, res.statusText) + exit (1) + if len (bridges) == 1: + if _verbose: + print "Last bridge on link, closing link..." + res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + if res.status != 0: + print "Error closing link: %d - %s" % (res.status, res.statusText) + exit (1) + exit (0) + if not _quiet: + print "Route not found" + exit (1) + + def ListRoutes (self): + mc = self.mclient + links = mc.syncGetObjects (self.mch, "link") + bridges = mc.syncGetObjects (self.mch, "bridge") + + for bridge in bridges: + myLink = None + for link in links: + if bridge.linkRef == link.id: + myLink = link + break + if myLink != None: + print "%s %s %s %s" % (self.dest.name(), myLink.address, bridge.dest, bridge.key) + + def LoadRoutes (self, inFile): + pass + + def ClearAllRoutes (self): + mc = self.mclient + links = mc.syncGetObjects (self.mch, "link") + bridges = mc.syncGetObjects (self.mch, "bridge") + + for bridge in bridges: + if _verbose: + myLink = None + for link in links: + if bridge.linkRef == link.id: + myLink = link + break + if myLink != None: + print "Deleting Bridge: %s %s %s... " % (myLink.address, bridge.dest, bridge.key), + res = mc.syncCallMethod (self.mch, bridge.id, bridge.classKey, "close") + if res.status != 0: + print "Error: %d - %s" % (res.status, res.statusText) + elif _verbose: + print "Ok" + + for link in links: + if _verbose: + print "Deleting Link: %s... " % link.address, + res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + if res.status != 0: + print "Error: %d - %s" % (res.status, res.statusText) + elif _verbose: + print "Ok" + +## +## Main Program +## + +try: + longOpts = ("verbose", "quiet", "spec-file=") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:vq", longOpts) +except: + Usage () + +for opt in optlist: + if opt[0] == "-s" or opt[0] == "--spec-file": + _specpath = opt[1] + if opt[0] == "-v" or opt[0] == "--verbose": + _verbose = True + if opt[0] == "-q" or opt[0] == "--quiet": + _quiet = True + +nargs = len (cargs) +if nargs < 2: + Usage () + +cmd = cargs[0] +if cmd != "load": + rm = RouteManager (cargs[1]) + rm.ConnectToBroker () + +if cmd == "add" or cmd == "del": + if nargs != 5: + Usage () + if cmd == "add": + rm.AddRoute (cargs[2], cargs[3], cargs[4]) + else: + rm.DelRoute (cargs[2], cargs[3], cargs[4]) +else: + if nargs != 2: + Usage () + + if cmd == "list": + rm.ListRoutes () + #elif cmd == "load": + # rm.LoadRoutes (cargs[1]) + elif cmd == "flush": + rm.ClearAllRoutes () + else: + Usage () + diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index b08566ee4f..d2dcacd62a 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -22,7 +22,7 @@ Management API for Qpid """ import qpid -import base64 +import struct import socket from threading import Thread from message import Message @@ -89,8 +89,8 @@ class managementChannel: to participate in the management protocol. """ response = ch.session_open (detached_lifetime=300) self.sessionId = response.session_id - self.topicName = "mgmt-" + base64.urlsafe_b64encode (response.session_id) - self.replyName = "reply-" + base64.urlsafe_b64encode (response.session_id) + self.topicName = "mgmt-%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", response.session_id) + self.replyName = "repl-%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", response.session_id) self.qpidChannel = ch self.tcb = topicCb self.rcb = replyCb -- cgit v1.2.1 From c2d21d3903178152a0873d6f274968c2f91c8ea6 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 9 Apr 2008 19:52:44 +0000 Subject: Fixes and automated tests for federation function. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@646505 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 3f29760ef5..c69ca6204f 100644 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -86,7 +86,7 @@ class RouteManager: self.mclient.syncWaitForStable (self.mch) except socket.error, e: print "Connect Error:", e - exit (1) + sys.exit (1) def getLink (self): links = self.mclient.syncGetObjects (self.mch, "link") @@ -117,20 +117,20 @@ class RouteManager: if link == None: print "Protocol Error - Missing link ID" - exit (1) + sys.exit (1) bridges = mc.syncGetObjects (self.mch, "bridge") for bridge in bridges: if bridge.linkRef == link.id and bridge.dest == exchange and bridge.key == routingKey: if not _quiet: print "Duplicate Route - ignoring: %s(%s)" % (exchange, routingKey) - exit (1) - exit (0) + sys.exit (1) + sys.exit (0) if _verbose: print "Creating inter-broker binding..." bridgeArgs = {} - bridgeArgs["src"] = "src" + bridgeArgs["src"] = exchange bridgeArgs["dest"] = exchange bridgeArgs["key"] = routingKey bridgeArgs["src_is_queue"] = 0 @@ -147,8 +147,8 @@ class RouteManager: if link == None: if not _quiet: print "No link found from %s to %s" % (self.src.name(), self.dest.name()) - exit (1) - exit (0) + sys.exit (1) + sys.exit (0) bridges = mc.syncGetObjects (self.mch, "bridge") for bridge in bridges: @@ -158,18 +158,18 @@ class RouteManager: res = mc.syncCallMethod (self.mch, bridge.id, bridge.classKey, "close") if res.status != 0: print "Error closing bridge: %d - %s" % (res.status, res.statusText) - exit (1) + sys.exit (1) if len (bridges) == 1: if _verbose: print "Last bridge on link, closing link..." res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") if res.status != 0: print "Error closing link: %d - %s" % (res.status, res.statusText) - exit (1) - exit (0) + sys.exit (1) + sys.exit (0) if not _quiet: print "Route not found" - exit (1) + sys.exit (1) def ListRoutes (self): mc = self.mclient -- cgit v1.2.1 From 32863060c89c7101f5db7d962ee3f689fd38323a Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 11 Apr 2008 10:02:49 +0000 Subject: QPID-913: committed patch from tross@redhat.com git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@647099 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 4 + qpid/python/commands/qpid-tool | 179 ++++++++++ qpid/python/mgmt-cli/disp.py | 77 ---- qpid/python/mgmt-cli/main.py | 182 ---------- qpid/python/mgmt-cli/managementdata.py | 601 ------------------------------- qpid/python/qpid/disp.py | 77 ++++ qpid/python/qpid/managementdata.py | 621 +++++++++++++++++++++++++++++++++ 7 files changed, 881 insertions(+), 860 deletions(-) create mode 100644 qpid/python/commands/qpid-tool delete mode 100644 qpid/python/mgmt-cli/disp.py delete mode 100755 qpid/python/mgmt-cli/main.py delete mode 100644 qpid/python/mgmt-cli/managementdata.py create mode 100644 qpid/python/qpid/disp.py create mode 100644 qpid/python/qpid/managementdata.py (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index c69ca6204f..0db28c791b 100644 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -160,6 +160,9 @@ class RouteManager: print "Error closing bridge: %d - %s" % (res.status, res.statusText) sys.exit (1) if len (bridges) == 1: + link = self.getLink () + if link == None: + sys.exit (0) if _verbose: print "Last bridge on link, closing link..." res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") @@ -208,6 +211,7 @@ class RouteManager: elif _verbose: print "Ok" + links = mc.syncGetObjects (self.mch, "link") for link in links: if _verbose: print "Deleting Link: %s... " % link.address, diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool new file mode 100644 index 0000000000..0983e1b8af --- /dev/null +++ b/qpid/python/commands/qpid-tool @@ -0,0 +1,179 @@ +#!/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 os +import getopt +import sys +import socket +from cmd import Cmd +from qpid.managementdata import ManagementData +from shlex import split +from qpid.disp import Display +from qpid.peer import Closed + +class Mcli (Cmd): + """ Management Command Interpreter """ + prompt = "qpid: " + + def __init__ (self, dataObject, dispObject): + Cmd.__init__ (self) + self.dataObject = dataObject + self.dispObject = dispObject + + def emptyline (self): + pass + + def do_help (self, data): + print "Management Tool for QPID" + print + print "Commands:" + print " list - Print summary of existing objects by class" + print " list - Print list of objects of the specified class" + print " list all - Print contents of all objects of specified class" + print " list active - Print contents of all non-deleted objects of specified class" + print " list - Print contents of one or more objects (infer className)" + print " list - Print contents of one or more objects" + print " list is space-separated, ranges may be specified (i.e. 1004-1010)" + print " call [] - Invoke a method on an object" + print " schema - Print summary of object classes seen on the target" + print " schema - Print details of an object class" + print " set time-format short - Select short timestamp format (default)" + print " set time-format long - Select long timestamp format" + print " quit or ^D - Exit the program" + print + + def complete_set (self, text, line, begidx, endidx): + """ Command completion for the 'set' command """ + tokens = split (line) + if len (tokens) < 2: + return ["time-format "] + elif tokens[1] == "time-format": + if len (tokens) == 2: + return ["long", "short"] + elif len (tokens) == 3: + if "long".find (text) == 0: + return ["long"] + elif "short".find (text) == 0: + return ["short"] + elif "time-format".find (text) == 0: + return ["time-format "] + return [] + + def do_set (self, data): + tokens = split (data) + try: + if tokens[0] == "time-format": + self.dispObject.do_setTimeFormat (tokens[1]) + except: + pass + + def complete_schema (self, text, line, begidx, endidx): + tokens = split (line) + if len (tokens) > 2: + return [] + return self.dataObject.classCompletions (text) + + def do_schema (self, data): + self.dataObject.do_schema (data) + + def complete_list (self, text, line, begidx, endidx): + tokens = split (line) + if len (tokens) > 2: + return [] + return self.dataObject.classCompletions (text) + + def do_list (self, data): + self.dataObject.do_list (data) + + def do_call (self, data): + try: + self.dataObject.do_call (data) + except ValueError, e: + print "ValueError:", e + + def do_EOF (self, data): + print "quit" + return True + + def do_quit (self, data): + return True + + def postcmd (self, stop, line): + return stop + + def postloop (self): + print "Exiting..." + self.dataObject.close () + +def Usage (): + print "Usage:", sys.argv[0], "[OPTIONS] []]" + print + print "Options:" + print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10-preview.xml)" + print + sys.exit (1) + +#========================================================= +# Main Program +#========================================================= + +# Get host name and port if specified on the command line +try: + longOpts = ("spec-file=") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], 's:', longOpts) +except: + Usage () + sys.exit (1) + +_specpath = "/usr/share/amqp/amqp.0-10-preview.xml" +_host = "localhost" + +for opt in optlist: + if opt[0] == "-s" or opt[0] == "--spec-file": + _specpath = opt[1] + +if len (cargs) > 0: + _host = cargs[0] + +disp = Display () + +# Attempt to make a connection to the target broker +try: + data = ManagementData (disp, _host, specfile=_specpath) +except socket.error, e: + print "Socket Error (%s):" % _host, e[1] + sys.exit (1) +except Closed, e: + if str(e).find ("Exchange not found") != -1: + print "Management not enabled on broker: Use '-m yes' option on broker startup." + sys.exit (1) +except IOError, e: + print "IOError: %d - %s: %s" % (e.errno, e.strerror, e.filename) + sys.exit (1) + +# Instantiate the CLI interpreter and launch it. +cli = Mcli (data, disp) +print ("Management Tool for QPID") +try: + cli.cmdloop () +except Closed, e: + print "Connection to Broker Lost:", e + sys.exit (1) diff --git a/qpid/python/mgmt-cli/disp.py b/qpid/python/mgmt-cli/disp.py deleted file mode 100644 index 5746a26e51..0000000000 --- a/qpid/python/mgmt-cli/disp.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/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. -# - -from time import strftime, gmtime - -class Display: - """ Display formatting for QPID Management CLI """ - - def __init__ (self): - self.tableSpacing = 2 - self.tablePrefix = " " - self.timestampFormat = "%X" - - def table (self, title, heads, rows): - """ Print a formatted table with autosized columns """ - print title - if len (rows) == 0: - return - colWidth = [] - col = 0 - line = self.tablePrefix - for head in heads: - width = len (head) - for row in rows: - cellWidth = len (str (row[col])) - if cellWidth > width: - width = cellWidth - colWidth.append (width + self.tableSpacing) - line = line + head - for i in range (colWidth[col] - len (head)): - line = line + " " - col = col + 1 - print line - line = self.tablePrefix - for width in colWidth: - for i in range (width): - line = line + "=" - print line - - for row in rows: - line = self.tablePrefix - col = 0 - for width in colWidth: - line = line + str (row[col]) - for i in range (width - len (str (row[col]))): - line = line + " " - col = col + 1 - print line - - def do_setTimeFormat (self, fmt): - """ Select timestamp format """ - if fmt == "long": - self.timestampFormat = "%c" - elif fmt == "short": - self.timestampFormat = "%X" - - def timestamp (self, nsec): - """ Format a nanosecond-since-the-epoch timestamp for printing """ - return strftime (self.timestampFormat, gmtime (nsec / 1000000000)) diff --git a/qpid/python/mgmt-cli/main.py b/qpid/python/mgmt-cli/main.py deleted file mode 100755 index f4c22012eb..0000000000 --- a/qpid/python/mgmt-cli/main.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/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 os -import getopt -import sys -import socket -from cmd import Cmd -from managementdata import ManagementData -from shlex import split -from disp import Display -from qpid.peer import Closed - -class Mcli (Cmd): - """ Management Command Interpreter """ - prompt = "qpid: " - - def __init__ (self, dataObject, dispObject): - Cmd.__init__ (self) - self.dataObject = dataObject - self.dispObject = dispObject - - def emptyline (self): - pass - - def do_help (self, data): - print "Management Tool for QPID" - print - print "Commands:" - print " list - Print summary of existing objects by class" - print " list - Print list of objects of the specified class" - print " list all - Print contents of all objects of specified class" - print " list active - Print contents of all non-deleted objects of specified class" - print " list - Print contents of one or more objects (infer className)" - print " list - Print contents of one or more objects" - print " list is space-separated, ranges may be specified (i.e. 1004-1010)" - print " call [] - Invoke a method on an object" - print " schema - Print summary of object classes seen on the target" - print " schema - Print details of an object class" - print " set time-format short - Select short timestamp format (default)" - print " set time-format long - Select long timestamp format" - print " quit or ^D - Exit the program" - print - - def complete_set (self, text, line, begidx, endidx): - """ Command completion for the 'set' command """ - tokens = split (line) - if len (tokens) < 2: - return ["time-format "] - elif tokens[1] == "time-format": - if len (tokens) == 2: - return ["long", "short"] - elif len (tokens) == 3: - if "long".find (text) == 0: - return ["long"] - elif "short".find (text) == 0: - return ["short"] - elif "time-format".find (text) == 0: - return ["time-format "] - return [] - - def do_set (self, data): - tokens = split (data) - try: - if tokens[0] == "time-format": - self.dispObject.do_setTimeFormat (tokens[1]) - except: - pass - - def complete_schema (self, text, line, begidx, endidx): - tokens = split (line) - if len (tokens) > 2: - return [] - return self.dataObject.classCompletions (text) - - def do_schema (self, data): - self.dataObject.do_schema (data) - - def complete_list (self, text, line, begidx, endidx): - tokens = split (line) - if len (tokens) > 2: - return [] - return self.dataObject.classCompletions (text) - - def do_list (self, data): - self.dataObject.do_list (data) - - def do_call (self, data): - try: - self.dataObject.do_call (data) - except ValueError, e: - print "ValueError:", e - - def do_EOF (self, data): - print "quit" - return True - - def do_quit (self, data): - return True - - def postcmd (self, stop, line): - return stop - - def postloop (self): - print "Exiting..." - self.dataObject.close () - -def Usage (): - print "Usage:", sys.argv[0], "[OPTIONS] [ []]" - print - print "Options:" - print " -s default: /usr/share/amqp/amqp.0-10-preview.xml" - print - sys.exit (1) - -#========================================================= -# Main Program -#========================================================= - -# Get host name and port if specified on the command line -try: - (optlist, cargs) = getopt.getopt (sys.argv[1:], 's:') -except: - Usage () - exit (1) - -specpath = "/usr/share/amqp/amqp.0-10-preview.xml" -host = "localhost" -port = 5672 - -for opt in optlist: - if opt[0] == "-s": - specpath = opt[1] - -if len (cargs) > 0: - host = cargs[0] - -if len (cargs) > 1: - port = int (cargs[1]) - -disp = Display () - -# Attempt to make a connection to the target broker -try: - data = ManagementData (disp, host, port, specfile=specpath) -except socket.error, e: - print "Socket Error:", e[1] - sys.exit (1) -except Closed, e: - if str(e).find ("Exchange not found") != -1: - print "Management not enabled on broker: Use '-m yes' option on broker startup." - sys.exit (1) -except IOError, e: - print "IOError: %d - %s: %s" % (e.errno, e.strerror, e.filename) - sys.exit (1) - -# Instantiate the CLI interpreter and launch it. -cli = Mcli (data, disp) -print ("Management Tool for QPID") -try: - cli.cmdloop () -except Closed, e: - print "Connection to Broker Lost:", e - exit (1) diff --git a/qpid/python/mgmt-cli/managementdata.py b/qpid/python/mgmt-cli/managementdata.py deleted file mode 100644 index 00fc0ec09e..0000000000 --- a/qpid/python/mgmt-cli/managementdata.py +++ /dev/null @@ -1,601 +0,0 @@ -#!/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 qpid -from qpid.management import managementChannel, managementClient -from threading import Lock -from disp import Display -from shlex import split -from qpid.client import Client - -class ManagementData: - - # - # Data Structure: - # - # Please note that this data structure holds only the most recent - # configuration and instrumentation data for each object. It does - # not hold the detailed historical data that is sent from the broker. - # The only historical data it keeps are the high and low watermarks - # for hi-lo statistics. - # - # tables :== {class-key} - # {} - # (timestamp, config-record, inst-record) - # class-key :== (, , ) - # timestamp :== (, , ) - # config-record :== [element] - # inst-record :== [element] - # element :== (, ) - # - - def registerObjId (self, objId): - if self.baseId == 0: - if objId & 0x8000000000000000L == 0: - self.baseId = objId - 1000 - - def displayObjId (self, objId): - if objId & 0x8000000000000000L == 0: - return objId - self.baseId - return (objId & 0x7fffffffffffffffL) + 5000 - - def rawObjId (self, displayId): - if displayId < 5000: - return displayId + self.baseId - return displayId - 5000 + 0x8000000000000000L - - def displayClassName (self, cls): - (packageName, className, hash) = cls - return packageName + "." + className - - def dataHandler (self, context, className, list, timestamps): - """ Callback for configuration and instrumentation data updates """ - self.lock.acquire () - try: - # If this class has not been seen before, create an empty dictionary to - # hold objects of this class - if className not in self.tables: - self.tables[className] = {} - - # Register the ID so a more friendly presentation can be displayed - id = long (list[0][1]) - self.registerObjId (id) - - # If this object hasn't been seen before, create a new object record with - # the timestamps and empty lists for configuration and instrumentation data. - if id not in self.tables[className]: - self.tables[className][id] = (timestamps, [], []) - - (unused, oldConf, oldInst) = self.tables[className][id] - - # For config updates, simply replace old config list with the new one. - if context == 0: #config - self.tables[className][id] = (timestamps, list, oldInst) - - # For instrumentation updates, carry the minimum and maximum values for - # "hi-lo" stats forward. - elif context == 1: #inst - if len (oldInst) == 0: - newInst = list - else: - newInst = [] - for idx in range (len (list)): - (key, value) = list[idx] - if key.find ("High") == len (key) - 4: - if oldInst[idx][1] > value: - value = oldInst[idx][1] - if key.find ("Low") == len (key) - 3: - if oldInst[idx][1] < value: - value = oldInst[idx][1] - newInst.append ((key, value)) - self.tables[className][id] = (timestamps, oldConf, newInst) - - finally: - self.lock.release () - - def ctrlHandler (self, context, op, data): - if op == self.mclient.CTRL_BROKER_INFO: - pass - - def configHandler (self, context, className, list, timestamps): - self.dataHandler (0, className, list, timestamps); - - def instHandler (self, context, className, list, timestamps): - self.dataHandler (1, className, list, timestamps); - - def methodReply (self, broker, sequence, status, sText, args): - """ Callback for method-reply messages """ - self.lock.acquire () - try: - line = "Call Result: " + self.methodsPending[sequence] + \ - " " + str (status) + " (" + sText + ")" - print line, args - del self.methodsPending[sequence] - finally: - self.lock.release () - - def schemaHandler (self, context, className, configs, insts, methods, events): - """ Callback for schema updates """ - if className not in self.schema: - self.schema[className] = (configs, insts, methods, events) - - def __init__ (self, disp, host, port=5672, username="guest", password="guest", - specfile="../../specs/amqp.0-10-preview.xml"): - self.spec = qpid.spec.load (specfile) - self.lock = Lock () - self.tables = {} - self.schema = {} - self.baseId = 0 - self.disp = disp - self.lastUnit = None - self.methodSeq = 1 - self.methodsPending = {} - - self.client = Client (host, port, self.spec) - self.client.start ({"LOGIN": username, "PASSWORD": password}) - self.channel = self.client.channel (1) - - self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, - self.instHandler, self.methodReply) - self.mclient.schemaListener (self.schemaHandler) - self.mch = self.mclient.addChannel (self.channel) - - def close (self): - self.mclient.removeChannel (self.mch) - - def refName (self, oid): - if oid == 0: - return "NULL" - return str (self.displayObjId (oid)) - - def valueDisplay (self, classKey, key, value): - for kind in range (2): - schema = self.schema[classKey][kind] - for item in schema: - if item[0] == key: - typecode = item[1] - unit = item[2] - if (typecode >= 1 and typecode <= 5) or typecode >= 12: # numerics - if unit == None or unit == self.lastUnit: - return str (value) - else: - self.lastUnit = unit - suffix = "" - if value != 1: - suffix = "s" - return str (value) + " " + unit + suffix - elif typecode == 6 or typecode == 7: # strings - return value - elif typecode == 8: - if value == 0: - return "--" - return self.disp.timestamp (value) - elif typecode == 9: - return str (value) - elif typecode == 10: - return self.refName (value) - elif typecode == 11: - if value == 0: - return "False" - else: - return "True" - elif typecode == 14: - return str (UUID (bytes=value)) - elif typecode == 15: - return str (value) - return "*type-error*" - - def getObjIndex (self, className, config): - """ Concatenate the values from index columns to form a unique object name """ - result = "" - schemaConfig = self.schema[className][0] - for item in schemaConfig: - if item[5] == 1 and item[0] != "id": - if result != "": - result = result + "." - for key,val in config: - if key == item[0]: - result = result + self.valueDisplay (className, key, val) - return result - - def getClassKey (self, className): - dotPos = className.find(".") - if dotPos == -1: - for key in self.schema: - if key[1] == className: - return key - else: - package = className[0:dotPos] - name = className[dotPos + 1:] - for key in self.schema: - if key[0] == package and key[1] == name: - return key - return None - - def classCompletions (self, prefix): - """ Provide a list of candidate class names for command completion """ - self.lock.acquire () - complist = [] - try: - for name in self.tables: - if name.find (prefix) == 0: - complist.append (name) - finally: - self.lock.release () - return complist - - def typeName (self, typecode): - """ Convert type-codes to printable strings """ - if typecode == 1: - return "uint8" - elif typecode == 2: - return "uint16" - elif typecode == 3: - return "uint32" - elif typecode == 4: - return "uint64" - elif typecode == 5: - return "bool" - elif typecode == 6: - return "short-string" - elif typecode == 7: - return "long-string" - elif typecode == 8: - return "abs-time" - elif typecode == 9: - return "delta-time" - elif typecode == 10: - return "reference" - elif typecode == 11: - return "boolean" - elif typecode == 12: - return "float" - elif typecode == 13: - return "double" - elif typecode == 14: - return "uuid" - elif typecode == 15: - return "field-table" - else: - raise ValueError ("Invalid type code: %d" % typecode) - - def accessName (self, code): - """ Convert element access codes to printable strings """ - if code == 1: - return "ReadCreate" - elif code == 2: - return "ReadWrite" - elif code == 3: - return "ReadOnly" - else: - raise ValueError ("Invalid access code: %d" %code) - - def notNone (self, text): - if text == None: - return "" - else: - return text - - def isOid (self, id): - for char in str (id): - if not char.isdigit () and not char == '-': - return False - return True - - def listOfIds (self, classKey, tokens): - """ Generate a tuple of object ids for a classname based on command tokens. """ - list = [] - if tokens[0] == "all": - for id in self.tables[classKey]: - list.append (self.displayObjId (id)) - - elif tokens[0] == "active": - for id in self.tables[classKey]: - if self.tables[classKey][id][0][2] == 0: - list.append (self.displayObjId (id)) - - else: - for token in tokens: - if self.isOid (token): - if token.find ("-") != -1: - ids = token.split("-", 2) - for id in range (int (ids[0]), int (ids[1]) + 1): - if self.getClassForId (self.rawObjId (long (id))) == classKey: - list.append (id) - else: - list.append (token) - - list.sort () - result = () - for item in list: - result = result + (item,) - return result - - def listClasses (self): - """ Generate a display of the list of classes """ - self.lock.acquire () - try: - rows = [] - sorted = self.tables.keys () - sorted.sort () - for name in sorted: - active = 0 - deleted = 0 - for record in self.tables[name]: - isdel = False - ts = self.tables[name][record][0] - if ts[2] > 0: - isdel = True - if isdel: - deleted = deleted + 1 - else: - active = active + 1 - rows.append ((self.displayClassName (name), active, deleted)) - self.disp.table ("Management Object Types:", - ("ObjectType", "Active", "Deleted"), rows) - finally: - self.lock.release () - - def listObjects (self, className): - """ Generate a display of a list of objects in a class """ - self.lock.acquire () - try: - classKey = self.getClassKey (className) - if classKey == None: - print ("Object type %s not known" % className) - else: - rows = [] - sorted = self.tables[classKey].keys () - sorted.sort () - for objId in sorted: - (ts, config, inst) = self.tables[classKey][objId] - createTime = self.disp.timestamp (ts[1]) - destroyTime = "-" - if ts[2] > 0: - destroyTime = self.disp.timestamp (ts[2]) - objIndex = self.getObjIndex (classKey, config) - row = (self.refName (objId), createTime, destroyTime, objIndex) - rows.append (row) - self.disp.table ("Objects of type %s.%s" % (classKey[0], classKey[1]), - ("ID", "Created", "Destroyed", "Index"), - rows) - finally: - self.lock.release () - - def showObjects (self, tokens): - """ Generate a display of object data for a particular class """ - self.lock.acquire () - try: - self.lastUnit = None - if self.isOid (tokens[0]): - if tokens[0].find ("-") != -1: - rootId = int (tokens[0][0:tokens[0].find ("-")]) - else: - rootId = int (tokens[0]) - - classKey = self.getClassForId (self.rawObjId (rootId)) - remaining = tokens - if classKey == None: - print "Id not known: %d" % int (tokens[0]) - raise ValueError () - else: - classKey = self.getClassKey (tokens[0]) - remaining = tokens[1:] - if classKey not in self.tables: - print "Class not known: %s" % tokens[0] - raise ValueError () - - userIds = self.listOfIds (classKey, remaining) - if len (userIds) == 0: - print "No object IDs supplied" - raise ValueError () - - ids = [] - for id in userIds: - if self.getClassForId (self.rawObjId (long (id))) == classKey: - ids.append (self.rawObjId (long (id))) - - rows = [] - timestamp = None - config = self.tables[classKey][ids[0]][1] - for eIdx in range (len (config)): - key = config[eIdx][0] - if key != "id": - row = ("config", key) - for id in ids: - if timestamp == None or \ - timestamp < self.tables[classKey][id][0][0]: - timestamp = self.tables[classKey][id][0][0] - (key, value) = self.tables[classKey][id][1][eIdx] - row = row + (self.valueDisplay (classKey, key, value),) - rows.append (row) - - inst = self.tables[classKey][ids[0]][2] - for eIdx in range (len (inst)): - key = inst[eIdx][0] - if key != "id": - row = ("inst", key) - for id in ids: - (key, value) = self.tables[classKey][id][2][eIdx] - row = row + (self.valueDisplay (classKey, key, value),) - rows.append (row) - - titleRow = ("Type", "Element") - for id in ids: - titleRow = titleRow + (self.refName (id),) - caption = "Object of type %s.%s:" % (classKey[0], classKey[1]) - if timestamp != None: - caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" - self.disp.table (caption, titleRow, rows) - - except: - pass - self.lock.release () - - def schemaSummary (self): - """ Generate a display of the list of classes in the schema """ - self.lock.acquire () - try: - rows = [] - sorted = self.schema.keys () - sorted.sort () - for classKey in sorted: - tuple = self.schema[classKey] - className = classKey[0] + "." + classKey[1] - row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) - rows.append (row) - self.disp.table ("Classes in Schema:", - ("Class", "ConfigElements", "InstElements", "Methods", "Events"), - rows) - finally: - self.lock.release () - - def schemaTable (self, className): - """ Generate a display of details of the schema of a particular class """ - self.lock.acquire () - try: - classKey = self.getClassKey (className) - if classKey == None: - print ("Class name %s not known" % className) - raise ValueError () - - rows = [] - for config in self.schema[classKey][0]: - name = config[0] - if name != "id": - typename = self.typeName(config[1]) - unit = self.notNone (config[2]) - desc = self.notNone (config[3]) - access = self.accessName (config[4]) - extra = "" - if config[5] == 1: - extra = extra + "index " - if config[6] != None: - extra = extra + "Min: " + str (config[6]) - if config[7] != None: - extra = extra + "Max: " + str (config[7]) - if config[8] != None: - extra = extra + "MaxLen: " + str (config[8]) - rows.append ((name, typename, unit, access, extra, desc)) - - for config in self.schema[classKey][1]: - name = config[0] - if name != "id": - typename = self.typeName(config[1]) - unit = self.notNone (config[2]) - desc = self.notNone (config[3]) - rows.append ((name, typename, unit, "", "", desc)) - - titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") - self.disp.table ("Schema for class '%s.%s':" % (classKey[0], classKey[1]), titles, rows) - - for mname in self.schema[classKey][2]: - (mdesc, args) = self.schema[classKey][2][mname] - caption = "\nMethod '%s' %s" % (mname, self.notNone (mdesc)) - rows = [] - for arg in args: - name = arg[0] - typename = self.typeName (arg[1]) - dir = arg[2] - unit = self.notNone (arg[3]) - desc = self.notNone (arg[4]) - extra = "" - if arg[5] != None: - extra = extra + "Min: " + str (arg[5]) - if arg[6] != None: - extra = extra + "Max: " + str (arg[6]) - if arg[7] != None: - extra = extra + "MaxLen: " + str (arg[7]) - if arg[8] != None: - extra = extra + "Default: " + str (arg[8]) - rows.append ((name, typename, dir, unit, extra, desc)) - titles = ("Argument", "Type", "Direction", "Unit", "Notes", "Description") - self.disp.table (caption, titles, rows) - - except: - pass - self.lock.release () - - def getClassForId (self, objId): - """ Given an object ID, return the class key for the referenced object """ - for classKey in self.tables: - if objId in self.tables[classKey]: - return classKey - return None - - def callMethod (self, userOid, methodName, args): - self.lock.acquire () - methodOk = True - try: - classKey = self.getClassForId (self.rawObjId (userOid)) - if classKey == None: - raise ValueError () - - if methodName not in self.schema[classKey][2]: - print "Method '%s' not valid for class '%s.%s'" % (methodName, classKey[0], classKey[1]) - raise ValueError () - - schemaMethod = self.schema[classKey][2][methodName] - if len (args) != len (schemaMethod[1]): - print "Wrong number of method args: Need %d, Got %d" % (len (schemaMethod[1]), len (args)) - raise ValueError () - - namedArgs = {} - for idx in range (len (args)): - namedArgs[schemaMethod[1][idx][0]] = args[idx] - - self.methodSeq = self.methodSeq + 1 - self.methodsPending[self.methodSeq] = methodName - except: - methodOk = False - self.lock.release () - if methodOk: -# try: - self.mclient.callMethod (self.mch, self.methodSeq, self.rawObjId (userOid), classKey, - methodName, namedArgs) -# except ValueError, e: -# print "Error invoking method:", e - - def do_list (self, data): - tokens = data.split () - if len (tokens) == 0: - self.listClasses () - elif len (tokens) == 1 and not self.isOid (tokens[0]): - self.listObjects (data) - else: - self.showObjects (tokens) - - def do_schema (self, data): - if data == "": - self.schemaSummary () - else: - self.schemaTable (data) - - def do_call (self, data): - tokens = data.split () - if len (tokens) < 2: - print "Not enough arguments supplied" - return - - userOid = long (tokens[0]) - methodName = tokens[1] - args = tokens[2:] - self.callMethod (userOid, methodName, args) diff --git a/qpid/python/qpid/disp.py b/qpid/python/qpid/disp.py new file mode 100644 index 0000000000..5746a26e51 --- /dev/null +++ b/qpid/python/qpid/disp.py @@ -0,0 +1,77 @@ +#!/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. +# + +from time import strftime, gmtime + +class Display: + """ Display formatting for QPID Management CLI """ + + def __init__ (self): + self.tableSpacing = 2 + self.tablePrefix = " " + self.timestampFormat = "%X" + + def table (self, title, heads, rows): + """ Print a formatted table with autosized columns """ + print title + if len (rows) == 0: + return + colWidth = [] + col = 0 + line = self.tablePrefix + for head in heads: + width = len (head) + for row in rows: + cellWidth = len (str (row[col])) + if cellWidth > width: + width = cellWidth + colWidth.append (width + self.tableSpacing) + line = line + head + for i in range (colWidth[col] - len (head)): + line = line + " " + col = col + 1 + print line + line = self.tablePrefix + for width in colWidth: + for i in range (width): + line = line + "=" + print line + + for row in rows: + line = self.tablePrefix + col = 0 + for width in colWidth: + line = line + str (row[col]) + for i in range (width - len (str (row[col]))): + line = line + " " + col = col + 1 + print line + + def do_setTimeFormat (self, fmt): + """ Select timestamp format """ + if fmt == "long": + self.timestampFormat = "%c" + elif fmt == "short": + self.timestampFormat = "%X" + + def timestamp (self, nsec): + """ Format a nanosecond-since-the-epoch timestamp for printing """ + return strftime (self.timestampFormat, gmtime (nsec / 1000000000)) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py new file mode 100644 index 0000000000..ff43352247 --- /dev/null +++ b/qpid/python/qpid/managementdata.py @@ -0,0 +1,621 @@ +#!/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 qpid +import socket +from qpid.management import managementChannel, managementClient +from threading import Lock +from disp import Display +from shlex import split +from qpid.client import Client + +class Broker: + def __init__ (self, text): + colon = text.find (":") + if colon == -1: + host = text + self.port = 5672 + else: + host = text[:colon] + self.port = int (text[colon+1:]) + self.host = socket.gethostbyname (host) + + def name (self): + return self.host + ":" + str (self.port) + +class ManagementData: + + # + # Data Structure: + # + # Please note that this data structure holds only the most recent + # configuration and instrumentation data for each object. It does + # not hold the detailed historical data that is sent from the broker. + # The only historical data it keeps are the high and low watermarks + # for hi-lo statistics. + # + # tables :== {class-key} + # {} + # (timestamp, config-record, inst-record) + # class-key :== (, , ) + # timestamp :== (, , ) + # config-record :== [element] + # inst-record :== [element] + # element :== (, ) + # + + def registerObjId (self, objId): + if self.baseId == 0: + if objId & 0x8000000000000000L == 0: + self.baseId = objId - 1000 + + def displayObjId (self, objId): + if objId & 0x8000000000000000L == 0: + return objId - self.baseId + return (objId & 0x7fffffffffffffffL) + 5000 + + def rawObjId (self, displayId): + if displayId < 5000: + return displayId + self.baseId + return displayId - 5000 + 0x8000000000000000L + + def displayClassName (self, cls): + (packageName, className, hash) = cls + return packageName + "." + className + + def dataHandler (self, context, className, list, timestamps): + """ Callback for configuration and instrumentation data updates """ + self.lock.acquire () + try: + # If this class has not been seen before, create an empty dictionary to + # hold objects of this class + if className not in self.tables: + self.tables[className] = {} + + # Register the ID so a more friendly presentation can be displayed + id = long (list[0][1]) + self.registerObjId (id) + + # If this object hasn't been seen before, create a new object record with + # the timestamps and empty lists for configuration and instrumentation data. + if id not in self.tables[className]: + self.tables[className][id] = (timestamps, [], []) + + (unused, oldConf, oldInst) = self.tables[className][id] + + # For config updates, simply replace old config list with the new one. + if context == 0: #config + self.tables[className][id] = (timestamps, list, oldInst) + + # For instrumentation updates, carry the minimum and maximum values for + # "hi-lo" stats forward. + elif context == 1: #inst + if len (oldInst) == 0: + newInst = list + else: + newInst = [] + for idx in range (len (list)): + (key, value) = list[idx] + if key.find ("High") == len (key) - 4: + if oldInst[idx][1] > value: + value = oldInst[idx][1] + if key.find ("Low") == len (key) - 3: + if oldInst[idx][1] < value: + value = oldInst[idx][1] + newInst.append ((key, value)) + self.tables[className][id] = (timestamps, oldConf, newInst) + + finally: + self.lock.release () + + def ctrlHandler (self, context, op, data): + if op == self.mclient.CTRL_BROKER_INFO: + pass + + def configHandler (self, context, className, list, timestamps): + self.dataHandler (0, className, list, timestamps); + + def instHandler (self, context, className, list, timestamps): + self.dataHandler (1, className, list, timestamps); + + def methodReply (self, broker, sequence, status, sText, args): + """ Callback for method-reply messages """ + self.lock.acquire () + try: + line = "Call Result: " + self.methodsPending[sequence] + \ + " " + str (status) + " (" + sText + ")" + print line, args + del self.methodsPending[sequence] + finally: + self.lock.release () + + def schemaHandler (self, context, className, configs, insts, methods, events): + """ Callback for schema updates """ + if className not in self.schema: + self.schema[className] = (configs, insts, methods, events) + + def __init__ (self, disp, host, username="guest", password="guest", + specfile="../../specs/amqp.0-10-preview.xml"): + self.spec = qpid.spec.load (specfile) + self.lock = Lock () + self.tables = {} + self.schema = {} + self.baseId = 0 + self.disp = disp + self.lastUnit = None + self.methodSeq = 1 + self.methodsPending = {} + + self.broker = Broker (host) + self.client = Client (self.broker.host, self.broker.port, self.spec) + self.client.start ({"LOGIN": username, "PASSWORD": password}) + self.channel = self.client.channel (1) + + self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, + self.instHandler, self.methodReply) + self.mclient.schemaListener (self.schemaHandler) + self.mch = self.mclient.addChannel (self.channel) + + def close (self): + self.mclient.removeChannel (self.mch) + + def refName (self, oid): + if oid == 0: + return "NULL" + return str (self.displayObjId (oid)) + + def valueDisplay (self, classKey, key, value): + for kind in range (2): + schema = self.schema[classKey][kind] + for item in schema: + if item[0] == key: + typecode = item[1] + unit = item[2] + if (typecode >= 1 and typecode <= 5) or typecode >= 12: # numerics + if unit == None or unit == self.lastUnit: + return str (value) + else: + self.lastUnit = unit + suffix = "" + if value != 1: + suffix = "s" + return str (value) + " " + unit + suffix + elif typecode == 6 or typecode == 7: # strings + return value + elif typecode == 8: + if value == 0: + return "--" + return self.disp.timestamp (value) + elif typecode == 9: + return str (value) + elif typecode == 10: + return self.refName (value) + elif typecode == 11: + if value == 0: + return "False" + else: + return "True" + elif typecode == 14: + return str (UUID (bytes=value)) + elif typecode == 15: + return str (value) + return "*type-error*" + + def getObjIndex (self, className, config): + """ Concatenate the values from index columns to form a unique object name """ + result = "" + schemaConfig = self.schema[className][0] + for item in schemaConfig: + if item[5] == 1 and item[0] != "id": + if result != "": + result = result + "." + for key,val in config: + if key == item[0]: + result = result + self.valueDisplay (className, key, val) + return result + + def getClassKey (self, className): + dotPos = className.find(".") + if dotPos == -1: + for key in self.schema: + if key[1] == className: + return key + else: + package = className[0:dotPos] + name = className[dotPos + 1:] + for key in self.schema: + if key[0] == package and key[1] == name: + return key + return None + + def classCompletions (self, prefix): + """ Provide a list of candidate class names for command completion """ + self.lock.acquire () + complist = [] + try: + for name in self.tables: + if name.find (prefix) == 0: + complist.append (name) + finally: + self.lock.release () + return complist + + def typeName (self, typecode): + """ Convert type-codes to printable strings """ + if typecode == 1: + return "uint8" + elif typecode == 2: + return "uint16" + elif typecode == 3: + return "uint32" + elif typecode == 4: + return "uint64" + elif typecode == 5: + return "bool" + elif typecode == 6: + return "short-string" + elif typecode == 7: + return "long-string" + elif typecode == 8: + return "abs-time" + elif typecode == 9: + return "delta-time" + elif typecode == 10: + return "reference" + elif typecode == 11: + return "boolean" + elif typecode == 12: + return "float" + elif typecode == 13: + return "double" + elif typecode == 14: + return "uuid" + elif typecode == 15: + return "field-table" + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def accessName (self, code): + """ Convert element access codes to printable strings """ + if code == 1: + return "ReadCreate" + elif code == 2: + return "ReadWrite" + elif code == 3: + return "ReadOnly" + else: + raise ValueError ("Invalid access code: %d" %code) + + def notNone (self, text): + if text == None: + return "" + else: + return text + + def isOid (self, id): + for char in str (id): + if not char.isdigit () and not char == '-': + return False + return True + + def listOfIds (self, classKey, tokens): + """ Generate a tuple of object ids for a classname based on command tokens. """ + list = [] + if tokens[0] == "all": + for id in self.tables[classKey]: + list.append (self.displayObjId (id)) + + elif tokens[0] == "active": + for id in self.tables[classKey]: + if self.tables[classKey][id][0][2] == 0: + list.append (self.displayObjId (id)) + + else: + for token in tokens: + if self.isOid (token): + if token.find ("-") != -1: + ids = token.split("-", 2) + for id in range (int (ids[0]), int (ids[1]) + 1): + if self.getClassForId (self.rawObjId (long (id))) == classKey: + list.append (id) + else: + list.append (token) + + list.sort () + result = () + for item in list: + result = result + (item,) + return result + + def listClasses (self): + """ Generate a display of the list of classes """ + self.lock.acquire () + try: + rows = [] + sorted = self.tables.keys () + sorted.sort () + for name in sorted: + active = 0 + deleted = 0 + for record in self.tables[name]: + isdel = False + ts = self.tables[name][record][0] + if ts[2] > 0: + isdel = True + if isdel: + deleted = deleted + 1 + else: + active = active + 1 + rows.append ((self.displayClassName (name), active, deleted)) + if len (rows) != 0: + self.disp.table ("Management Object Types:", + ("ObjectType", "Active", "Deleted"), rows) + else: + print "Waiting for next periodic update" + finally: + self.lock.release () + + def listObjects (self, className): + """ Generate a display of a list of objects in a class """ + self.lock.acquire () + try: + classKey = self.getClassKey (className) + if classKey == None: + print ("Object type %s not known" % className) + else: + rows = [] + if classKey in self.tables: + sorted = self.tables[classKey].keys () + sorted.sort () + for objId in sorted: + (ts, config, inst) = self.tables[classKey][objId] + createTime = self.disp.timestamp (ts[1]) + destroyTime = "-" + if ts[2] > 0: + destroyTime = self.disp.timestamp (ts[2]) + objIndex = self.getObjIndex (classKey, config) + row = (self.refName (objId), createTime, destroyTime, objIndex) + rows.append (row) + self.disp.table ("Objects of type %s.%s" % (classKey[0], classKey[1]), + ("ID", "Created", "Destroyed", "Index"), + rows) + finally: + self.lock.release () + + def showObjects (self, tokens): + """ Generate a display of object data for a particular class """ + self.lock.acquire () + try: + self.lastUnit = None + if self.isOid (tokens[0]): + if tokens[0].find ("-") != -1: + rootId = int (tokens[0][0:tokens[0].find ("-")]) + else: + rootId = int (tokens[0]) + + classKey = self.getClassForId (self.rawObjId (rootId)) + remaining = tokens + if classKey == None: + print "Id not known: %d" % int (tokens[0]) + raise ValueError () + else: + classKey = self.getClassKey (tokens[0]) + remaining = tokens[1:] + if classKey not in self.tables: + print "Class not known: %s" % tokens[0] + raise ValueError () + + userIds = self.listOfIds (classKey, remaining) + if len (userIds) == 0: + print "No object IDs supplied" + raise ValueError () + + ids = [] + for id in userIds: + if self.getClassForId (self.rawObjId (long (id))) == classKey: + ids.append (self.rawObjId (long (id))) + + rows = [] + timestamp = None + config = self.tables[classKey][ids[0]][1] + for eIdx in range (len (config)): + key = config[eIdx][0] + if key != "id": + row = ("config", key) + for id in ids: + if timestamp == None or \ + timestamp < self.tables[classKey][id][0][0]: + timestamp = self.tables[classKey][id][0][0] + (key, value) = self.tables[classKey][id][1][eIdx] + row = row + (self.valueDisplay (classKey, key, value),) + rows.append (row) + + inst = self.tables[classKey][ids[0]][2] + for eIdx in range (len (inst)): + key = inst[eIdx][0] + if key != "id": + row = ("inst", key) + for id in ids: + (key, value) = self.tables[classKey][id][2][eIdx] + row = row + (self.valueDisplay (classKey, key, value),) + rows.append (row) + + titleRow = ("Type", "Element") + for id in ids: + titleRow = titleRow + (self.refName (id),) + caption = "Object of type %s.%s:" % (classKey[0], classKey[1]) + if timestamp != None: + caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" + self.disp.table (caption, titleRow, rows) + + except: + pass + self.lock.release () + + def schemaSummary (self): + """ Generate a display of the list of classes in the schema """ + self.lock.acquire () + try: + rows = [] + sorted = self.schema.keys () + sorted.sort () + for classKey in sorted: + tuple = self.schema[classKey] + className = classKey[0] + "." + classKey[1] + row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) + rows.append (row) + self.disp.table ("Classes in Schema:", + ("Class", "ConfigElements", "InstElements", "Methods", "Events"), + rows) + finally: + self.lock.release () + + def schemaTable (self, className): + """ Generate a display of details of the schema of a particular class """ + self.lock.acquire () + try: + classKey = self.getClassKey (className) + if classKey == None: + print ("Class name %s not known" % className) + raise ValueError () + + rows = [] + for config in self.schema[classKey][0]: + name = config[0] + if name != "id": + typename = self.typeName(config[1]) + unit = self.notNone (config[2]) + desc = self.notNone (config[3]) + access = self.accessName (config[4]) + extra = "" + if config[5] == 1: + extra = extra + "index " + if config[6] != None: + extra = extra + "Min: " + str (config[6]) + if config[7] != None: + extra = extra + "Max: " + str (config[7]) + if config[8] != None: + extra = extra + "MaxLen: " + str (config[8]) + rows.append ((name, typename, unit, access, extra, desc)) + + for config in self.schema[classKey][1]: + name = config[0] + if name != "id": + typename = self.typeName(config[1]) + unit = self.notNone (config[2]) + desc = self.notNone (config[3]) + rows.append ((name, typename, unit, "", "", desc)) + + titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") + self.disp.table ("Schema for class '%s.%s':" % (classKey[0], classKey[1]), titles, rows) + + for mname in self.schema[classKey][2]: + (mdesc, args) = self.schema[classKey][2][mname] + caption = "\nMethod '%s' %s" % (mname, self.notNone (mdesc)) + rows = [] + for arg in args: + name = arg[0] + typename = self.typeName (arg[1]) + dir = arg[2] + unit = self.notNone (arg[3]) + desc = self.notNone (arg[4]) + extra = "" + if arg[5] != None: + extra = extra + "Min: " + str (arg[5]) + if arg[6] != None: + extra = extra + "Max: " + str (arg[6]) + if arg[7] != None: + extra = extra + "MaxLen: " + str (arg[7]) + if arg[8] != None: + extra = extra + "Default: " + str (arg[8]) + rows.append ((name, typename, dir, unit, extra, desc)) + titles = ("Argument", "Type", "Direction", "Unit", "Notes", "Description") + self.disp.table (caption, titles, rows) + + except: + pass + self.lock.release () + + def getClassForId (self, objId): + """ Given an object ID, return the class key for the referenced object """ + for classKey in self.tables: + if objId in self.tables[classKey]: + return classKey + return None + + def callMethod (self, userOid, methodName, args): + self.lock.acquire () + methodOk = True + try: + classKey = self.getClassForId (self.rawObjId (userOid)) + if classKey == None: + raise ValueError () + + if methodName not in self.schema[classKey][2]: + print "Method '%s' not valid for class '%s.%s'" % (methodName, classKey[0], classKey[1]) + raise ValueError () + + schemaMethod = self.schema[classKey][2][methodName] + if len (args) != len (schemaMethod[1]): + print "Wrong number of method args: Need %d, Got %d" % (len (schemaMethod[1]), len (args)) + raise ValueError () + + namedArgs = {} + for idx in range (len (args)): + namedArgs[schemaMethod[1][idx][0]] = args[idx] + + self.methodSeq = self.methodSeq + 1 + self.methodsPending[self.methodSeq] = methodName + except: + methodOk = False + self.lock.release () + if methodOk: +# try: + self.mclient.callMethod (self.mch, self.methodSeq, self.rawObjId (userOid), classKey, + methodName, namedArgs) +# except ValueError, e: +# print "Error invoking method:", e + + def do_list (self, data): + tokens = data.split () + if len (tokens) == 0: + self.listClasses () + elif len (tokens) == 1 and not self.isOid (tokens[0]): + self.listObjects (data) + else: + self.showObjects (tokens) + + def do_schema (self, data): + if data == "": + self.schemaSummary () + else: + self.schemaTable (data) + + def do_call (self, data): + tokens = data.split () + if len (tokens) < 2: + print "Not enough arguments supplied" + return + + userOid = long (tokens[0]) + methodName = tokens[1] + args = tokens[2:] + self.callMethod (userOid, methodName, args) -- cgit v1.2.1 From 6a19e26beb8c473f985bc184deb41e6c7e6bdfb2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 11 Apr 2008 11:44:12 +0000 Subject: Set executable property for commands git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@647123 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 0 qpid/python/commands/qpid-tool | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 qpid/python/commands/qpid-route mode change 100644 => 100755 qpid/python/commands/qpid-tool (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route old mode 100644 new mode 100755 diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool old mode 100644 new mode 100755 -- cgit v1.2.1 From 0b90b60d4dbcf82b5770652c5b19e940b96f28d2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 14 Apr 2008 08:57:06 +0000 Subject: QPID-917: Use PLAIN (rather than the non-standard AMQPLAIN) as the SASL mechanism when authenticating python test clients. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@647704 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index b6cf6d7446..931face3a4 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -192,7 +192,10 @@ Options: user = user or self.user password = password or self.password client = qpid.client.Client(host, port, spec) - client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params) + if self.use08spec(): + client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params) + else: + client.start("\x00" + user + "\x00" + password, mechanism="PLAIN", tune_params=tune_params) return client def get_spec_file(self, fname): -- cgit v1.2.1 From e9172bcdf62db1ed07d64ab10d8850cbaad9046b Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 14 Apr 2008 16:33:40 +0000 Subject: fixed encode/decode of structs in command/control arguments to include the type code when specified git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@647887 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 8 ++++---- qpid/python/qpid/spec010.py | 5 +++++ qpid/python/tests/spec010.py | 10 +++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 0e4244fb75..f82a7a49dc 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -195,21 +195,21 @@ class Codec(Packer): def read_control(self): cntrl = self.spec.controls[self.read_uint16()] - return cntrl.decode(self) + return Struct(cntrl, **cntrl.decode_fields(self)) def write_control(self, ctrl): type = ctrl._type self.write_uint16(type.code) - type.encode(self, ctrl) + type.encode_fields(self, ctrl) def read_command(self): type = self.spec.commands[self.read_uint16()] hdr = self.spec["session.header"].decode(self) - cmd = type.decode(self) + cmd = Struct(type, **type.decode_fields(self)) return hdr, cmd def write_command(self, hdr, cmd): self.write_uint16(cmd._type.code) hdr._type.encode(self, hdr) - cmd._type.encode(self, cmd) + cmd._type.encode_fields(self, cmd) def read_size(self, width): if width > 0: diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 4eb03008d0..03f60edc4e 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -192,6 +192,9 @@ class Composite(Type, Coded): def decode(self, codec): codec.read_size(self.size) + if self.code is not None: + code = codec.read_uint16() + assert self.code == code return datatypes.Struct(self, **self.decode_fields(codec)) def decode_fields(self, codec): @@ -211,6 +214,8 @@ class Composite(Type, Coded): def encode(self, codec, value): sc = StringCodec(self.spec) + if self.code is not None: + sc.write_uint16(self.code) self.encode_fields(sc, value) codec.write_size(self.size, len(sc.encoded)) codec.write(sc.encoded) diff --git a/qpid/python/tests/spec010.py b/qpid/python/tests/spec010.py index 4161dc060f..ff29bd8cea 100644 --- a/qpid/python/tests/spec010.py +++ b/qpid/python/tests/spec010.py @@ -26,7 +26,7 @@ from qpid.datatypes import Struct class SpecTest(TestCase): def setUp(self): - self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) + self.spec = load(testrunner.get_spec_file("amqp.0-10-qpid-errata.xml")) def testSessionHeader(self): hdr = self.spec["session.header"] @@ -62,3 +62,11 @@ class SpecTest(TestCase): dec = self.encdec(self.spec["message.subscribe"], cmd) assert cmd.exclusive == dec.exclusive assert cmd.destination == dec.destination + + def testXid(self): + xid = self.spec["dtx.xid"] + sc = StringCodec(self.spec) + st = Struct(xid, format=0, global_id="gid", branch_id="bid") + xid.encode(sc, st) + assert sc.encoded == '\x00\x00\x00\x10\x06\x04\x07\x00\x00\x00\x00\x00\x03gid\x03bid' + assert xid.decode(sc).__dict__ == st.__dict__ -- cgit v1.2.1 From c886f05e4a84202803090adb0312450f3ea5a354 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 14 Apr 2008 16:56:35 +0000 Subject: Use the errata file for final 0-10 that has a type code for xids without which dtx.recover can't work. Return the indoubt xids as an array of struct32s each of which contains an encoded xid. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@647903 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 931face3a4..a0018c671f 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -132,6 +132,8 @@ Options: # Abbreviations for default settings. if (self.specfile == "0-10"): self.spec = load(self.get_spec_file("amqp.0-10.xml")) + elif (self.specfile == "0-10-errata"): + self.spec = load(self.get_spec_file("amqp.0-10-qpid-errata.xml")) else: if (self.specfile == "0-8"): self.specfile = self.get_spec_file("amqp.0-8.xml") -- cgit v1.2.1 From b8c35ee8da8ad85f679894197d1764c4c127c22c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 14 Apr 2008 21:11:25 +0000 Subject: * Fix interpretation of accept-mode, 0 == EXPLICIT * Ensure accepts are taken into account in command sequence git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@647999 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index bbe2b326d6..427a403b90 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -281,6 +281,9 @@ class Delegate: def __init__(self, session): self.session = session + #XXX: do something with incoming accepts + def message_accept(self, ma): None + def execution_result(self, er): future = self.session.results.pop(er.command_id) future.set(er.value) -- cgit v1.2.1 From 0cb55b441ec82124319fd3b154261a07ded82df2 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Tue, 15 Apr 2008 16:12:01 +0000 Subject: QPID-921: applied qpid-patch36.diff on behalf of Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@648308 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 10 +++++++--- qpid/python/commands/qpid-route | 3 ++- qpid/python/qpid/management.py | 8 ++++---- qpid/python/qpid/managementdata.py | 33 ++++++++++++++++++++------------- 4 files changed, 33 insertions(+), 21 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 3fd8e93c63..b93e74c4d2 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -97,13 +97,17 @@ class BrokerManager: try: self.spec = qpid.spec.load (_specpath) self.client = Client (self.broker.host, self.broker.port, self.spec) - self.client.start ({"LOGIN":"guest","PASSWORD":"guest"}) + self.client.start (response='\x00' + "guest" + '\x00' + "guest", + mechanism="PLAIN") self.channel = self.client.channel (1) self.mclient = managementClient (self.spec) self.mchannel = self.mclient.addChannel (self.channel) except socket.error, e: - print "Connect Error:", e - exit (1) + print "Socket Error:", e + sys.exit (1) + except Closed, e: + print "Connect Failed:", e + sys.exit (1) def Overview (self): self.ConnectToBroker () diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 0db28c791b..c268c638c8 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -79,7 +79,8 @@ class RouteManager: try: self.spec = qpid.spec.load (_specpath) self.client = Client (broker.host, broker.port, self.spec) - self.client.start ({"LOGIN":"guest","PASSWORD":"guest"}) + self.client.start (response='\x00' + "guest" + '\x00' + "guest", + mechanism="PLAIN") self.channel = self.client.channel (1) self.mclient = managementClient (self.spec) self.mch = self.mclient.addChannel (self.channel) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index d2dcacd62a..d32d458270 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -83,11 +83,11 @@ class methodResult: class managementChannel: """ This class represents a connection to an AMQP broker. """ - def __init__ (self, ch, topicCb, replyCb, cbContext): + def __init__ (self, ch, topicCb, replyCb, cbContext, _detlife=0): """ Given a channel on an established AMQP broker connection, this method opens a session and performs all of the declarations and bindings needed to participate in the management protocol. """ - response = ch.session_open (detached_lifetime=300) + response = ch.session_open (detached_lifetime=_detlife) self.sessionId = response.session_id self.topicName = "mgmt-%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", response.session_id) self.replyName = "repl-%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", response.session_id) @@ -97,8 +97,8 @@ class managementChannel: self.context = cbContext self.reqsOutstanding = 0 - ch.queue_declare (queue=self.topicName, exclusive=1, auto_delete=1) - ch.queue_declare (queue=self.replyName, exclusive=1, auto_delete=1) + ch.queue_declare (queue=self.topicName, exclusive=True, auto_delete=True) + ch.queue_declare (queue=self.replyName, exclusive=True, auto_delete=True) ch.queue_bind (exchange="qpid.management", queue=self.topicName, routing_key="mgmt.#") diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index ff43352247..2876bf0948 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -21,6 +21,7 @@ import qpid import socket +import struct from qpid.management import managementChannel, managementClient from threading import Lock from disp import Display @@ -63,19 +64,24 @@ class ManagementData: # def registerObjId (self, objId): - if self.baseId == 0: - if objId & 0x8000000000000000L == 0: - self.baseId = objId - 1000 + boot = objId & 0x7FFF000000000000L + if boot == 0: + return + self.bootSequence = boot def displayObjId (self, objId): - if objId & 0x8000000000000000L == 0: - return objId - self.baseId - return (objId & 0x7fffffffffffffffL) + 5000 + bank = (objId & 0x0000FFFFFF000000L) >> 24 + id = objId & 0x0000000000FFFFFFL + return bank * 1000 + id def rawObjId (self, displayId): - if displayId < 5000: - return displayId + self.baseId - return displayId - 5000 + 0x8000000000000000L + bank = displayId / 1000 + id = displayId % 1000 + if bank < 3: + objId = (bank << 24) + id + else: + objId = self.bootSequence + (bank << 24) + id + return objId def displayClassName (self, cls): (packageName, className, hash) = cls @@ -158,7 +164,7 @@ class ManagementData: self.lock = Lock () self.tables = {} self.schema = {} - self.baseId = 0 + self.bootSequence = 0 self.disp = disp self.lastUnit = None self.methodSeq = 1 @@ -166,7 +172,8 @@ class ManagementData: self.broker = Broker (host) self.client = Client (self.broker.host, self.broker.port, self.spec) - self.client.start ({"LOGIN": username, "PASSWORD": password}) + self.client.start (response='\x00' + username + '\x00' + password, + mechanism="PLAIN") self.channel = self.client.channel (1) self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, @@ -189,7 +196,7 @@ class ManagementData: if item[0] == key: typecode = item[1] unit = item[2] - if (typecode >= 1 and typecode <= 5) or typecode >= 12: # numerics + if (typecode >= 1 and typecode <= 5) or typecode == 12 or typecode == 13: # numerics if unit == None or unit == self.lastUnit: return str (value) else: @@ -214,7 +221,7 @@ class ManagementData: else: return "True" elif typecode == 14: - return str (UUID (bytes=value)) + return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", value) elif typecode == 15: return str (value) return "*type-error*" -- cgit v1.2.1 From 2f4cc8f03bff93c4533f725f016bc87f54ae4fcd Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Fri, 18 Apr 2008 14:49:15 +0000 Subject: From Ted Ross: https://issues.apache.org/jira/browse/QPID-934 This patch fixes a problem related to multiple management sessions run over the same AMQP session (typically seen in test environments). git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@649554 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index d32d458270..6b25d5ea08 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -176,6 +176,7 @@ class managementClient: mch = managementChannel (channel, self.topicCb, self.replyCb, cbContext) self.channels.append (mch) + self.incOutstanding (mch) codec = Codec (StringIO (), self.spec) self.setHeader (codec, ord ('B')) msg = Content (codec.stream.getvalue ()) @@ -213,6 +214,10 @@ class managementClient: def syncWaitForStable (self, channel): """ Synchronous (blocking) call to wait for schema stability on a channel """ self.cv.acquire () + if channel.reqsOutstanding == 0: + self.cv.release () + return + self.syncInFlight = True starttime = time () while channel.reqsOutstanding != 0: @@ -470,7 +475,6 @@ class managementClient: sendCodec = Codec (StringIO (), self.spec) seq = self.seqMgr.reserve ("outstanding") self.setHeader (sendCodec, ord ('P'), seq) - self.incOutstanding (ch) smsg = Content (sendCodec.stream.getvalue ()) smsg["content_type"] = "application/octet-stream" smsg["routing_key"] = "agent" @@ -508,7 +512,8 @@ class managementClient: if (cname, hash) not in self.packages[pname]: # Send a schema request sendCodec = Codec (StringIO (), self.spec) - self.setHeader (sendCodec, ord ('S')) + seq = self.seqMgr.reserve ("outstanding") + self.setHeader (sendCodec, ord ('S'), seq) self.incOutstanding (ch) sendCodec.encode_shortstr (pname) sendCodec.encode_shortstr (cname) -- cgit v1.2.1 From ab24602c21632ffb3f0748331819b2e099b188da Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 22 Apr 2008 12:05:52 +0000 Subject: QPID-944: do no-local checking where requested when there is an exclusive subscription active git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@650450 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 28 ++++++++++++++++++++++++++++ qpid/python/tests_0-10/queue.py | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 2b1b446e7a..8302515b2f 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -92,6 +92,34 @@ class MessageTests(TestBase010): #check queue is empty self.assertEqual(0, session.queue_query(queue="test-queue").message_count) + def test_no_local_exclusive_subscribe(self): + """ + Test that the no_local flag is honoured in the consume method + """ + session = self.session + + #setup, declare two queues one of which excludes delivery of + #locally sent messages but is not declared as exclusive + session.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) + session.queue_declare(queue="test-queue-1b", auto_delete=True, arguments={'no-local':'true'}) + #establish two consumers + self.subscribe(destination="local_included", queue="test-queue-1a") + self.subscribe(destination="local_excluded", queue="test-queue-1b", exclusive=True) + + #send a message + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1a"), "deliver-me")) + session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1b"), "dont-deliver-me")) + + #check the queues of the two consumers + excluded = session.incoming("local_excluded") + included = session.incoming("local_included") + msg = included.get(timeout=1) + self.assertEqual("deliver-me", msg.body) + try: + excluded.get(timeout=1) + self.fail("Received locally published message though no_local=true") + except Empty: None + def test_consume_exclusive(self): """ diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 758794dd52..97e7a92b87 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -223,7 +223,7 @@ class QueueTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="delete-me"), "b")) session.message_transfer(message=Message(session.delivery_properties(routing_key="delete-me"), "c")) session.queue_delete(queue="delete-me") - #check that it has gone be declaring passively + #check that it has gone by declaring passively try: session.queue_declare(queue="delete-me", passive=True) self.fail("Queue has not been deleted") -- cgit v1.2.1 From 6b4af6b24c0e29007c28998d4d7d19383c0ae702 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 22 Apr 2008 16:11:34 +0000 Subject: QPID-947: update cpp and python management to 0-10 final git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@650565 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/client.py | 2 +- qpid/python/qpid/codec.py | 6 +- qpid/python/qpid/codec010.py | 8 +- qpid/python/qpid/connection.py | 566 +++++++-------------------- qpid/python/qpid/connection010.py | 181 --------- qpid/python/qpid/connection08.py | 483 +++++++++++++++++++++++ qpid/python/qpid/delegate.py | 2 +- qpid/python/qpid/delegates.py | 10 +- qpid/python/qpid/framer.py | 26 +- qpid/python/qpid/management.py | 274 ++++++------- qpid/python/qpid/message.py | 2 +- qpid/python/qpid/peer.py | 2 +- qpid/python/qpid/session.py | 31 +- qpid/python/qpid/spec.py | 486 +---------------------- qpid/python/qpid/spec08.py | 512 ++++++++++++++++++++++++ qpid/python/qpid/testlib.py | 6 +- qpid/python/tests/__init__.py | 2 +- qpid/python/tests/connection.py | 136 +++++++ qpid/python/tests/connection010.py | 136 ------- qpid/python/tests_0-10/management.py | 89 +++++ qpid/python/tests_0-10/persistence.py | 67 ++++ qpid/python/tests_0-10_preview/management.py | 89 ----- 22 files changed, 1620 insertions(+), 1496 deletions(-) delete mode 100644 qpid/python/qpid/connection010.py create mode 100644 qpid/python/qpid/connection08.py create mode 100644 qpid/python/qpid/spec08.py create mode 100644 qpid/python/tests/connection.py delete mode 100644 qpid/python/tests/connection010.py create mode 100644 qpid/python/tests_0-10/management.py create mode 100644 qpid/python/tests_0-10/persistence.py delete mode 100644 qpid/python/tests_0-10_preview/management.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py index 13c3f13fe5..4605710de8 100644 --- a/qpid/python/qpid/client.py +++ b/qpid/python/qpid/client.py @@ -25,7 +25,7 @@ interacting with the server. import os, threading from peer import Peer, Channel, Closed from delegate import Delegate -from connection import Connection, Frame, connect +from connection08 import Connection, Frame, connect from spec import load from queue import Queue from reference import ReferenceId, References diff --git a/qpid/python/qpid/codec.py b/qpid/python/qpid/codec.py index dfa74b6a2f..8026b209dc 100644 --- a/qpid/python/qpid/codec.py +++ b/qpid/python/qpid/codec.py @@ -26,7 +26,7 @@ fields. The unit test for this module is located in tests/codec.py """ -import re, qpid, spec +import re, qpid, spec08 from cStringIO import StringIO from struct import * from reference import ReferenceId @@ -156,7 +156,7 @@ class Codec: """ calls the appropriate encode function e.g. encode_octet, encode_short etc. """ - if isinstance(type, spec.Struct): + if isinstance(type, spec08.Struct): self.encode_struct(type, value) else: getattr(self, "encode_" + type)(value) @@ -165,7 +165,7 @@ class Codec: """ calls the appropriate decode function e.g. decode_octet, decode_short etc. """ - if isinstance(type, spec.Struct): + if isinstance(type, spec08.Struct): return self.decode_struct(type) else: return getattr(self, "decode_" + type)() diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index f82a7a49dc..27fcd5d418 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -221,12 +221,18 @@ class Codec(Packer): attr = "write_uint%d" % (width*8) getattr(self, attr)(n) + def read_uuid(self): + return self.unpack("16s") + def write_uuid(self, s): self.pack("16s", s) - def read_uuid(self): + def read_bin128(self): return self.unpack("16s") + def write_bin128(self, b): + self.pack("16s", b) + class StringCodec(Codec): diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index eafad7067a..dc72cd9cb8 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -17,467 +17,165 @@ # under the License. # -""" -A Connection class containing socket code that uses the spec metadata -to read and write Frame objects. This could be used by a client, -server, or even a proxy implementation. -""" +import datatypes, session +from threading import Thread, Condition, RLock +from util import wait +from framer import Closed +from assembler import Assembler, Segment +from codec010 import StringCodec +from session import Session +from invoker import Invoker +from spec010 import Control, Command +from exceptions import * +from logging import getLogger +import delegates -import socket, codec, logging, qpid -from cStringIO import StringIO -from spec import load -from codec import EOF +class ChannelBusy(Exception): pass -class SockIO: +class ChannelsBusy(Exception): pass - def __init__(self, sock): - self.sock = sock +class SessionBusy(Exception): pass - def write(self, buf): -# print "OUT: %r" % buf - self.sock.sendall(buf) +def client(*args): + return delegates.Client(*args) - def read(self, n): - data = "" - while len(data) < n: - try: - s = self.sock.recv(n - len(data)) - except socket.error: - break - if len(s) == 0: - break -# print "IN: %r" % s - data += s - return data - - def flush(self): - pass - - def close(self): - self.sock.shutdown(socket.SHUT_RDWR) +def server(*args): + return delegates.Server(*args) -def connect(host, port): - sock = socket.socket() - sock.connect((host, port)) - sock.setblocking(1) - return SockIO(sock) +class Connection(Assembler): -def listen(host, port, predicate = lambda: True): - sock = socket.socket() - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind((host, port)) - sock.listen(5) - while predicate(): - s, a = sock.accept() - yield SockIO(s) - -class Connection: - - def __init__(self, io, spec): - self.codec = codec.Codec(io, spec) + def __init__(self, sock, spec, delegate=client): + Assembler.__init__(self, sock) self.spec = spec - self.FRAME_END = self.spec.constants.byname["frame_end"].id - self.write = getattr(self, "write_%s_%s" % (self.spec.major, self.spec.minor)) - self.read = getattr(self, "read_%s_%s" % (self.spec.major, self.spec.minor)) - - def flush(self): - self.codec.flush() - - INIT="!4s4B" + self.track = self.spec["track"] - def init(self): - self.codec.pack(Connection.INIT, "AMQP", 1, 1, self.spec.major, - self.spec.minor) + self.lock = RLock() + self.attached = {} + self.sessions = {} - def tini(self): - self.codec.unpack(Connection.INIT) + self.condition = Condition() + self.opened = False - def write_8_0(self, frame): - c = self.codec - c.encode_octet(self.spec.constants.byname[frame.type].id) - c.encode_short(frame.channel) - body = StringIO() - enc = codec.Codec(body, self.spec) - frame.encode(enc) - enc.flush() - c.encode_longstr(body.getvalue()) - c.encode_octet(self.FRAME_END) + self.thread = Thread(target=self.run) + self.thread.setDaemon(True) - def read_8_0(self): - c = self.codec - type = self.spec.constants.byid[c.decode_octet()].name - channel = c.decode_short() - body = c.decode_longstr() - dec = codec.Codec(StringIO(body), self.spec) - frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) - frame.channel = channel - end = c.decode_octet() - if end != self.FRAME_END: - garbage = "" - while end != self.FRAME_END: - garbage += chr(end) - end = c.decode_octet() - raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) - return frame + self.channel_max = 65535 - def write_0_10(self, frame): - c = self.codec - flags = 0 - if frame.bof: flags |= 0x08 - if frame.eof: flags |= 0x04 - if frame.bos: flags |= 0x02 - if frame.eos: flags |= 0x01 + self.delegate = delegate(self) - c.encode_octet(flags) # TODO: currently fixed at ver=0, B=E=b=e=1 - c.encode_octet(self.spec.constants.byname[frame.type].id) - body = StringIO() - enc = codec.Codec(body, self.spec) - frame.encode(enc) - enc.flush() - frame_size = len(body.getvalue()) + 12 # TODO: Magic number (frame header size) - c.encode_short(frame_size) - c.encode_octet(0) # Reserved - c.encode_octet(frame.subchannel & 0x0f) - c.encode_short(frame.channel) - c.encode_long(0) # Reserved - c.write(body.getvalue()) - c.encode_octet(self.FRAME_END) - - def read_0_10(self): - c = self.codec - flags = c.decode_octet() # TODO: currently ignoring flags - framing_version = (flags & 0xc0) >> 6 - if framing_version != 0: - raise "frame error: unknown framing version" - type = self.spec.constants.byid[c.decode_octet()].name - frame_size = c.decode_short() - if frame_size < 12: # TODO: Magic number (frame header size) - raise "frame error: frame size too small" - reserved1 = c.decode_octet() - field = c.decode_octet() - subchannel = field & 0x0f - channel = c.decode_short() - reserved2 = c.decode_long() # TODO: reserved maybe need to ensure 0 - if (flags & 0x30) != 0 or reserved1 != 0 or (field & 0xf0) != 0: - raise "frame error: reserved bits not all zero" - body_size = frame_size - 12 # TODO: Magic number (frame header size) - body = c.read(body_size) - dec = codec.Codec(StringIO(body), self.spec) + def attach(self, name, ch, delegate, force=False): + self.lock.acquire() try: - frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) - except EOF: - raise "truncated frame body: %r" % body - frame.channel = channel - frame.subchannel = subchannel - end = c.decode_octet() - if end != self.FRAME_END: - garbage = "" - while end != self.FRAME_END: - garbage += chr(end) - end = c.decode_octet() - raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) - return frame - - def write_99_0(self, frame): - self.write_0_10(frame) - - def read_99_0(self): - return self.read_0_10() - -class Frame: - - DECODERS = {} - - class __metaclass__(type): - - def __new__(cls, name, bases, dict): - for attr in ("encode", "decode", "type"): - if not dict.has_key(attr): - raise TypeError("%s must define %s" % (name, attr)) - dict["decode"] = staticmethod(dict["decode"]) - if dict.has_key("__init__"): - __init__ = dict["__init__"] - def init(self, *args, **kwargs): - args = list(args) - self.init(args, kwargs) - __init__(self, *args, **kwargs) - dict["__init__"] = init - t = type.__new__(cls, name, bases, dict) - if t.type != None: - Frame.DECODERS[t.type] = t - return t - - type = None - - def init(self, args, kwargs): - self.channel = kwargs.pop("channel", 0) - self.subchannel = kwargs.pop("subchannel", 0) - self.bos = True - self.eos = True - self.bof = True - self.eof = True - - def encode(self, enc): abstract - - def decode(spec, dec, size): abstract - -class Method(Frame): - - type = "frame_method" - - def __init__(self, method, args): - if len(args) != len(method.fields): - argspec = ["%s: %s" % (f.name, f.type) - for f in method.fields] - raise TypeError("%s.%s expecting (%s), got %s" % - (method.klass.name, method.name, ", ".join(argspec), - args)) - self.method = method - self.method_type = method - self.args = args - self.eof = not method.content - - def encode(self, c): - version = (c.spec.major, c.spec.minor) - if version == (0, 10) or version == (99, 0): - c.encode_octet(self.method.klass.id) - c.encode_octet(self.method.id) - else: - c.encode_short(self.method.klass.id) - c.encode_short(self.method.id) - for field, arg in zip(self.method.fields, self.args): - c.encode(field.type, arg) - - def decode(spec, c, size): - version = (c.spec.major, c.spec.minor) - if version == (0, 10) or version == (99, 0): - klass = spec.classes.byid[c.decode_octet()] - meth = klass.methods.byid[c.decode_octet()] - else: - klass = spec.classes.byid[c.decode_short()] - meth = klass.methods.byid[c.decode_short()] - args = tuple([c.decode(f.type) for f in meth.fields]) - return Method(meth, args) - - def __str__(self): - return "[%s] %s %s" % (self.channel, self.method, - ", ".join([str(a) for a in self.args])) - -class Request(Frame): - - type = "frame_request" - - def __init__(self, id, response_mark, method): - self.id = id - self.response_mark = response_mark - self.method = method - self.method_type = method.method_type - self.args = method.args - - def encode(self, enc): - enc.encode_longlong(self.id) - enc.encode_longlong(self.response_mark) - # reserved - enc.encode_long(0) - self.method.encode(enc) - - def decode(spec, dec, size): - id = dec.decode_longlong() - mark = dec.decode_longlong() - # reserved - dec.decode_long() - method = Method.decode(spec, dec, size - 20) - return Request(id, mark, method) - - def __str__(self): - return "[%s] Request(%s) %s" % (self.channel, self.id, self.method) - -class Response(Frame): - - type = "frame_response" - - def __init__(self, id, request_id, batch_offset, method): - self.id = id - self.request_id = request_id - self.batch_offset = batch_offset - self.method = method - self.method_type = method.method_type - self.args = method.args - - def encode(self, enc): - enc.encode_longlong(self.id) - enc.encode_longlong(self.request_id) - enc.encode_long(self.batch_offset) - self.method.encode(enc) - - def decode(spec, dec, size): - id = dec.decode_longlong() - request_id = dec.decode_longlong() - batch_offset = dec.decode_long() - method = Method.decode(spec, dec, size - 20) - return Response(id, request_id, batch_offset, method) - - def __str__(self): - return "[%s] Response(%s,%s,%s) %s" % (self.channel, self.id, self.request_id, self.batch_offset, self.method) - -def uses_struct_encoding(spec): - return (spec.major == 0 and spec.minor == 10) or (spec.major == 99 and spec.minor == 0) - -class Header(Frame): - - type = "frame_header" - - def __init__(self, klass, weight, size, properties): - self.klass = klass - self.weight = weight - self.size = size - self.properties = properties - self.eof = size == 0 - self.bof = False - - def __getitem__(self, name): - return self.properties[name] - - def __setitem__(self, name, value): - self.properties[name] = value - - def __delitem__(self, name): - del self.properties[name] - - def encode(self, c): - if uses_struct_encoding(c.spec): - self.encode_structs(c) - else: - self.encode_legacy(c) - - def encode_structs(self, c): - # XXX - structs = [qpid.Struct(c.spec.domains.byname["delivery_properties"].type), - qpid.Struct(c.spec.domains.byname["message_properties"].type)] - - # XXX - props = self.properties.copy() - for k in self.properties: - for s in structs: - if s.exists(k): - s.set(k, props.pop(k)) - if props: - raise TypeError("no such property: %s" % (", ".join(props))) - - # message properties store the content-length now, and weight is - # deprecated - if self.size != None: - structs[1].content_length = self.size - - for s in structs: - c.encode_long_struct(s) - - def encode_legacy(self, c): - c.encode_short(self.klass.id) - c.encode_short(self.weight) - c.encode_longlong(self.size) - - # property flags - nprops = len(self.klass.fields) - flags = 0 - for i in range(nprops): - f = self.klass.fields.items[i] - flags <<= 1 - if self.properties.get(f.name) != None: - flags |= 1 - # the last bit indicates more flags - if i > 0 and (i % 15) == 0: - flags <<= 1 - if nprops > (i + 1): - flags |= 1 - c.encode_short(flags) - flags = 0 - flags <<= ((16 - (nprops % 15)) % 16) - c.encode_short(flags) - - # properties - for f in self.klass.fields: - v = self.properties.get(f.name) - if v != None: - c.encode(f.type, v) - - def decode(spec, c, size): - if uses_struct_encoding(spec): - return Header.decode_structs(spec, c, size) + ssn = self.attached.get(ch.id) + if ssn is not None: + if ssn.name != name: + raise ChannelBusy(ch, ssn) + else: + ssn = self.sessions.get(name) + if ssn is None: + ssn = Session(name, self.spec, delegate=delegate) + self.sessions[name] = ssn + elif ssn.channel is not None: + if force: + del self.attached[ssn.channel.id] + ssn.channel = None + else: + raise SessionBusy(ssn) + self.attached[ch.id] = ssn + ssn.channel = ch + ch.session = ssn + return ssn + finally: + self.lock.release() + + def detach(self, name, ch): + self.lock.acquire() + try: + self.attached.pop(ch.id, None) + ssn = self.sessions.pop(name, None) + if ssn is not None: + ssn.channel = None + return ssn + finally: + self.lock.release() + + def __channel(self): + # XXX: ch 0? + for i in xrange(self.channel_max): + if not self.attached.has_key(i): + return i else: - return Header.decode_legacy(spec, c, size) + raise ChannelsBusy() - @staticmethod - def decode_structs(spec, c, size): - structs = [] - start = c.nread - while c.nread - start < size: - structs.append(c.decode_long_struct()) - - # XXX - props = {} - length = None - for s in structs: - for f in s.type.fields: - if s.has(f.name): - props[f.name] = s.get(f.name) - if f.name == "content_length": - length = s.get(f.name) - return Header(None, 0, length, props) - - @staticmethod - def decode_legacy(spec, c, size): - klass = spec.classes.byid[c.decode_short()] - weight = c.decode_short() - size = c.decode_longlong() - - # property flags - bits = [] + def session(self, name, timeout=None, delegate=session.client): + self.lock.acquire() + try: + ch = Channel(self, self.__channel()) + ssn = self.attach(name, ch, delegate) + ssn.channel.session_attach(name) + if wait(ssn.condition, lambda: ssn.channel is not None, timeout): + return ssn + else: + self.detach(name, ch) + raise Timeout() + finally: + self.lock.release() + + def start(self, timeout=None): + self.delegate.start() + self.thread.start() + if not wait(self.condition, lambda: self.opened, timeout): + raise Timeout() + + def run(self): + # XXX: we don't really have a good way to exit this loop without + # getting the other end to kill the socket while True: - flags = c.decode_short() - for i in range(15, 0, -1): - if flags >> i & 0x1 != 0: - bits.append(True) - else: - bits.append(False) - if flags & 0x1 == 0: + try: + seg = self.read_segment() + except Closed: break + self.delegate.received(seg) - # properties - properties = {} - for b, f in zip(bits, klass.fields): - if b: - # Note: decode returns a unicode u'' string but only - # plain '' strings can be used as keywords so we need to - # stringify the names. - properties[str(f.name)] = c.decode(f.type) - return Header(klass, weight, size, properties) + def close(self, timeout=None): + if not self.opened: return + Channel(self, 0).connection_close(200) + if not wait(self.condition, lambda: not self.opened, timeout): + raise Timeout() + self.thread.join(timeout=timeout) def __str__(self): - return "%s %s %s %s" % (self.klass, self.weight, self.size, - self.properties) + return "%s:%s" % self.sock.getsockname() + + def __repr__(self): + return str(self) -class Body(Frame): +log = getLogger("qpid.io.ctl") - type = "frame_body" +class Channel(Invoker): - def __init__(self, content): - self.content = content - self.eof = True - self.bof = False + def __init__(self, connection, id): + self.connection = connection + self.id = id + self.session = None - def encode(self, enc): - enc.write(self.content) + def resolve_method(self, name): + inst = self.connection.spec.instructions.get(name) + if inst is not None and isinstance(inst, Control): + return inst + else: + return None - def decode(spec, dec, size): - return Body(dec.read(size)) + def invoke(self, type, args, kwargs): + ctl = type.new(args, kwargs) + sc = StringCodec(self.connection.spec) + sc.write_control(ctl) + self.connection.write_segment(Segment(True, True, type.segment_type, + type.track, self.id, sc.encoded)) + log.debug("SENT %s", ctl) def __str__(self): - return "Body(%r)" % self.content + return "%s[%s]" % (self.connection, self.id) -# TODO: -# OOB_METHOD = "frame_oob_method" -# OOB_HEADER = "frame_oob_header" -# OOB_BODY = "frame_oob_body" -# TRACE = "frame_trace" -# HEARTBEAT = "frame_heartbeat" + def __repr__(self): + return str(self) diff --git a/qpid/python/qpid/connection010.py b/qpid/python/qpid/connection010.py deleted file mode 100644 index 59aa41c88d..0000000000 --- a/qpid/python/qpid/connection010.py +++ /dev/null @@ -1,181 +0,0 @@ -# -# 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 datatypes, session -from threading import Thread, Condition, RLock -from util import wait -from framer import Closed -from assembler import Assembler, Segment -from codec010 import StringCodec -from session import Session -from invoker import Invoker -from spec010 import Control, Command -from exceptions import * -from logging import getLogger -import delegates - -class ChannelBusy(Exception): pass - -class ChannelsBusy(Exception): pass - -class SessionBusy(Exception): pass - -def client(*args): - return delegates.Client(*args) - -def server(*args): - return delegates.Server(*args) - -class Connection(Assembler): - - def __init__(self, sock, spec, delegate=client): - Assembler.__init__(self, sock) - self.spec = spec - self.track = self.spec["track"] - - self.lock = RLock() - self.attached = {} - self.sessions = {} - - self.condition = Condition() - self.opened = False - - self.thread = Thread(target=self.run) - self.thread.setDaemon(True) - - self.channel_max = 65535 - - self.delegate = delegate(self) - - def attach(self, name, ch, delegate, force=False): - self.lock.acquire() - try: - ssn = self.attached.get(ch.id) - if ssn is not None: - if ssn.name != name: - raise ChannelBusy(ch, ssn) - else: - ssn = self.sessions.get(name) - if ssn is None: - ssn = Session(name, self.spec, delegate=delegate) - self.sessions[name] = ssn - elif ssn.channel is not None: - if force: - del self.attached[ssn.channel.id] - ssn.channel = None - else: - raise SessionBusy(ssn) - self.attached[ch.id] = ssn - ssn.channel = ch - ch.session = ssn - return ssn - finally: - self.lock.release() - - def detach(self, name, ch): - self.lock.acquire() - try: - self.attached.pop(ch.id, None) - ssn = self.sessions.pop(name, None) - if ssn is not None: - ssn.channel = None - return ssn - finally: - self.lock.release() - - def __channel(self): - # XXX: ch 0? - for i in xrange(self.channel_max): - if not self.attached.has_key(i): - return i - else: - raise ChannelsBusy() - - def session(self, name, timeout=None, delegate=session.client): - self.lock.acquire() - try: - ch = Channel(self, self.__channel()) - ssn = self.attach(name, ch, delegate) - ssn.channel.session_attach(name) - if wait(ssn.condition, lambda: ssn.channel is not None, timeout): - return ssn - else: - self.detach(name, ch) - raise Timeout() - finally: - self.lock.release() - - def start(self, timeout=None): - self.delegate.start() - self.thread.start() - if not wait(self.condition, lambda: self.opened, timeout): - raise Timeout() - - def run(self): - # XXX: we don't really have a good way to exit this loop without - # getting the other end to kill the socket - while True: - try: - seg = self.read_segment() - except Closed: - break - self.delegate.received(seg) - - def close(self, timeout=None): - if not self.opened: return - Channel(self, 0).connection_close() - if not wait(self.condition, lambda: not self.opened, timeout): - raise Timeout() - self.thread.join(timeout=timeout) - - def __str__(self): - return "%s:%s" % self.sock.getsockname() - - def __repr__(self): - return str(self) - -log = getLogger("qpid.io.ctl") - -class Channel(Invoker): - - def __init__(self, connection, id): - self.connection = connection - self.id = id - self.session = None - - def resolve_method(self, name): - inst = self.connection.spec.instructions.get(name) - if inst is not None and isinstance(inst, Control): - return inst - else: - return None - - def invoke(self, type, args, kwargs): - ctl = type.new(args, kwargs) - sc = StringCodec(self.connection.spec) - sc.write_control(ctl) - self.connection.write_segment(Segment(True, True, type.segment_type, - type.track, self.id, sc.encoded)) - log.debug("SENT %s", ctl) - - def __str__(self): - return "%s[%s]" % (self.connection, self.id) - - def __repr__(self): - return str(self) diff --git a/qpid/python/qpid/connection08.py b/qpid/python/qpid/connection08.py new file mode 100644 index 0000000000..eafad7067a --- /dev/null +++ b/qpid/python/qpid/connection08.py @@ -0,0 +1,483 @@ +# +# 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. +# + +""" +A Connection class containing socket code that uses the spec metadata +to read and write Frame objects. This could be used by a client, +server, or even a proxy implementation. +""" + +import socket, codec, logging, qpid +from cStringIO import StringIO +from spec import load +from codec import EOF + +class SockIO: + + def __init__(self, sock): + self.sock = sock + + def write(self, buf): +# print "OUT: %r" % buf + self.sock.sendall(buf) + + def read(self, n): + data = "" + while len(data) < n: + try: + s = self.sock.recv(n - len(data)) + except socket.error: + break + if len(s) == 0: + break +# print "IN: %r" % s + data += s + return data + + def flush(self): + pass + + def close(self): + self.sock.shutdown(socket.SHUT_RDWR) + +def connect(host, port): + sock = socket.socket() + sock.connect((host, port)) + sock.setblocking(1) + return SockIO(sock) + +def listen(host, port, predicate = lambda: True): + sock = socket.socket() + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((host, port)) + sock.listen(5) + while predicate(): + s, a = sock.accept() + yield SockIO(s) + +class Connection: + + def __init__(self, io, spec): + self.codec = codec.Codec(io, spec) + self.spec = spec + self.FRAME_END = self.spec.constants.byname["frame_end"].id + self.write = getattr(self, "write_%s_%s" % (self.spec.major, self.spec.minor)) + self.read = getattr(self, "read_%s_%s" % (self.spec.major, self.spec.minor)) + + def flush(self): + self.codec.flush() + + INIT="!4s4B" + + def init(self): + self.codec.pack(Connection.INIT, "AMQP", 1, 1, self.spec.major, + self.spec.minor) + + def tini(self): + self.codec.unpack(Connection.INIT) + + def write_8_0(self, frame): + c = self.codec + c.encode_octet(self.spec.constants.byname[frame.type].id) + c.encode_short(frame.channel) + body = StringIO() + enc = codec.Codec(body, self.spec) + frame.encode(enc) + enc.flush() + c.encode_longstr(body.getvalue()) + c.encode_octet(self.FRAME_END) + + def read_8_0(self): + c = self.codec + type = self.spec.constants.byid[c.decode_octet()].name + channel = c.decode_short() + body = c.decode_longstr() + dec = codec.Codec(StringIO(body), self.spec) + frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) + frame.channel = channel + end = c.decode_octet() + if end != self.FRAME_END: + garbage = "" + while end != self.FRAME_END: + garbage += chr(end) + end = c.decode_octet() + raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) + return frame + + def write_0_10(self, frame): + c = self.codec + flags = 0 + if frame.bof: flags |= 0x08 + if frame.eof: flags |= 0x04 + if frame.bos: flags |= 0x02 + if frame.eos: flags |= 0x01 + + c.encode_octet(flags) # TODO: currently fixed at ver=0, B=E=b=e=1 + c.encode_octet(self.spec.constants.byname[frame.type].id) + body = StringIO() + enc = codec.Codec(body, self.spec) + frame.encode(enc) + enc.flush() + frame_size = len(body.getvalue()) + 12 # TODO: Magic number (frame header size) + c.encode_short(frame_size) + c.encode_octet(0) # Reserved + c.encode_octet(frame.subchannel & 0x0f) + c.encode_short(frame.channel) + c.encode_long(0) # Reserved + c.write(body.getvalue()) + c.encode_octet(self.FRAME_END) + + def read_0_10(self): + c = self.codec + flags = c.decode_octet() # TODO: currently ignoring flags + framing_version = (flags & 0xc0) >> 6 + if framing_version != 0: + raise "frame error: unknown framing version" + type = self.spec.constants.byid[c.decode_octet()].name + frame_size = c.decode_short() + if frame_size < 12: # TODO: Magic number (frame header size) + raise "frame error: frame size too small" + reserved1 = c.decode_octet() + field = c.decode_octet() + subchannel = field & 0x0f + channel = c.decode_short() + reserved2 = c.decode_long() # TODO: reserved maybe need to ensure 0 + if (flags & 0x30) != 0 or reserved1 != 0 or (field & 0xf0) != 0: + raise "frame error: reserved bits not all zero" + body_size = frame_size - 12 # TODO: Magic number (frame header size) + body = c.read(body_size) + dec = codec.Codec(StringIO(body), self.spec) + try: + frame = Frame.DECODERS[type].decode(self.spec, dec, len(body)) + except EOF: + raise "truncated frame body: %r" % body + frame.channel = channel + frame.subchannel = subchannel + end = c.decode_octet() + if end != self.FRAME_END: + garbage = "" + while end != self.FRAME_END: + garbage += chr(end) + end = c.decode_octet() + raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) + return frame + + def write_99_0(self, frame): + self.write_0_10(frame) + + def read_99_0(self): + return self.read_0_10() + +class Frame: + + DECODERS = {} + + class __metaclass__(type): + + def __new__(cls, name, bases, dict): + for attr in ("encode", "decode", "type"): + if not dict.has_key(attr): + raise TypeError("%s must define %s" % (name, attr)) + dict["decode"] = staticmethod(dict["decode"]) + if dict.has_key("__init__"): + __init__ = dict["__init__"] + def init(self, *args, **kwargs): + args = list(args) + self.init(args, kwargs) + __init__(self, *args, **kwargs) + dict["__init__"] = init + t = type.__new__(cls, name, bases, dict) + if t.type != None: + Frame.DECODERS[t.type] = t + return t + + type = None + + def init(self, args, kwargs): + self.channel = kwargs.pop("channel", 0) + self.subchannel = kwargs.pop("subchannel", 0) + self.bos = True + self.eos = True + self.bof = True + self.eof = True + + def encode(self, enc): abstract + + def decode(spec, dec, size): abstract + +class Method(Frame): + + type = "frame_method" + + def __init__(self, method, args): + if len(args) != len(method.fields): + argspec = ["%s: %s" % (f.name, f.type) + for f in method.fields] + raise TypeError("%s.%s expecting (%s), got %s" % + (method.klass.name, method.name, ", ".join(argspec), + args)) + self.method = method + self.method_type = method + self.args = args + self.eof = not method.content + + def encode(self, c): + version = (c.spec.major, c.spec.minor) + if version == (0, 10) or version == (99, 0): + c.encode_octet(self.method.klass.id) + c.encode_octet(self.method.id) + else: + c.encode_short(self.method.klass.id) + c.encode_short(self.method.id) + for field, arg in zip(self.method.fields, self.args): + c.encode(field.type, arg) + + def decode(spec, c, size): + version = (c.spec.major, c.spec.minor) + if version == (0, 10) or version == (99, 0): + klass = spec.classes.byid[c.decode_octet()] + meth = klass.methods.byid[c.decode_octet()] + else: + klass = spec.classes.byid[c.decode_short()] + meth = klass.methods.byid[c.decode_short()] + args = tuple([c.decode(f.type) for f in meth.fields]) + return Method(meth, args) + + def __str__(self): + return "[%s] %s %s" % (self.channel, self.method, + ", ".join([str(a) for a in self.args])) + +class Request(Frame): + + type = "frame_request" + + def __init__(self, id, response_mark, method): + self.id = id + self.response_mark = response_mark + self.method = method + self.method_type = method.method_type + self.args = method.args + + def encode(self, enc): + enc.encode_longlong(self.id) + enc.encode_longlong(self.response_mark) + # reserved + enc.encode_long(0) + self.method.encode(enc) + + def decode(spec, dec, size): + id = dec.decode_longlong() + mark = dec.decode_longlong() + # reserved + dec.decode_long() + method = Method.decode(spec, dec, size - 20) + return Request(id, mark, method) + + def __str__(self): + return "[%s] Request(%s) %s" % (self.channel, self.id, self.method) + +class Response(Frame): + + type = "frame_response" + + def __init__(self, id, request_id, batch_offset, method): + self.id = id + self.request_id = request_id + self.batch_offset = batch_offset + self.method = method + self.method_type = method.method_type + self.args = method.args + + def encode(self, enc): + enc.encode_longlong(self.id) + enc.encode_longlong(self.request_id) + enc.encode_long(self.batch_offset) + self.method.encode(enc) + + def decode(spec, dec, size): + id = dec.decode_longlong() + request_id = dec.decode_longlong() + batch_offset = dec.decode_long() + method = Method.decode(spec, dec, size - 20) + return Response(id, request_id, batch_offset, method) + + def __str__(self): + return "[%s] Response(%s,%s,%s) %s" % (self.channel, self.id, self.request_id, self.batch_offset, self.method) + +def uses_struct_encoding(spec): + return (spec.major == 0 and spec.minor == 10) or (spec.major == 99 and spec.minor == 0) + +class Header(Frame): + + type = "frame_header" + + def __init__(self, klass, weight, size, properties): + self.klass = klass + self.weight = weight + self.size = size + self.properties = properties + self.eof = size == 0 + self.bof = False + + def __getitem__(self, name): + return self.properties[name] + + def __setitem__(self, name, value): + self.properties[name] = value + + def __delitem__(self, name): + del self.properties[name] + + def encode(self, c): + if uses_struct_encoding(c.spec): + self.encode_structs(c) + else: + self.encode_legacy(c) + + def encode_structs(self, c): + # XXX + structs = [qpid.Struct(c.spec.domains.byname["delivery_properties"].type), + qpid.Struct(c.spec.domains.byname["message_properties"].type)] + + # XXX + props = self.properties.copy() + for k in self.properties: + for s in structs: + if s.exists(k): + s.set(k, props.pop(k)) + if props: + raise TypeError("no such property: %s" % (", ".join(props))) + + # message properties store the content-length now, and weight is + # deprecated + if self.size != None: + structs[1].content_length = self.size + + for s in structs: + c.encode_long_struct(s) + + def encode_legacy(self, c): + c.encode_short(self.klass.id) + c.encode_short(self.weight) + c.encode_longlong(self.size) + + # property flags + nprops = len(self.klass.fields) + flags = 0 + for i in range(nprops): + f = self.klass.fields.items[i] + flags <<= 1 + if self.properties.get(f.name) != None: + flags |= 1 + # the last bit indicates more flags + if i > 0 and (i % 15) == 0: + flags <<= 1 + if nprops > (i + 1): + flags |= 1 + c.encode_short(flags) + flags = 0 + flags <<= ((16 - (nprops % 15)) % 16) + c.encode_short(flags) + + # properties + for f in self.klass.fields: + v = self.properties.get(f.name) + if v != None: + c.encode(f.type, v) + + def decode(spec, c, size): + if uses_struct_encoding(spec): + return Header.decode_structs(spec, c, size) + else: + return Header.decode_legacy(spec, c, size) + + @staticmethod + def decode_structs(spec, c, size): + structs = [] + start = c.nread + while c.nread - start < size: + structs.append(c.decode_long_struct()) + + # XXX + props = {} + length = None + for s in structs: + for f in s.type.fields: + if s.has(f.name): + props[f.name] = s.get(f.name) + if f.name == "content_length": + length = s.get(f.name) + return Header(None, 0, length, props) + + @staticmethod + def decode_legacy(spec, c, size): + klass = spec.classes.byid[c.decode_short()] + weight = c.decode_short() + size = c.decode_longlong() + + # property flags + bits = [] + while True: + flags = c.decode_short() + for i in range(15, 0, -1): + if flags >> i & 0x1 != 0: + bits.append(True) + else: + bits.append(False) + if flags & 0x1 == 0: + break + + # properties + properties = {} + for b, f in zip(bits, klass.fields): + if b: + # Note: decode returns a unicode u'' string but only + # plain '' strings can be used as keywords so we need to + # stringify the names. + properties[str(f.name)] = c.decode(f.type) + return Header(klass, weight, size, properties) + + def __str__(self): + return "%s %s %s %s" % (self.klass, self.weight, self.size, + self.properties) + +class Body(Frame): + + type = "frame_body" + + def __init__(self, content): + self.content = content + self.eof = True + self.bof = False + + def encode(self, enc): + enc.write(self.content) + + def decode(spec, dec, size): + return Body(dec.read(size)) + + def __str__(self): + return "Body(%r)" % self.content + +# TODO: +# OOB_METHOD = "frame_oob_method" +# OOB_HEADER = "frame_oob_header" +# OOB_BODY = "frame_oob_body" +# TRACE = "frame_trace" +# HEARTBEAT = "frame_heartbeat" diff --git a/qpid/python/qpid/delegate.py b/qpid/python/qpid/delegate.py index c4c47592b4..b447c4aa29 100644 --- a/qpid/python/qpid/delegate.py +++ b/qpid/python/qpid/delegate.py @@ -22,7 +22,7 @@ Delegate implementation intended for use with the peer module. """ import threading, inspect, traceback, sys -from connection import Method, Request, Response +from connection08 import Method, Request, Response def _handler_name(method): return "%s_%s" % (method.klass.name, method.name) diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 9df2cd04bd..c958313671 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -17,7 +17,7 @@ # under the License. # -import os, connection010, session +import os, connection, session from util import notify from datatypes import RangedSet from logging import getLogger @@ -35,7 +35,7 @@ class Delegate: def received(self, seg): ssn = self.connection.attached.get(seg.channel) if ssn is None: - ch = connection010.Channel(self.connection, seg.channel) + ch = connection.Channel(self.connection, seg.channel) else: ch = ssn.channel @@ -61,9 +61,9 @@ class Delegate: try: self.connection.attach(a.name, ch, self.delegate, a.force) ch.session_attached(a.name) - except connection010.ChannelBusy: + except connection.ChannelBusy: ch.session_detached(a.name) - except connection010.SessionBusy: + except connection.SessionBusy: ch.session_detached(a.name) def session_attached(self, ch, a): @@ -105,7 +105,7 @@ class Server(Delegate): def start(self): self.connection.read_header() self.connection.write_header(self.spec.major, self.spec.minor) - connection010.Channel(self.connection, 0).connection_start() + connection.Channel(self.connection, 0).connection_start() def connection_start_ok(self, ch, start_ok): ch.connection_tune() diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index 11fe385d46..fb0e677cee 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -19,6 +19,7 @@ import struct, socket from packer import Packer +from threading import Lock from logging import getLogger raw = getLogger("qpid.io.raw") @@ -75,6 +76,7 @@ class Framer(Packer): def __init__(self, sock): self.sock = sock + self.sock_lock = Lock() def aborted(self): return False @@ -116,16 +118,24 @@ class Framer(Packer): return self.unpack(Framer.HEADER) def write_header(self, major, minor): - self.pack(Framer.HEADER, "AMQP", 1, 1, major, minor) + self.sock_lock.acquire() + try: + self.pack(Framer.HEADER, "AMQP", 1, 1, major, minor) + finally: + self.sock_lock.release() def write_frame(self, frame): - size = len(frame.payload) + struct.calcsize(Frame.HEADER) - track = frame.track & 0x0F - self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) - self.write(frame.payload) - # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ - self.write("\xCE") - frm.debug("SENT %s", frame) + self.sock_lock.acquire() + try: + size = len(frame.payload) + struct.calcsize(Frame.HEADER) + track = frame.track & 0x0F + self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) + self.write(frame.payload) + # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ + self.write("\xCE") + frm.debug("SENT %s", frame) + finally: + self.sock_lock.release() def read_frame(self): flags, type, size, track, channel = self.unpack(Frame.HEADER) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 6b25d5ea08..3a7a564e19 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -25,12 +25,10 @@ import qpid import struct import socket from threading import Thread -from message import Message +from datatypes import Message, RangedSet from time import time -from qpid.client import Client -from qpid.content import Content from cStringIO import StringIO -from codec import Codec, EOF +from codec010 import StringCodec as Codec from threading import Lock, Condition @@ -83,40 +81,39 @@ class methodResult: class managementChannel: """ This class represents a connection to an AMQP broker. """ - def __init__ (self, ch, topicCb, replyCb, cbContext, _detlife=0): + def __init__ (self, ssn, topicCb, replyCb, cbContext, _detlife=0): """ Given a channel on an established AMQP broker connection, this method opens a session and performs all of the declarations and bindings needed to participate in the management protocol. """ - response = ch.session_open (detached_lifetime=_detlife) - self.sessionId = response.session_id - self.topicName = "mgmt-%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", response.session_id) - self.replyName = "repl-%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", response.session_id) - self.qpidChannel = ch + self.sessionId = ssn.name + self.topicName = "mgmt-%s" % self.sessionId + self.replyName = "repl-%s" % self.sessionId + self.qpidChannel = ssn self.tcb = topicCb self.rcb = replyCb self.context = cbContext self.reqsOutstanding = 0 - ch.queue_declare (queue=self.topicName, exclusive=True, auto_delete=True) - ch.queue_declare (queue=self.replyName, exclusive=True, auto_delete=True) + ssn.queue_declare (queue=self.topicName, exclusive=True, auto_delete=True) + ssn.queue_declare (queue=self.replyName, exclusive=True, auto_delete=True) - ch.queue_bind (exchange="qpid.management", - queue=self.topicName, routing_key="mgmt.#") - ch.queue_bind (exchange="amq.direct", - queue=self.replyName, routing_key=self.replyName) - ch.message_subscribe (queue=self.topicName, destination="tdest") - ch.message_subscribe (queue=self.replyName, destination="rdest") + ssn.exchange_bind (exchange="qpid.management", + queue=self.topicName, binding_key="mgmt.#") + ssn.exchange_bind (exchange="amq.direct", + queue=self.replyName, binding_key=self.replyName) + ssn.message_subscribe (queue=self.topicName, destination="tdest") + ssn.message_subscribe (queue=self.replyName, destination="rdest") - ch.client.queue ("tdest").listen (self.topicCb) - ch.client.queue ("rdest").listen (self.replyCb) + ssn.incoming ("tdest").listen (self.topicCb) + ssn.incoming ("rdest").listen (self.replyCb) - ch.message_flow_mode (destination="tdest", mode=1) - ch.message_flow (destination="tdest", unit=0, value=0xFFFFFFFF) - ch.message_flow (destination="tdest", unit=1, value=0xFFFFFFFF) + ssn.message_set_flow_mode (destination="tdest", flow_mode=1) + ssn.message_flow (destination="tdest", unit=0, value=0xFFFFFFFF) + ssn.message_flow (destination="tdest", unit=1, value=0xFFFFFFFF) - ch.message_flow_mode (destination="rdest", mode=1) - ch.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) - ch.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) + ssn.message_set_flow_mode (destination="rdest", flow_mode=1) + ssn.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) + ssn.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) def topicCb (self, msg): """ Receive messages via the topic queue on this channel. """ @@ -127,7 +124,18 @@ class managementChannel: self.rcb (self, msg) def send (self, exchange, msg): - self.qpidChannel.message_transfer (destination=exchange, content=msg) + self.qpidChannel.message_transfer (destination=exchange, message=msg) + + def accept (self, msg): + self.qpidChannel.message_accept(RangedSet(msg.id)) + + def message (self, body, routing_key="agent"): + dp = self.qpidChannel.delivery_properties() + dp.routing_key = routing_key + mp = self.qpidChannel.message_properties() + mp.content_type = "application/octet-stream" + mp.reply_to = self.qpidChannel.reply_to("amq.direct", self.replyName) + return Message(dp, mp, body) class managementClient: @@ -177,14 +185,9 @@ class managementClient: self.channels.append (mch) self.incOutstanding (mch) - codec = Codec (StringIO (), self.spec) + codec = Codec (self.spec) self.setHeader (codec, ord ('B')) - msg = Content (codec.stream.getvalue ()) - msg["content_type"] = "application/octet-stream" - msg["routing_key"] = "agent" - msg["reply_to"] = self.spec.struct ("reply_to") - msg["reply_to"]["exchange_name"] = "amq.direct" - msg["reply_to"]["routing_key"] = mch.replyName + msg = mch.message(codec.encoded) mch.send ("qpid.management", msg) return mch @@ -198,17 +201,12 @@ class managementClient: def getObjects (self, channel, userSequence, className): """ Request immediate content from broker """ - codec = Codec (StringIO (), self.spec) + codec = Codec (self.spec) self.setHeader (codec, ord ('G'), userSequence) ft = {} ft["_class"] = className - codec.encode_table (ft) - msg = Content (codec.stream.getvalue ()) - msg["content_type"] = "application/octet-stream" - msg["routing_key"] = "agent" - msg["reply_to"] = self.spec.struct ("reply_to") - msg["reply_to"]["exchange_name"] = "amq.direct" - msg["reply_to"]["routing_key"] = channel.replyName + codec.write_map (ft) + msg = channel.message(codec.encoded) channel.send ("qpid.management", msg) def syncWaitForStable (self, channel): @@ -270,19 +268,19 @@ class managementClient: #======================================================== def topicCb (self, ch, msg): """ Receive messages via the topic queue of a particular channel. """ - codec = Codec (StringIO (msg.content.body), self.spec) + codec = Codec (self.spec, msg.body) hdr = self.checkHeader (codec) if hdr == None: raise ValueError ("outer header invalid"); self.parse (ch, codec, hdr[0], hdr[1]) - msg.complete () + ch.accept(msg) def replyCb (self, ch, msg): """ Receive messages via the reply queue of a particular channel. """ - codec = Codec (StringIO (msg.content.body), self.spec) + codec = Codec (self.spec, msg.body) hdr = self.checkHeader (codec) if hdr == None: - msg.complete () + ch.accept(msg) return if hdr[0] == 'm': @@ -297,102 +295,102 @@ class managementClient: self.handleClassInd (ch, codec) else: self.parse (ch, codec, hdr[0], hdr[1]) - msg.complete () + ch.accept(msg) #======================================================== # Internal Functions #======================================================== def setHeader (self, codec, opcode, seq = 0): """ Compose the header of a management message. """ - codec.encode_octet (ord ('A')) - codec.encode_octet (ord ('M')) - codec.encode_octet (ord ('1')) - codec.encode_octet (opcode) - codec.encode_long (seq) + codec.write_uint8 (ord ('A')) + codec.write_uint8 (ord ('M')) + codec.write_uint8 (ord ('1')) + codec.write_uint8 (opcode) + codec.write_uint32 (seq) def checkHeader (self, codec): """ Check the header of a management message and extract the opcode and class. """ - octet = chr (codec.decode_octet ()) + octet = chr (codec.read_uint8 ()) if octet != 'A': return None - octet = chr (codec.decode_octet ()) + octet = chr (codec.read_uint8 ()) if octet != 'M': return None - octet = chr (codec.decode_octet ()) + octet = chr (codec.read_uint8 ()) if octet != '1': return None - opcode = chr (codec.decode_octet ()) - seq = codec.decode_long () + opcode = chr (codec.read_uint8 ()) + seq = codec.read_uint32 () return (opcode, seq) def encodeValue (self, codec, value, typecode): """ Encode, into the codec, a value based on its typecode. """ if typecode == 1: - codec.encode_octet (int (value)) + codec.write_uint8 (int (value)) elif typecode == 2: - codec.encode_short (int (value)) + codec.write_uint16 (int (value)) elif typecode == 3: - codec.encode_long (long (value)) + codec.write_uint32 (long (value)) elif typecode == 4: - codec.encode_longlong (long (value)) + codec.write_uint64 (long (value)) elif typecode == 5: - codec.encode_octet (int (value)) + codec.write_uint8 (int (value)) elif typecode == 6: - codec.encode_shortstr (value) + codec.write_str8 (value) elif typecode == 7: - codec.encode_longstr (value) + codec.write_vbin32 (value) elif typecode == 8: # ABSTIME - codec.encode_longlong (long (value)) + codec.write_uint64 (long (value)) elif typecode == 9: # DELTATIME - codec.encode_longlong (long (value)) + codec.write_uint64 (long (value)) elif typecode == 10: # REF - codec.encode_longlong (long (value)) + codec.write_uint64 (long (value)) elif typecode == 11: # BOOL - codec.encode_octet (int (value)) + codec.write_uint8 (int (value)) elif typecode == 12: # FLOAT - codec.encode_float (float (value)) + codec.write_float (float (value)) elif typecode == 13: # DOUBLE - codec.encode_double (double (value)) + codec.write_double (double (value)) elif typecode == 14: # UUID - codec.encode_uuid (value) + codec.write_uuid (value) elif typecode == 15: # FTABLE - codec.encode_table (value) + codec.write_map (value) else: raise ValueError ("Invalid type code: %d" % typecode) def decodeValue (self, codec, typecode): """ Decode, from the codec, a value based on its typecode. """ if typecode == 1: - data = codec.decode_octet () + data = codec.read_uint8 () elif typecode == 2: - data = codec.decode_short () + data = codec.read_uint16 () elif typecode == 3: - data = codec.decode_long () + data = codec.read_uint32 () elif typecode == 4: - data = codec.decode_longlong () + data = codec.read_uint64 () elif typecode == 5: - data = codec.decode_octet () + data = codec.read_uint8 () elif typecode == 6: - data = codec.decode_shortstr () + data = codec.read_str8 () elif typecode == 7: - data = codec.decode_longstr () + data = codec.read_vbin32 () elif typecode == 8: # ABSTIME - data = codec.decode_longlong () + data = codec.read_uint64 () elif typecode == 9: # DELTATIME - data = codec.decode_longlong () + data = codec.read_uint64 () elif typecode == 10: # REF - data = codec.decode_longlong () + data = codec.read_uint64 () elif typecode == 11: # BOOL - data = codec.decode_octet () + data = codec.read_uint8 () elif typecode == 12: # FLOAT - data = codec.decode_float () + data = codec.read_float () elif typecode == 13: # DOUBLE - data = codec.decode_double () + data = codec.read_double () elif typecode == 14: # UUID - data = codec.decode_uuid () + data = codec.read_uuid () elif typecode == 15: # FTABLE - data = codec.decode_table () + data = codec.read_map () else: raise ValueError ("Invalid type code: %d" % typecode) return data @@ -415,8 +413,8 @@ class managementClient: self.ctrlCb (ch.context, self.CTRL_SCHEMA_LOADED, None) def handleMethodReply (self, ch, codec, sequence): - status = codec.decode_long () - sText = codec.decode_shortstr () + status = codec.read_uint32 () + sText = codec.read_str8 () data = self.seqMgr.release (sequence) if data == None: @@ -451,8 +449,8 @@ class managementClient: self.methodCb (ch.context, userSequence, status, sText, args) def handleCommandComplete (self, ch, codec, seq): - code = codec.decode_long () - text = codec.decode_shortstr () + code = codec.read_uint32 () + text = codec.read_str8 () data = (seq, code, text) context = self.seqMgr.release (seq) if context == "outstanding": @@ -467,75 +465,60 @@ class managementClient: def handleBrokerResponse (self, ch, codec): if self.ctrlCb != None: - uuid = codec.decode_uuid () + uuid = codec.read_uuid () data = (uuid, ch.sessionId) self.ctrlCb (ch.context, self.CTRL_BROKER_INFO, data) # Send a package request - sendCodec = Codec (StringIO (), self.spec) + sendCodec = Codec (self.spec) seq = self.seqMgr.reserve ("outstanding") self.setHeader (sendCodec, ord ('P'), seq) - smsg = Content (sendCodec.stream.getvalue ()) - smsg["content_type"] = "application/octet-stream" - smsg["routing_key"] = "agent" - smsg["reply_to"] = self.spec.struct ("reply_to") - smsg["reply_to"]["exchange_name"] = "amq.direct" - smsg["reply_to"]["routing_key"] = ch.replyName + smsg = ch.message(sendCodec.encoded) ch.send ("qpid.management", smsg) - + def handlePackageInd (self, ch, codec): - pname = codec.decode_shortstr () + pname = codec.read_str8 () if pname not in self.packages: self.packages[pname] = {} # Send a class request - sendCodec = Codec (StringIO (), self.spec) + sendCodec = Codec (self.spec) seq = self.seqMgr.reserve ("outstanding") self.setHeader (sendCodec, ord ('Q'), seq) self.incOutstanding (ch) - sendCodec.encode_shortstr (pname) - smsg = Content (sendCodec.stream.getvalue ()) - smsg["content_type"] = "application/octet-stream" - smsg["routing_key"] = "agent" - smsg["reply_to"] = self.spec.struct ("reply_to") - smsg["reply_to"]["exchange_name"] = "amq.direct" - smsg["reply_to"]["routing_key"] = ch.replyName + sendCodec.write_str8 (pname) + smsg = ch.message(sendCodec.encoded) ch.send ("qpid.management", smsg) def handleClassInd (self, ch, codec): - pname = codec.decode_shortstr () - cname = codec.decode_shortstr () - hash = codec.decode_bin128 () + pname = codec.read_str8 () + cname = codec.read_str8 () + hash = codec.read_bin128 () if pname not in self.packages: return if (cname, hash) not in self.packages[pname]: # Send a schema request - sendCodec = Codec (StringIO (), self.spec) + sendCodec = Codec (self.spec) seq = self.seqMgr.reserve ("outstanding") self.setHeader (sendCodec, ord ('S'), seq) self.incOutstanding (ch) - sendCodec.encode_shortstr (pname) - sendCodec.encode_shortstr (cname) - sendCodec.encode_bin128 (hash) - smsg = Content (sendCodec.stream.getvalue ()) - smsg["content_type"] = "application/octet-stream" - smsg["routing_key"] = "agent" - smsg["reply_to"] = self.spec.struct ("reply_to") - smsg["reply_to"]["exchange_name"] = "amq.direct" - smsg["reply_to"]["routing_key"] = ch.replyName + sendCodec.write_str8 (pname) + sendCodec.write_str8 (cname) + sendCodec.write_bin128 (hash) + smsg = ch.message(sendCodec.encoded) ch.send ("qpid.management", smsg) def parseSchema (self, ch, codec): """ Parse a received schema-description message. """ self.decOutstanding (ch) - packageName = codec.decode_shortstr () - className = codec.decode_shortstr () - hash = codec.decode_bin128 () - configCount = codec.decode_short () - instCount = codec.decode_short () - methodCount = codec.decode_short () - eventCount = codec.decode_short () + packageName = codec.read_str8 () + className = codec.read_str8 () + hash = codec.read_bin128 () + configCount = codec.read_uint16 () + instCount = codec.read_uint16 () + methodCount = codec.read_uint16 () + eventCount = codec.read_uint16 () if packageName not in self.packages: return @@ -555,7 +538,7 @@ class managementClient: insts.append (("id", 4, None, None)) for idx in range (configCount): - ft = codec.decode_table () + ft = codec.read_map () name = ft["name"] type = ft["type"] access = ft["access"] @@ -582,7 +565,7 @@ class managementClient: configs.append (config) for idx in range (instCount): - ft = codec.decode_table () + ft = codec.read_map () name = ft["name"] type = ft["type"] unit = None @@ -598,7 +581,7 @@ class managementClient: insts.append (inst) for idx in range (methodCount): - ft = codec.decode_table () + ft = codec.read_map () mname = ft["name"] argCount = ft["argCount"] if "desc" in ft: @@ -608,7 +591,7 @@ class managementClient: args = [] for aidx in range (argCount): - ft = codec.decode_table () + ft = codec.read_map () name = ft["name"] type = ft["type"] dir = ft["dir"].upper () @@ -654,9 +637,9 @@ class managementClient: if cls == 'I' and self.instCb == None: return - packageName = codec.decode_shortstr () - className = codec.decode_shortstr () - hash = codec.decode_bin128 () + packageName = codec.read_str8 () + className = codec.read_str8 () + hash = codec.read_bin128 () classKey = (packageName, className, hash) if classKey not in self.schema: @@ -665,9 +648,9 @@ class managementClient: row = [] timestamps = [] - timestamps.append (codec.decode_longlong ()) # Current Time - timestamps.append (codec.decode_longlong ()) # Create Time - timestamps.append (codec.decode_longlong ()) # Delete Time + timestamps.append (codec.read_uint64 ()) # Current Time + timestamps.append (codec.read_uint64 ()) # Create Time + timestamps.append (codec.read_uint64 ()) # Delete Time schemaClass = self.schema[classKey] if cls == 'C' or cls == 'B': @@ -712,10 +695,10 @@ class managementClient: def method (self, channel, userSequence, objId, classId, methodName, args): """ Invoke a method on an object """ - codec = Codec (StringIO (), self.spec) + codec = Codec (self.spec) sequence = self.seqMgr.reserve ((userSequence, classId, methodName)) self.setHeader (codec, ord ('M'), sequence) - codec.encode_longlong (objId) # ID of object + codec.write_uint64 (objId) # ID of object # Encode args according to schema if classId not in self.schema: @@ -745,11 +728,6 @@ class managementClient: packageName = classId[0] className = classId[1] - msg = Content (codec.stream.getvalue ()) - msg["content_type"] = "application/octet-stream" - msg["routing_key"] = "agent.method." + packageName + "." + \ - className + "." + methodName - msg["reply_to"] = self.spec.struct ("reply_to") - msg["reply_to"]["exchange_name"] = "amq.direct" - msg["reply_to"]["routing_key"] = channel.replyName + msg = channel.message(codec.encoded, "agent.method." + packageName + "." + \ + className + "." + methodName) channel.send ("qpid.management", msg) diff --git a/qpid/python/qpid/message.py b/qpid/python/qpid/message.py index c9ea5a8a0c..eb3ef5c03c 100644 --- a/qpid/python/qpid/message.py +++ b/qpid/python/qpid/message.py @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -from connection import Method, Request +from connection08 import Method, Request from sets import Set class Message: diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index a464e95593..0932efeab3 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -25,7 +25,7 @@ incoming method frames to a delegate. """ import thread, threading, traceback, socket, sys, logging -from connection import EOF, Method, Header, Body, Request, Response +from connection08 import EOF, Method, Header, Body, Request, Response from message import Message from queue import Queue, Closed as QueueClosed from content import Content diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 427a403b90..f649b95a2c 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -17,7 +17,7 @@ # under the License. # -from threading import Condition, RLock, currentThread +from threading import Condition, RLock, Lock, currentThread from invoker import Invoker from datatypes import RangedSet, Struct, Future from codec010 import StringCodec @@ -29,8 +29,11 @@ from exceptions import * from logging import getLogger log = getLogger("qpid.io.cmd") +msg = getLogger("qpid.io.msg") -class SessionDetached(Exception): pass +class SessionException(Exception): pass +class SessionClosed(SessionException): pass +class SessionDetached(SessionException): pass def client(*args): return Client(*args) @@ -38,8 +41,6 @@ def client(*args): def server(*args): return Server(*args) -class SessionException(Exception): pass - INCOMPLETE = object() class Session(Invoker): @@ -50,6 +51,8 @@ class Session(Invoker): self.auto_sync = auto_sync self.timeout = timeout self.channel = None + self.invoke_lock = Lock() + self.closed = False self.condition = Condition() @@ -97,7 +100,12 @@ class Session(Invoker): raise SessionException(self.error()) def close(self, timeout=None): - self.channel.session_detach(self.name) + self.invoke_lock.acquire() + try: + self.closed = True + self.channel.session_detach(self.name) + finally: + self.invoke_lock.release() if not wait(self.condition, lambda: self.channel is None, timeout): raise Timeout() @@ -119,6 +127,16 @@ class Session(Invoker): if not hasattr(type, "track"): return type.new(args, kwargs) + self.invoke_lock.acquire() + try: + return self.do_invoke(type, args, kwargs) + finally: + self.invoke_lock.release() + + def do_invoke(self, type, args, kwargs): + if self.closed: + raise SessionClosed() + if self.channel == None: raise SessionDetached() @@ -160,6 +178,7 @@ class Session(Invoker): seg = Segment(False, True, self.spec["segment_type.body"].value, type.track, self.channel.id, message.body) self.send(seg) + msg.debug("SENT %s", message) if type.result: if self.auto_sync: @@ -304,8 +323,6 @@ class Delegate: finally: self.session.lock.release() -msg = getLogger("qpid.io.msg") - class Client(Delegate): def message_transfer(self, cmd, headers, body): diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 623b2e9e9f..64a14b0f61 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -29,484 +29,18 @@ class so that the generated code can be reused in a variety of situations. """ -import re, textwrap, new, mllib, qpid - -class SpecContainer: - - def __init__(self): - self.items = [] - self.byname = {} - self.byid = {} - self.indexes = {} - - def add(self, item): - if self.byname.has_key(item.name): - raise ValueError("duplicate name: %s" % item) - if item.id == None: - item.id = len(self) - elif self.byid.has_key(item.id): - raise ValueError("duplicate id: %s" % item) - self.indexes[item] = len(self.items) - self.items.append(item) - self.byname[item.name] = item - self.byid[item.id] = item - - def index(self, item): - try: - return self.indexes[item] - except KeyError: - raise ValueError(item) - - def __iter__(self): - return iter(self.items) - - def __len__(self): - return len(self.items) - -class Metadata: - - PRINT = [] - - def __init__(self): - pass - - def __str__(self): - args = map(lambda f: "%s=%s" % (f, getattr(self, f)), self.PRINT) - return "%s(%s)" % (self.__class__.__name__, ", ".join(args)) - - def __repr__(self): - return str(self) - -class Spec(Metadata): - - PRINT=["major", "minor", "file"] - - def __init__(self, major, minor, file): - Metadata.__init__(self) - self.major = major - self.minor = minor - self.file = file - self.constants = SpecContainer() - self.domains = SpecContainer() - self.classes = SpecContainer() - # methods indexed by classname_methname - self.methods = {} - # structs by type code - self.structs = {} - - def post_load(self): - self.module = self.define_module("amqp%s%s" % (self.major, self.minor)) - self.klass = self.define_class("Amqp%s%s" % (self.major, self.minor)) - - def method(self, name): - if not self.methods.has_key(name): - for cls in self.classes: - clen = len(cls.name) - if name.startswith(cls.name) and name[clen] == "_": - end = name[clen + 1:] - if cls.methods.byname.has_key(end): - self.methods[name] = cls.methods.byname[end] - return self.methods.get(name) - - def parse_method(self, name): - parts = re.split(r"\s*\.\s*", name) - if len(parts) != 2: - raise ValueError(name) - klass, meth = parts - return self.classes.byname[klass].methods.byname[meth] - - def struct(self, name, *args, **kwargs): - type = self.domains.byname[name].type - return qpid.Struct(type, *args, **kwargs) - - def define_module(self, name, doc = None): - module = new.module(name, doc) - module.__file__ = self.file - for c in self.classes: - cls = c.define_class(c.name) - cls.__module__ = module.__name__ - setattr(module, c.name, cls) - return module - - def define_class(self, name): - methods = {} - for c in self.classes: - for m in c.methods: - meth = m.klass.name + "_" + m.name - methods[meth] = m.define_method(meth) - return type(name, (), methods) - -class Constant(Metadata): - - PRINT=["name", "id"] - - def __init__(self, spec, name, id, klass, docs): - Metadata.__init__(self) - self.spec = spec - self.name = name - self.id = id - self.klass = klass - self.docs = docs - -class Domain(Metadata): - - PRINT=["name", "type"] - - def __init__(self, spec, name, type, description, docs): - Metadata.__init__(self) - self.spec = spec - self.id = None - self.name = name - self.type = type - self.description = description - self.docs = docs - -class Struct(Metadata): - - PRINT=["size", "type", "pack"] - - def __init__(self, size, type, pack): - Metadata.__init__(self) - self.size = size - self.type = type - self.pack = pack - self.fields = SpecContainer() - -class Class(Metadata): - - PRINT=["name", "id"] - - def __init__(self, spec, name, id, handler, docs): - Metadata.__init__(self) - self.spec = spec - self.name = name - self.id = id - self.handler = handler - self.fields = SpecContainer() - self.methods = SpecContainer() - self.docs = docs - - def define_class(self, name): - methods = {} - for m in self.methods: - methods[m.name] = m.define_method(m.name) - return type(name, (), methods) - -class Method(Metadata): - - PRINT=["name", "id"] - - def __init__(self, klass, name, id, content, responses, result, synchronous, - description, docs): - Metadata.__init__(self) - self.klass = klass - self.name = name - self.id = id - self.content = content - self.responses = responses - self.result = result - self.synchronous = synchronous - self.fields = SpecContainer() - self.description = description - self.docs = docs - self.response = False - - def is_l4_command(self): - return self.klass.name not in ["execution", "channel", "connection", "session"] - - def arguments(self, *args, **kwargs): - nargs = len(args) + len(kwargs) - maxargs = len(self.fields) - if nargs > maxargs: - self._type_error("takes at most %s arguments (%s) given", maxargs, nargs) - result = [] - for f in self.fields: - idx = self.fields.index(f) - if idx < len(args): - result.append(args[idx]) - elif kwargs.has_key(f.name): - result.append(kwargs.pop(f.name)) - else: - result.append(Method.DEFAULTS[f.type]) - for key, value in kwargs.items(): - if self.fields.byname.has_key(key): - self._type_error("got multiple values for keyword argument '%s'", key) - else: - self._type_error("got an unexpected keyword argument '%s'", key) - return tuple(result) - - def _type_error(self, msg, *args): - raise TypeError("%s %s" % (self.name, msg % args)) - - def docstring(self): - s = "\n\n".join([fill(d, 2) for d in [self.description] + self.docs]) - for f in self.fields: - if f.docs: - s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, f.name)] + - [fill(d, 4) for d in f.docs[1:]]) - if self.responses: - s += "\n\nValid responses: " - for r in self.responses: - s += r.name + " " - return s - - METHOD = "__method__" - DEFAULTS = {"bit": False, - "shortstr": "", - "longstr": "", - "table": {}, - "array": [], - "octet": 0, - "short": 0, - "long": 0, - "longlong": 0, - "timestamp": 0, - "content": None, - "uuid": "", - "rfc1982_long": 0, - "rfc1982_long_set": [], - "long_struct": None} - - def define_method(self, name): - g = {Method.METHOD: self} - l = {} - args = [(f.name, Method.DEFAULTS[f.type]) for f in self.fields] - methargs = args[:] - if self.content: - args += [("content", None)] - code = "def %s(self, %s):\n" % \ - (name, ", ".join(["%s = %r" % a for a in args])) - code += " %r\n" % self.docstring() - argnames = ", ".join([a[0] for a in methargs]) - code += " return self.invoke(%s" % Method.METHOD - if argnames: - code += ", (%s,)" % argnames - else: - code += ", ()" - if self.content: - code += ", content" - code += ")" - exec code in g, l - return l[name] - -class Field(Metadata): - - PRINT=["name", "id", "type"] - - def __init__(self, name, id, type, domain, description, docs): - Metadata.__init__(self) - self.name = name - self.id = id - self.type = type - self.domain = domain - self.description = description - self.docs = docs - - def default(self): - if isinstance(self.type, Struct): - return None - else: - return Method.DEFAULTS[self.type] - -WIDTHS = { - "octet": 1, - "short": 2, - "long": 4 - } - -def width(st, default=None): - if st in (None, "none", ""): - return default - else: - return WIDTHS[st] - -def get_result(nd, spec): - result = nd["result"] - if not result: return None - name = result["@domain"] - if name != None: return spec.domains.byname[name] - st_nd = result["struct"] - st = Struct(width(st_nd["@size"]), int(result.parent.parent["@index"])*256 + - int(st_nd["@type"]), width(st_nd["@pack"], 2)) - spec.structs[st.type] = st - load_fields(st_nd, st.fields, spec.domains.byname) - return st - -def get_desc(nd): - label = nd["@label"] - if not label: - label = nd.text() - if label: - label = label.strip() - return label - -def get_docs(nd): - return [n.text() for n in nd.query["doc"]] - -def load_fields(nd, l, domains): - for f_nd in nd.query["field"]: - type = f_nd["@domain"] - if type == None: - type = f_nd["@type"] - type = pythonize(type) - domain = None - while domains.has_key(type) and domains[type].type != type: - domain = domains[type] - type = domain.type - l.add(Field(pythonize(f_nd["@name"]), f_nd.index(), type, domain, - get_desc(f_nd), get_docs(f_nd))) +import os, mllib, spec08, spec010 def load(specfile, *errata): - doc = mllib.xml_parse(specfile) - spec_root = doc["amqp"] - spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile) - - for root in [spec_root] + map(lambda x: mllib.xml_parse(x)["amqp"], errata): - # constants - for nd in root.query["constant"]: - val = nd["@value"] - if val.startswith("0x"): val = int(val, 16) - else: val = int(val) - const = Constant(spec, pythonize(nd["@name"]), val, nd["@class"], - get_docs(nd)) - try: - spec.constants.add(const) - except ValueError, e: - pass - #print "Warning:", e - - # domains are typedefs - structs = [] - for nd in root.query["domain"]: - type = nd["@type"] - if type == None: - st_nd = nd["struct"] - code = st_nd["@type"] - if code not in (None, "", "none"): - code = int(code) - type = Struct(width(st_nd["@size"]), code, width(st_nd["@pack"], 2)) - if type.type != None: - spec.structs[type.type] = type - structs.append((type, st_nd)) - else: - type = pythonize(type) - domain = Domain(spec, pythonize(nd["@name"]), type, get_desc(nd), - get_docs(nd)) - spec.domains.add(domain) - - # structs - for st, st_nd in structs: - load_fields(st_nd, st.fields, spec.domains.byname) - - # classes - for c_nd in root.query["class"]: - cname = pythonize(c_nd["@name"]) - if spec.classes.byname.has_key(cname): - klass = spec.classes.byname[cname] - else: - klass = Class(spec, cname, int(c_nd["@index"]), c_nd["@handler"], - get_docs(c_nd)) - spec.classes.add(klass) - - added_methods = [] - load_fields(c_nd, klass.fields, spec.domains.byname) - for m_nd in c_nd.query["method"]: - mname = pythonize(m_nd["@name"]) - if klass.methods.byname.has_key(mname): - meth = klass.methods.byname[mname] - else: - meth = Method(klass, mname, - int(m_nd["@index"]), - m_nd["@content"] == "1", - [pythonize(nd["@name"]) for nd in m_nd.query["response"]], - get_result(m_nd, spec), - m_nd["@synchronous"] == "1", - get_desc(m_nd), - get_docs(m_nd)) - klass.methods.add(meth) - added_methods.append(meth) - load_fields(m_nd, meth.fields, spec.domains.byname) - # resolve the responses - for m in added_methods: - m.responses = [klass.methods.byname[r] for r in m.responses] - for resp in m.responses: - resp.response = True - - spec.post_load() - return spec - -REPLACE = {" ": "_", "-": "_"} -KEYWORDS = {"global": "global_", - "return": "return_"} + for name in (specfile,) + errata: + if not os.path.exists(name): + raise IOError("No such file or directory: '%s'" % name) -def pythonize(name): - name = str(name) - for key, val in REPLACE.items(): - name = name.replace(key, val) - try: - name = KEYWORDS[name] - except KeyError: - pass - return name + doc = mllib.xml_parse(specfile) + major = doc["amqp/@major"] + minor = doc["amqp/@minor"] -def fill(text, indent, heading = None): - sub = indent * " " - if heading: - init = (indent - 2) * " " + heading + " -- " + if major == "0" and minor == "10": + return spec010.load(specfile, *errata) else: - init = sub - w = textwrap.TextWrapper(initial_indent = init, subsequent_indent = sub) - return w.fill(" ".join(text.split())) - -class Rule(Metadata): - - PRINT = ["text", "implement", "tests"] - - def __init__(self, text, implement, tests, path): - self.text = text - self.implement = implement - self.tests = tests - self.path = path - -def find_rules(node, rules): - if node.name == "rule": - rules.append(Rule(node.text, node.get("@implement"), - [ch.text for ch in node if ch.name == "test"], - node.path())) - if node.name == "doc" and node.get("@name") == "rule": - tests = [] - if node.has("@test"): - tests.append(node["@test"]) - rules.append(Rule(node.text, None, tests, node.path())) - for child in node: - find_rules(child, rules) - -def load_rules(specfile): - rules = [] - find_rules(xmlutil.parse(specfile), rules) - return rules - -def test_summary(): - template = """ - AMQP Tests - - - %s -
- - - """ - rows = [] - for rule in load_rules("amqp.org/specs/amqp7.xml"): - if rule.tests: - tests = ", ".join(rule.tests) - else: - tests = " " - rows.append('Path: %s' - 'Implement: %s' - 'Tests: %s' % - (rule.path[len("/root/amqp"):], rule.implement, tests)) - rows.append('%s' % rule.text) - rows.append(' ') - - print template % "\n".join(rows) + return spec08.load(specfile, *errata) diff --git a/qpid/python/qpid/spec08.py b/qpid/python/qpid/spec08.py new file mode 100644 index 0000000000..623b2e9e9f --- /dev/null +++ b/qpid/python/qpid/spec08.py @@ -0,0 +1,512 @@ +# +# 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. +# + +""" +This module loads protocol metadata into python objects. It provides +access to spec metadata via a python object model, and can also +dynamically creating python methods, classes, and modules based on the +spec metadata. All the generated methods have proper signatures and +doc strings based on the spec metadata so the python help system can +be used to browse the spec documentation. The generated methods all +dispatch to the self.invoke(meth, args) callback of the containing +class so that the generated code can be reused in a variety of +situations. +""" + +import re, textwrap, new, mllib, qpid + +class SpecContainer: + + def __init__(self): + self.items = [] + self.byname = {} + self.byid = {} + self.indexes = {} + + def add(self, item): + if self.byname.has_key(item.name): + raise ValueError("duplicate name: %s" % item) + if item.id == None: + item.id = len(self) + elif self.byid.has_key(item.id): + raise ValueError("duplicate id: %s" % item) + self.indexes[item] = len(self.items) + self.items.append(item) + self.byname[item.name] = item + self.byid[item.id] = item + + def index(self, item): + try: + return self.indexes[item] + except KeyError: + raise ValueError(item) + + def __iter__(self): + return iter(self.items) + + def __len__(self): + return len(self.items) + +class Metadata: + + PRINT = [] + + def __init__(self): + pass + + def __str__(self): + args = map(lambda f: "%s=%s" % (f, getattr(self, f)), self.PRINT) + return "%s(%s)" % (self.__class__.__name__, ", ".join(args)) + + def __repr__(self): + return str(self) + +class Spec(Metadata): + + PRINT=["major", "minor", "file"] + + def __init__(self, major, minor, file): + Metadata.__init__(self) + self.major = major + self.minor = minor + self.file = file + self.constants = SpecContainer() + self.domains = SpecContainer() + self.classes = SpecContainer() + # methods indexed by classname_methname + self.methods = {} + # structs by type code + self.structs = {} + + def post_load(self): + self.module = self.define_module("amqp%s%s" % (self.major, self.minor)) + self.klass = self.define_class("Amqp%s%s" % (self.major, self.minor)) + + def method(self, name): + if not self.methods.has_key(name): + for cls in self.classes: + clen = len(cls.name) + if name.startswith(cls.name) and name[clen] == "_": + end = name[clen + 1:] + if cls.methods.byname.has_key(end): + self.methods[name] = cls.methods.byname[end] + return self.methods.get(name) + + def parse_method(self, name): + parts = re.split(r"\s*\.\s*", name) + if len(parts) != 2: + raise ValueError(name) + klass, meth = parts + return self.classes.byname[klass].methods.byname[meth] + + def struct(self, name, *args, **kwargs): + type = self.domains.byname[name].type + return qpid.Struct(type, *args, **kwargs) + + def define_module(self, name, doc = None): + module = new.module(name, doc) + module.__file__ = self.file + for c in self.classes: + cls = c.define_class(c.name) + cls.__module__ = module.__name__ + setattr(module, c.name, cls) + return module + + def define_class(self, name): + methods = {} + for c in self.classes: + for m in c.methods: + meth = m.klass.name + "_" + m.name + methods[meth] = m.define_method(meth) + return type(name, (), methods) + +class Constant(Metadata): + + PRINT=["name", "id"] + + def __init__(self, spec, name, id, klass, docs): + Metadata.__init__(self) + self.spec = spec + self.name = name + self.id = id + self.klass = klass + self.docs = docs + +class Domain(Metadata): + + PRINT=["name", "type"] + + def __init__(self, spec, name, type, description, docs): + Metadata.__init__(self) + self.spec = spec + self.id = None + self.name = name + self.type = type + self.description = description + self.docs = docs + +class Struct(Metadata): + + PRINT=["size", "type", "pack"] + + def __init__(self, size, type, pack): + Metadata.__init__(self) + self.size = size + self.type = type + self.pack = pack + self.fields = SpecContainer() + +class Class(Metadata): + + PRINT=["name", "id"] + + def __init__(self, spec, name, id, handler, docs): + Metadata.__init__(self) + self.spec = spec + self.name = name + self.id = id + self.handler = handler + self.fields = SpecContainer() + self.methods = SpecContainer() + self.docs = docs + + def define_class(self, name): + methods = {} + for m in self.methods: + methods[m.name] = m.define_method(m.name) + return type(name, (), methods) + +class Method(Metadata): + + PRINT=["name", "id"] + + def __init__(self, klass, name, id, content, responses, result, synchronous, + description, docs): + Metadata.__init__(self) + self.klass = klass + self.name = name + self.id = id + self.content = content + self.responses = responses + self.result = result + self.synchronous = synchronous + self.fields = SpecContainer() + self.description = description + self.docs = docs + self.response = False + + def is_l4_command(self): + return self.klass.name not in ["execution", "channel", "connection", "session"] + + def arguments(self, *args, **kwargs): + nargs = len(args) + len(kwargs) + maxargs = len(self.fields) + if nargs > maxargs: + self._type_error("takes at most %s arguments (%s) given", maxargs, nargs) + result = [] + for f in self.fields: + idx = self.fields.index(f) + if idx < len(args): + result.append(args[idx]) + elif kwargs.has_key(f.name): + result.append(kwargs.pop(f.name)) + else: + result.append(Method.DEFAULTS[f.type]) + for key, value in kwargs.items(): + if self.fields.byname.has_key(key): + self._type_error("got multiple values for keyword argument '%s'", key) + else: + self._type_error("got an unexpected keyword argument '%s'", key) + return tuple(result) + + def _type_error(self, msg, *args): + raise TypeError("%s %s" % (self.name, msg % args)) + + def docstring(self): + s = "\n\n".join([fill(d, 2) for d in [self.description] + self.docs]) + for f in self.fields: + if f.docs: + s += "\n\n" + "\n\n".join([fill(f.docs[0], 4, f.name)] + + [fill(d, 4) for d in f.docs[1:]]) + if self.responses: + s += "\n\nValid responses: " + for r in self.responses: + s += r.name + " " + return s + + METHOD = "__method__" + DEFAULTS = {"bit": False, + "shortstr": "", + "longstr": "", + "table": {}, + "array": [], + "octet": 0, + "short": 0, + "long": 0, + "longlong": 0, + "timestamp": 0, + "content": None, + "uuid": "", + "rfc1982_long": 0, + "rfc1982_long_set": [], + "long_struct": None} + + def define_method(self, name): + g = {Method.METHOD: self} + l = {} + args = [(f.name, Method.DEFAULTS[f.type]) for f in self.fields] + methargs = args[:] + if self.content: + args += [("content", None)] + code = "def %s(self, %s):\n" % \ + (name, ", ".join(["%s = %r" % a for a in args])) + code += " %r\n" % self.docstring() + argnames = ", ".join([a[0] for a in methargs]) + code += " return self.invoke(%s" % Method.METHOD + if argnames: + code += ", (%s,)" % argnames + else: + code += ", ()" + if self.content: + code += ", content" + code += ")" + exec code in g, l + return l[name] + +class Field(Metadata): + + PRINT=["name", "id", "type"] + + def __init__(self, name, id, type, domain, description, docs): + Metadata.__init__(self) + self.name = name + self.id = id + self.type = type + self.domain = domain + self.description = description + self.docs = docs + + def default(self): + if isinstance(self.type, Struct): + return None + else: + return Method.DEFAULTS[self.type] + +WIDTHS = { + "octet": 1, + "short": 2, + "long": 4 + } + +def width(st, default=None): + if st in (None, "none", ""): + return default + else: + return WIDTHS[st] + +def get_result(nd, spec): + result = nd["result"] + if not result: return None + name = result["@domain"] + if name != None: return spec.domains.byname[name] + st_nd = result["struct"] + st = Struct(width(st_nd["@size"]), int(result.parent.parent["@index"])*256 + + int(st_nd["@type"]), width(st_nd["@pack"], 2)) + spec.structs[st.type] = st + load_fields(st_nd, st.fields, spec.domains.byname) + return st + +def get_desc(nd): + label = nd["@label"] + if not label: + label = nd.text() + if label: + label = label.strip() + return label + +def get_docs(nd): + return [n.text() for n in nd.query["doc"]] + +def load_fields(nd, l, domains): + for f_nd in nd.query["field"]: + type = f_nd["@domain"] + if type == None: + type = f_nd["@type"] + type = pythonize(type) + domain = None + while domains.has_key(type) and domains[type].type != type: + domain = domains[type] + type = domain.type + l.add(Field(pythonize(f_nd["@name"]), f_nd.index(), type, domain, + get_desc(f_nd), get_docs(f_nd))) + +def load(specfile, *errata): + doc = mllib.xml_parse(specfile) + spec_root = doc["amqp"] + spec = Spec(int(spec_root["@major"]), int(spec_root["@minor"]), specfile) + + for root in [spec_root] + map(lambda x: mllib.xml_parse(x)["amqp"], errata): + # constants + for nd in root.query["constant"]: + val = nd["@value"] + if val.startswith("0x"): val = int(val, 16) + else: val = int(val) + const = Constant(spec, pythonize(nd["@name"]), val, nd["@class"], + get_docs(nd)) + try: + spec.constants.add(const) + except ValueError, e: + pass + #print "Warning:", e + + # domains are typedefs + structs = [] + for nd in root.query["domain"]: + type = nd["@type"] + if type == None: + st_nd = nd["struct"] + code = st_nd["@type"] + if code not in (None, "", "none"): + code = int(code) + type = Struct(width(st_nd["@size"]), code, width(st_nd["@pack"], 2)) + if type.type != None: + spec.structs[type.type] = type + structs.append((type, st_nd)) + else: + type = pythonize(type) + domain = Domain(spec, pythonize(nd["@name"]), type, get_desc(nd), + get_docs(nd)) + spec.domains.add(domain) + + # structs + for st, st_nd in structs: + load_fields(st_nd, st.fields, spec.domains.byname) + + # classes + for c_nd in root.query["class"]: + cname = pythonize(c_nd["@name"]) + if spec.classes.byname.has_key(cname): + klass = spec.classes.byname[cname] + else: + klass = Class(spec, cname, int(c_nd["@index"]), c_nd["@handler"], + get_docs(c_nd)) + spec.classes.add(klass) + + added_methods = [] + load_fields(c_nd, klass.fields, spec.domains.byname) + for m_nd in c_nd.query["method"]: + mname = pythonize(m_nd["@name"]) + if klass.methods.byname.has_key(mname): + meth = klass.methods.byname[mname] + else: + meth = Method(klass, mname, + int(m_nd["@index"]), + m_nd["@content"] == "1", + [pythonize(nd["@name"]) for nd in m_nd.query["response"]], + get_result(m_nd, spec), + m_nd["@synchronous"] == "1", + get_desc(m_nd), + get_docs(m_nd)) + klass.methods.add(meth) + added_methods.append(meth) + load_fields(m_nd, meth.fields, spec.domains.byname) + # resolve the responses + for m in added_methods: + m.responses = [klass.methods.byname[r] for r in m.responses] + for resp in m.responses: + resp.response = True + + spec.post_load() + return spec + +REPLACE = {" ": "_", "-": "_"} +KEYWORDS = {"global": "global_", + "return": "return_"} + +def pythonize(name): + name = str(name) + for key, val in REPLACE.items(): + name = name.replace(key, val) + try: + name = KEYWORDS[name] + except KeyError: + pass + return name + +def fill(text, indent, heading = None): + sub = indent * " " + if heading: + init = (indent - 2) * " " + heading + " -- " + else: + init = sub + w = textwrap.TextWrapper(initial_indent = init, subsequent_indent = sub) + return w.fill(" ".join(text.split())) + +class Rule(Metadata): + + PRINT = ["text", "implement", "tests"] + + def __init__(self, text, implement, tests, path): + self.text = text + self.implement = implement + self.tests = tests + self.path = path + +def find_rules(node, rules): + if node.name == "rule": + rules.append(Rule(node.text, node.get("@implement"), + [ch.text for ch in node if ch.name == "test"], + node.path())) + if node.name == "doc" and node.get("@name") == "rule": + tests = [] + if node.has("@test"): + tests.append(node["@test"]) + rules.append(Rule(node.text, None, tests, node.path())) + for child in node: + find_rules(child, rules) + +def load_rules(specfile): + rules = [] + find_rules(xmlutil.parse(specfile), rules) + return rules + +def test_summary(): + template = """ + AMQP Tests + + + %s +
+ + + """ + rows = [] + for rule in load_rules("amqp.org/specs/amqp7.xml"): + if rule.tests: + tests = ", ".join(rule.tests) + else: + tests = " " + rows.append('Path: %s' + 'Implement: %s' + 'Tests: %s' % + (rule.path[len("/root/amqp"):], rule.implement, tests)) + rows.append('%s' % rule.text) + rows.append(' ') + + print template % "\n".join(rows) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index a0018c671f..adda1a650f 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -30,7 +30,7 @@ from qpid.content import Content from qpid.message import Message #0-10 support -from qpid.connection010 import Connection +from qpid.connection import Connection from qpid.spec010 import load from qpid.util import connect @@ -357,9 +357,9 @@ class TestBase010(unittest.TestCase): self.conn.start(timeout=10) self.session = self.conn.session("test-session", timeout=10) - def connect(self): + def connect(self, host=None, port=None): spec = testrunner.spec - conn = Connection(connect(testrunner.host, testrunner.port), spec) + conn = Connection(connect(host or testrunner.host, port or testrunner.port), spec) conn.start(timeout=10) return conn diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py index 521e2f15c9..8ad514fc2f 100644 --- a/qpid/python/tests/__init__.py +++ b/qpid/python/tests/__init__.py @@ -25,6 +25,6 @@ from spec import * from framer import * from assembler import * from datatypes import * -from connection010 import * +from connection import * from spec010 import * from codec010 import * diff --git a/qpid/python/tests/connection.py b/qpid/python/tests/connection.py new file mode 100644 index 0000000000..6925480ed3 --- /dev/null +++ b/qpid/python/tests/connection.py @@ -0,0 +1,136 @@ +# +# 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. +# + +from threading import * +from unittest import TestCase +from qpid.util import connect, listen +from qpid.connection import * +from qpid.datatypes import Message +from qpid.testlib import testrunner +from qpid.delegates import Server +from qpid.queue import Queue +from qpid.spec010 import load +from qpid.session import Delegate + +PORT = 1234 + +class TestServer: + + def __init__(self, queue): + self.queue = queue + + def connection(self, connection): + return Server(connection, delegate=self.session) + + def session(self, session): + session.auto_sync = False + return TestSession(session, self.queue) + +class TestSession(Delegate): + + def __init__(self, session, queue): + self.session = session + self.queue = queue + + def queue_query(self, qq): + return qq._type.result.type.new((qq.queue,), {}) + + def message_transfer(self, cmd, header, body): + self.queue.put((cmd, header, body)) + +class ConnectionTest(TestCase): + + def setUp(self): + self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) + self.queue = Queue() + self.running = True + started = Event() + + def run(): + ts = TestServer(self.queue) + for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()): + conn = Connection(s, self.spec, ts.connection) + try: + conn.start(5) + except Closed: + pass + + self.server = Thread(target=run) + self.server.setDaemon(True) + self.server.start() + + started.wait(3) + + def tearDown(self): + self.running = False + connect("0.0.0.0", PORT).close() + self.server.join(3) + + def test(self): + c = Connection(connect("0.0.0.0", PORT), self.spec) + c.start(10) + + ssn1 = c.session("test1", timeout=10) + ssn2 = c.session("test2", timeout=10) + + assert ssn1 == c.sessions["test1"] + assert ssn2 == c.sessions["test2"] + assert ssn1.channel != None + assert ssn2.channel != None + assert ssn1 in c.attached.values() + assert ssn2 in c.attached.values() + + ssn1.close(5) + + assert ssn1.channel == None + assert ssn1 not in c.attached.values() + assert ssn2 in c.sessions.values() + + ssn2.close(5) + + assert ssn2.channel == None + assert ssn2 not in c.attached.values() + assert ssn2 not in c.sessions.values() + + ssn = c.session("session", timeout=10) + + assert ssn.channel != None + assert ssn in c.sessions.values() + + destinations = ("one", "two", "three") + + for d in destinations: + ssn.message_transfer(d) + + for d in destinations: + cmd, header, body = self.queue.get(10) + assert cmd.destination == d + assert header == None + assert body == None + + msg = Message("this is a test") + ssn.message_transfer("four", message=msg) + cmd, header, body = self.queue.get(10) + assert cmd.destination == "four" + assert header == None + assert body == msg.body + + qq = ssn.queue_query("asdf") + assert qq.queue == "asdf" + c.close(5) diff --git a/qpid/python/tests/connection010.py b/qpid/python/tests/connection010.py deleted file mode 100644 index a953e034a2..0000000000 --- a/qpid/python/tests/connection010.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# 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. -# - -from threading import * -from unittest import TestCase -from qpid.util import connect, listen -from qpid.connection010 import * -from qpid.datatypes import Message -from qpid.testlib import testrunner -from qpid.delegates import Server -from qpid.queue import Queue -from qpid.spec010 import load -from qpid.session import Delegate - -PORT = 1234 - -class TestServer: - - def __init__(self, queue): - self.queue = queue - - def connection(self, connection): - return Server(connection, delegate=self.session) - - def session(self, session): - session.auto_sync = False - return TestSession(session, self.queue) - -class TestSession(Delegate): - - def __init__(self, session, queue): - self.session = session - self.queue = queue - - def queue_query(self, qq): - return qq._type.result.type.new((qq.queue,), {}) - - def message_transfer(self, cmd, header, body): - self.queue.put((cmd, header, body)) - -class ConnectionTest(TestCase): - - def setUp(self): - self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) - self.queue = Queue() - self.running = True - started = Event() - - def run(): - ts = TestServer(self.queue) - for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()): - conn = Connection(s, self.spec, ts.connection) - try: - conn.start(5) - except Closed: - pass - - self.server = Thread(target=run) - self.server.setDaemon(True) - self.server.start() - - started.wait(3) - - def tearDown(self): - self.running = False - connect("0.0.0.0", PORT).close() - self.server.join(3) - - def test(self): - c = Connection(connect("0.0.0.0", PORT), self.spec) - c.start(10) - - ssn1 = c.session("test1", timeout=10) - ssn2 = c.session("test2", timeout=10) - - assert ssn1 == c.sessions["test1"] - assert ssn2 == c.sessions["test2"] - assert ssn1.channel != None - assert ssn2.channel != None - assert ssn1 in c.attached.values() - assert ssn2 in c.attached.values() - - ssn1.close(5) - - assert ssn1.channel == None - assert ssn1 not in c.attached.values() - assert ssn2 in c.sessions.values() - - ssn2.close(5) - - assert ssn2.channel == None - assert ssn2 not in c.attached.values() - assert ssn2 not in c.sessions.values() - - ssn = c.session("session", timeout=10) - - assert ssn.channel != None - assert ssn in c.sessions.values() - - destinations = ("one", "two", "three") - - for d in destinations: - ssn.message_transfer(d) - - for d in destinations: - cmd, header, body = self.queue.get(10) - assert cmd.destination == d - assert header == None - assert body == None - - msg = Message("this is a test") - ssn.message_transfer("four", message=msg) - cmd, header, body = self.queue.get(10) - assert cmd.destination == "four" - assert header == None - assert body == msg.body - - qq = ssn.queue_query("asdf") - assert qq.queue == "asdf" - c.close(5) diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py new file mode 100644 index 0000000000..e893dbbd87 --- /dev/null +++ b/qpid/python/tests_0-10/management.py @@ -0,0 +1,89 @@ +# +# 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. +# + +from qpid.datatypes import Message, RangedSet +from qpid.testlib import TestBase010 +from qpid.management import managementChannel, managementClient + +class ManagementTest (TestBase010): + """ + Tests for the management hooks + """ + + def test_broker_connectivity (self): + """ + Call the "echo" method on the broker to verify it is alive and talking. + """ + session = self.session + + mc = managementClient (session.spec) + mch = mc.addChannel (session) + + mc.syncWaitForStable (mch) + brokers = mc.syncGetObjects (mch, "broker") + self.assertEqual (len (brokers), 1) + broker = brokers[0] + args = {} + body = "Echo Message Body" + args["body"] = body + + for seq in range (1, 5): + args["sequence"] = seq + res = mc.syncCallMethod (mch, broker.id, broker.classKey, "echo", args) + self.assertEqual (res.status, 0) + self.assertEqual (res.statusText, "OK") + self.assertEqual (res.sequence, seq) + self.assertEqual (res.body, body) + + def test_system_object (self): + session = self.session + + mc = managementClient (session.spec) + mch = mc.addChannel (session) + + mc.syncWaitForStable (mch) + systems = mc.syncGetObjects (mch, "system") + self.assertEqual (len (systems), 1) + + def test_standard_exchanges (self): + session = self.session + + mc = managementClient (session.spec) + mch = mc.addChannel (session) + + mc.syncWaitForStable (mch) + exchanges = mc.syncGetObjects (mch, "exchange") + exchange = self.findExchange (exchanges, "") + self.assertEqual (exchange.type, "direct") + exchange = self.findExchange (exchanges, "amq.direct") + self.assertEqual (exchange.type, "direct") + exchange = self.findExchange (exchanges, "amq.topic") + self.assertEqual (exchange.type, "topic") + exchange = self.findExchange (exchanges, "amq.fanout") + self.assertEqual (exchange.type, "fanout") + exchange = self.findExchange (exchanges, "amq.match") + self.assertEqual (exchange.type, "headers") + exchange = self.findExchange (exchanges, "qpid.management") + self.assertEqual (exchange.type, "topic") + + def findExchange (self, exchanges, name): + for exchange in exchanges: + if exchange.name == name: + return exchange + return None diff --git a/qpid/python/tests_0-10/persistence.py b/qpid/python/tests_0-10/persistence.py new file mode 100644 index 0000000000..a4b5691910 --- /dev/null +++ b/qpid/python/tests_0-10/persistence.py @@ -0,0 +1,67 @@ +# +# 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. +# +from qpid.datatypes import Message, RangedSet +from qpid.testlib import testrunner, TestBase010 + +class PersistenceTests(TestBase010): + def test_delete_queue_after_publish(self): + session = self.session + session.auto_sync = False + + #create queue + session.queue_declare(queue = "q", auto_delete=True, durable=True) + + #send message + for i in range(1, 10): + dp = session.delivery_properties(routing_key="q", delivery_mode=2) + session.message_transfer(message=Message(dp, "my-message")) + + session.auto_sync = True + #explicitly delete queue + session.queue_delete(queue = "q") + + def test_ack_message_from_deleted_queue(self): + session = self.session + session.auto_sync = False + + #create queue + session.queue_declare(queue = "q", auto_delete=True, durable=True) + + #send message + dp = session.delivery_properties(routing_key="q", delivery_mode=2) + session.message_transfer(message=Message(dp, "my-message")) + + #create consumer + session.message_subscribe(queue = "q", destination = "a", accept_mode = 1, acquire_mode=0) + session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = 0, value = 10, destination = "a") + queue = session.incoming("a") + + #consume the message, cancel subscription (triggering auto-delete), then ack it + msg = queue.get(timeout = 5) + session.message_cancel(destination = "a") + session.message_accept(RangedSet(msg.id)) + + def test_queue_deletion(self): + session = self.session + session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True) + session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz") + dp = session.delivery_properties(routing_key="xyz", delivery_mode=2) + session.message_transfer(destination="amq.topic", message=Message(dp, "my-message")) + session.queue_delete(queue = "durable-subscriber-queue") diff --git a/qpid/python/tests_0-10_preview/management.py b/qpid/python/tests_0-10_preview/management.py deleted file mode 100644 index de6161ae96..0000000000 --- a/qpid/python/tests_0-10_preview/management.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# 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. -# - -from qpid.datatypes import Message, RangedSet -from qpid.testlib import TestBase -from qpid.management import managementChannel, managementClient - -class ManagementTest (TestBase): - """ - Tests for the management hooks - """ - - def test_broker_connectivity (self): - """ - Call the "echo" method on the broker to verify it is alive and talking. - """ - channel = self.client.channel(2) - - mc = managementClient (channel.spec) - mch = mc.addChannel (channel) - - mc.syncWaitForStable (mch) - brokers = mc.syncGetObjects (mch, "broker") - self.assertEqual (len (brokers), 1) - broker = brokers[0] - args = {} - body = "Echo Message Body" - args["body"] = body - - for seq in range (1, 5): - args["sequence"] = seq - res = mc.syncCallMethod (mch, broker.id, broker.classKey, "echo", args) - self.assertEqual (res.status, 0) - self.assertEqual (res.statusText, "OK") - self.assertEqual (res.sequence, seq) - self.assertEqual (res.body, body) - - def test_system_object (self): - channel = self.client.channel(2) - - mc = managementClient (channel.spec) - mch = mc.addChannel (channel) - - mc.syncWaitForStable (mch) - systems = mc.syncGetObjects (mch, "system") - self.assertEqual (len (systems), 1) - - def test_standard_exchanges (self): - channel = self.client.channel(2) - - mc = managementClient (channel.spec) - mch = mc.addChannel (channel) - - mc.syncWaitForStable (mch) - exchanges = mc.syncGetObjects (mch, "exchange") - exchange = self.findExchange (exchanges, "") - self.assertEqual (exchange.type, "direct") - exchange = self.findExchange (exchanges, "amq.direct") - self.assertEqual (exchange.type, "direct") - exchange = self.findExchange (exchanges, "amq.topic") - self.assertEqual (exchange.type, "topic") - exchange = self.findExchange (exchanges, "amq.fanout") - self.assertEqual (exchange.type, "fanout") - exchange = self.findExchange (exchanges, "amq.match") - self.assertEqual (exchange.type, "headers") - exchange = self.findExchange (exchanges, "qpid.management") - self.assertEqual (exchange.type, "topic") - - def findExchange (self, exchanges, name): - for exchange in exchanges: - if exchange.name == name: - return exchange - return None -- cgit v1.2.1 From 303807136b7ff8f8c7ed0e2884ac858fbe8dc95f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 22 Apr 2008 18:15:48 +0000 Subject: QPID-948: patch from Ted Ross for updated management utilities to 0-10 final git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@650604 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 17 ++++++++--------- qpid/python/commands/qpid-route | 16 ++++++++-------- qpid/python/commands/qpid-tool | 4 ++-- qpid/python/qpid/managementdata.py | 14 +++++++------- 4 files changed, 25 insertions(+), 26 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index b93e74c4d2..20b08933a4 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -19,7 +19,7 @@ # under the License. # -import os +import os, uuid import getopt import sys import socket @@ -27,10 +27,11 @@ import qpid from threading import Condition from qpid.management import managementClient from qpid.peer import Closed -from qpid.client import Client +from qpid.connection import Connection +from qpid.util import connect from time import sleep -_defspecpath = "/usr/share/amqp/amqp.0-10-preview.xml" +_defspecpath = "/usr/share/amqp/amqp.0-10.xml" _specpath = _defspecpath _recursive = False _host = "localhost" @@ -95,13 +96,11 @@ class BrokerManager: def ConnectToBroker (self): try: - self.spec = qpid.spec.load (_specpath) - self.client = Client (self.broker.host, self.broker.port, self.spec) - self.client.start (response='\x00' + "guest" + '\x00' + "guest", - mechanism="PLAIN") - self.channel = self.client.channel (1) + self.spec = qpid.spec.load (_specpath) + self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) + self.conn.start () self.mclient = managementClient (self.spec) - self.mchannel = self.mclient.addChannel (self.channel) + self.mchannel = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) except socket.error, e: print "Socket Error:", e sys.exit (1) diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index c268c638c8..a0755641c9 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -23,9 +23,11 @@ import getopt import sys import socket import qpid +import uuid from qpid.management import managementClient from qpid.peer import Closed -from qpid.client import Client +from qpid.connection import Connection +from qpid.util import connect def Usage (): print "Usage: qpid-route [OPTIONS] add " @@ -35,7 +37,7 @@ def Usage (): print " qpid-route [OPTIONS] flush " print print "Options:" - print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10-preview.xml)" + print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10.xml)" print " -v [ --verbose ] Verbose output" print " -q [ --quiet ] Quiet output, don't print duplicate warnings" print @@ -49,7 +51,7 @@ def Usage (): #print sys.exit (1) -_specpath = "/usr/share/amqp/amqp.0-10-preview.xml" +_specpath = "/usr/share/amqp/amqp.0-10.xml" _verbose = False _quiet = False @@ -78,12 +80,10 @@ class RouteManager: print "Connecting to broker: %s:%d" % (broker.host, broker.port) try: self.spec = qpid.spec.load (_specpath) - self.client = Client (broker.host, broker.port, self.spec) - self.client.start (response='\x00' + "guest" + '\x00' + "guest", - mechanism="PLAIN") - self.channel = self.client.channel (1) + self.conn = Connection (connect (broker.host, broker.port), self.spec) + self.conn.start () self.mclient = managementClient (self.spec) - self.mch = self.mclient.addChannel (self.channel) + self.mch = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) self.mclient.syncWaitForStable (self.mch) except socket.error, e: print "Connect Error:", e diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 0983e1b8af..02579b9d35 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -127,7 +127,7 @@ def Usage (): print "Usage:", sys.argv[0], "[OPTIONS] []]" print print "Options:" - print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10-preview.xml)" + print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10.xml)" print sys.exit (1) @@ -143,7 +143,7 @@ except: Usage () sys.exit (1) -_specpath = "/usr/share/amqp/amqp.0-10-preview.xml" +_specpath = "/usr/share/amqp/amqp.0-10.xml" _host = "localhost" for opt in optlist: diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 2876bf0948..327ae96a26 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -22,11 +22,13 @@ import qpid import socket import struct +import uuid from qpid.management import managementChannel, managementClient from threading import Lock from disp import Display from shlex import split -from qpid.client import Client +from qpid.connection import Connection +from qpid.util import connect class Broker: def __init__ (self, text): @@ -159,7 +161,7 @@ class ManagementData: self.schema[className] = (configs, insts, methods, events) def __init__ (self, disp, host, username="guest", password="guest", - specfile="../../specs/amqp.0-10-preview.xml"): + specfile="../../specs/amqp.0-10.xml"): self.spec = qpid.spec.load (specfile) self.lock = Lock () self.tables = {} @@ -171,15 +173,13 @@ class ManagementData: self.methodsPending = {} self.broker = Broker (host) - self.client = Client (self.broker.host, self.broker.port, self.spec) - self.client.start (response='\x00' + username + '\x00' + password, - mechanism="PLAIN") - self.channel = self.client.channel (1) + self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) + self.conn.start () self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, self.instHandler, self.methodReply) self.mclient.schemaListener (self.schemaHandler) - self.mch = self.mclient.addChannel (self.channel) + self.mch = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) def close (self): self.mclient.removeChannel (self.mch) -- cgit v1.2.1 From 74cae9fc609b5964d6e49f405dca07b2b1df592b Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Tue, 22 Apr 2008 19:22:58 +0000 Subject: QPID-945 from Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@650620 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-queue-stats | 159 ++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100755 qpid/python/commands/qpid-queue-stats (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats new file mode 100755 index 0000000000..6e3579edfd --- /dev/null +++ b/qpid/python/commands/qpid-queue-stats @@ -0,0 +1,159 @@ +#!/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 os +import getopt +import sys +import socket +import qpid +import uuid +from threading import Condition +from qpid.management import managementClient +from qpid.peer import Closed +from qpid.connection import Connection +from qpid.util import connect +from time import sleep + +defspecpath = "/usr/share/amqp/amqp.0-10.xml" +specpath = defspecpath +host = "localhost" + +def Usage (): + print "Usage: qpid-queue-stats [OPTIONS]" + print + print "Options:" + print " -a default: localhost" + print " broker-addr is in the form: hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000" + print " -s default:", defspecpath + print + sys.exit (1) + +class Broker: + def __init__ (self, text): + colon = text.find (":") + if colon == -1: + host = text + self.port = 5672 + else: + host = text[:colon] + self.port = int (text[colon+1:]) + self.host = socket.gethostbyname (host) + + def name (self): + return self.host + ":" + str (self.port) + +class mgmtObject (object): + """ Generic object that holds the contents of a management object with its + attributes set as object attributes. """ + + def __init__ (self, classKey, timestamps, row): + self.classKey = classKey + self.timestamps = timestamps + for cell in row: + setattr (self, cell[0], cell[1]) + + + +class BrokerManager: + def __init__ (self): + self.dest = None + self.src = None + self.broker = None + self.objects = {} + + def SetBroker (self, broker): + self.broker = broker + + def ConnectToBroker (self): + try: + self.spec = qpid.spec.load (specpath) + self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) + self.conn.start () + self.mclient = managementClient (self.spec, None, self.configCb, self.instCb) + self.mchannel = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) + except socket.error, e: + print "Connect Error:", e + exit (1) + + def configCb (self, context, classKey, row, timestamps): + className = classKey[1] + if className != "queue": + return + + obj = mgmtObject (classKey, timestamps, row) + if obj.id not in self.objects: + self.objects[obj.id] = (obj.name, None, None) + + def instCb (self, context, classKey, row, timestamps): + className = classKey[1] + if className != "queue": + return + + obj = mgmtObject (classKey, timestamps, row) + if obj.id not in self.objects: + return + + (name, first, last) = self.objects[obj.id] + if first == None: + self.objects[obj.id] = (name, obj, None) + return + + if last == None: + lastSample = first + else: + lastSample = last + + self.objects[obj.id] = (name, first, obj) + + deltaTime = float (obj.timestamps[0] - lastSample.timestamps[0]) + enqueueRate = float (obj.msgTotalEnqueues - lastSample.msgTotalEnqueues) / (deltaTime / 1000000000.0) + dequeueRate = float (obj.msgTotalDequeues - lastSample.msgTotalDequeues) / (deltaTime / 1000000000.0) + print "%-41s%10.2f%10d..%-10d%13.2f%13.2f" % \ + (name, deltaTime / 1000000000, obj.msgDepthLow, obj.msgDepthHigh, enqueueRate, dequeueRate) + + + def Overview (self): + self.ConnectToBroker () + print "Queue Name Sec Depth Range Enq Rate Deq Rate" + print "===================================================================================================" + while True: + sleep (1) + +## +## Main Program +## + +try: + (optlist, cargs) = getopt.getopt (sys.argv[1:], "s:a:") +except: + Usage () + +for opt in optlist: + if opt[0] == "-s": + specpath = opt[1] + if opt[0] == "-a": + host = opt[1] + +nargs = len (cargs) +bm = BrokerManager () +bm.SetBroker (Broker (host)) +bm.Overview () -- cgit v1.2.1 From 1f351ba2cbc2631deedd62ce27de0da0e07db08c Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 23 Apr 2008 08:53:15 +0000 Subject: Added to the no-local tests, cleaned up comments (and highlighted non-standard nature of these tests) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@650795 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 8302515b2f..ac915f7863 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -30,7 +30,9 @@ class MessageTests(TestBase010): def test_no_local(self): """ - Test that the no_local flag is honoured in the consume method + NOTE: this is a test of a QPID specific feature + + Test that the qpid specific no_local arg is honoured. """ session = self.session #setup, declare two queues one of which excludes delivery of locally sent messages @@ -44,11 +46,27 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1a"), "deliver-me")) session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1b"), "dont-deliver-me")) + #send a message from another session on the same connection to each queue + session2 = self.conn.session("my-local-session") + session2.message_transfer(message=Message(session2.delivery_properties(routing_key="test-queue-1a"), "deliver-me-as-well")) + session2.message_transfer(message=Message(session2.delivery_properties(routing_key="test-queue-1b"), "dont-deliver-me-either")) + + #send a message from a session on another connection to each queue + for q in ["test-queue-1a", "test-queue-1b"]: + session.exchange_bind(queue=q, exchange="amq.fanout", binding_key="my-key") + other = self.connect() + session3 = other.session("my-other-session") + session3.message_transfer(destination="amq.fanout", message=Message("i-am-not-local")) + other.close() + #check the queues of the two consumers excluded = session.incoming("local_excluded") included = session.incoming("local_included") - msg = included.get(timeout=1) - self.assertEqual("deliver-me", msg.body) + for b in ["deliver-me", "deliver-me-as-well", "i-am-not-local"]: + msg = included.get(timeout=1) + self.assertEqual(b, msg.body) + msg = excluded.get(timeout=1) + self.assertEqual("i-am-not-local", msg.body) try: excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") @@ -57,12 +75,10 @@ class MessageTests(TestBase010): def test_no_local_awkward(self): """ - If an exclusive queue gets a no-local delivered to it, that - message could 'block' delivery of subsequent messages or it - could be left on the queue, possibly never being consumed - (this is the case for example in the qpid JMS mapping of - topics). This test excercises a Qpid C++ broker hack that - deletes such messages. + NOTE: this is a test of a QPID specific feature + + Check that messages which will be excluded through no-local + processing will not block subsequent deliveries """ session = self.session @@ -94,7 +110,10 @@ class MessageTests(TestBase010): def test_no_local_exclusive_subscribe(self): """ - Test that the no_local flag is honoured in the consume method + NOTE: this is a test of a QPID specific feature + + Test that the no_local processing works on queues not declared + as exclusive, but with an exclusive subscription """ session = self.session @@ -106,15 +125,31 @@ class MessageTests(TestBase010): self.subscribe(destination="local_included", queue="test-queue-1a") self.subscribe(destination="local_excluded", queue="test-queue-1b", exclusive=True) - #send a message + #send a message from the same session to each queue session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1a"), "deliver-me")) session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-1b"), "dont-deliver-me")) + #send a message from another session on the same connection to each queue + session2 = self.conn.session("my-session") + session2.message_transfer(message=Message(session2.delivery_properties(routing_key="test-queue-1a"), "deliver-me-as-well")) + session2.message_transfer(message=Message(session2.delivery_properties(routing_key="test-queue-1b"), "dont-deliver-me-either")) + + #send a message from a session on another connection to each queue + for q in ["test-queue-1a", "test-queue-1b"]: + session.exchange_bind(queue=q, exchange="amq.fanout", binding_key="my-key") + other = self.connect() + session3 = other.session("my-other-session") + session3.message_transfer(destination="amq.fanout", message=Message("i-am-not-local")) + other.close() + #check the queues of the two consumers excluded = session.incoming("local_excluded") included = session.incoming("local_included") - msg = included.get(timeout=1) - self.assertEqual("deliver-me", msg.body) + for b in ["deliver-me", "deliver-me-as-well", "i-am-not-local"]: + msg = included.get(timeout=1) + self.assertEqual(b, msg.body) + msg = excluded.get(timeout=1) + self.assertEqual("i-am-not-local", msg.body) try: excluded.get(timeout=1) self.fail("Received locally published message though no_local=true") -- cgit v1.2.1 From 8c89d05f7d73ff9256cee24bb6274a16cad4b7c0 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 23 Apr 2008 14:07:07 +0000 Subject: Add support for reading 0-10 arrays; Set sync bit on session header for commands sent with auto_sync on. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@650875 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 9 ++++++++- qpid/python/qpid/session.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 27fcd5d418..0ba3341665 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -179,7 +179,14 @@ class Codec(Packer): def write_array(self, a): pass def read_array(self): - pass + sc = StringCodec(self.spec, self.read_vbin32()) + type = self.spec.types[sc.read_uint8()] + count = sc.read_uint32() + result = [] + while count: + result.append(type.decode(sc)) + count = count - 1 + return result def read_struct32(self): size = self.read_uint32() diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index f649b95a2c..11249ca435 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -152,6 +152,7 @@ class Session(Invoker): cmd = type.new(args, kwargs) sc = StringCodec(self.spec) hdr = Struct(self.spec["session.header"]) + hdr.sync = self.auto_sync sc.write_command(hdr, cmd) seg = Segment(True, (message == None or -- cgit v1.2.1 From e48979cba21658045c8ccb7fd17a8fb354fd7984 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Thu, 24 Apr 2008 15:24:51 +0000 Subject: QPID-953 from tross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651290 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 10 +++++++--- qpid/python/commands/qpid-queue-stats | 19 +++++++++++++------ qpid/python/commands/qpid-route | 10 +++++++--- qpid/python/commands/qpid-tool | 8 ++++++++ qpid/python/qpid/management.py | 17 ++++++++++++++--- qpid/python/qpid/managementdata.py | 10 +++++++--- qpid/python/tests_0-10/management.py | 3 +++ 7 files changed, 59 insertions(+), 18 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 20b08933a4..03a0fd8538 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -19,7 +19,7 @@ # under the License. # -import os, uuid +import os import getopt import sys import socket @@ -97,10 +97,11 @@ class BrokerManager: def ConnectToBroker (self): try: self.spec = qpid.spec.load (_specpath) + self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) self.conn.start () self.mclient = managementClient (self.spec) - self.mchannel = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) + self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) except socket.error, e: print "Socket Error:", e sys.exit (1) @@ -108,6 +109,9 @@ class BrokerManager: print "Connect Failed:", e sys.exit (1) + def Disconnect (self): + self.mclient.removeChannel (self.mchannel) + def Overview (self): self.ConnectToBroker () mc = self.mclient @@ -374,4 +378,4 @@ else: bm.Unbind (cargs[1:]) else: Usage () - +bm.Disconnect() diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 6e3579edfd..ff28e5b50c 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -24,7 +24,6 @@ import getopt import sys import socket import qpid -import uuid from threading import Condition from qpid.management import managementClient from qpid.peer import Closed @@ -86,14 +85,18 @@ class BrokerManager: def ConnectToBroker (self): try: self.spec = qpid.spec.load (specpath) + self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) self.conn.start () self.mclient = managementClient (self.spec, None, self.configCb, self.instCb) - self.mchannel = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) + self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) except socket.error, e: print "Connect Error:", e exit (1) + def Disconnect (self): + self.mclient.removeChannel (self.mchannel) + def configCb (self, context, classKey, row, timestamps): className = classKey[1] if className != "queue": @@ -131,12 +134,16 @@ class BrokerManager: (name, deltaTime / 1000000000, obj.msgDepthLow, obj.msgDepthHigh, enqueueRate, dequeueRate) - def Overview (self): + def Display (self): self.ConnectToBroker () print "Queue Name Sec Depth Range Enq Rate Deq Rate" print "===================================================================================================" - while True: - sleep (1) + try: + while True: + sleep (1) + except KeyboardInterrupt: + pass + self.Disconnect () ## ## Main Program @@ -156,4 +163,4 @@ for opt in optlist: nargs = len (cargs) bm = BrokerManager () bm.SetBroker (Broker (host)) -bm.Overview () +bm.Display () diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index a0755641c9..a7fbb17777 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -23,7 +23,7 @@ import getopt import sys import socket import qpid -import uuid +import os from qpid.management import managementClient from qpid.peer import Closed from qpid.connection import Connection @@ -80,15 +80,19 @@ class RouteManager: print "Connecting to broker: %s:%d" % (broker.host, broker.port) try: self.spec = qpid.spec.load (_specpath) + self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.conn = Connection (connect (broker.host, broker.port), self.spec) self.conn.start () self.mclient = managementClient (self.spec) - self.mch = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) + self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) self.mclient.syncWaitForStable (self.mch) except socket.error, e: print "Connect Error:", e sys.exit (1) + def Disconnect (self): + self.mclient.removeChannel (self.mch) + def getLink (self): links = self.mclient.syncGetObjects (self.mch, "link") for link in links: @@ -268,4 +272,4 @@ else: rm.ClearAllRoutes () else: Usage () - +rm.Disconnect () diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 02579b9d35..1aee3a1b7f 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -111,9 +111,17 @@ class Mcli (Cmd): def do_EOF (self, data): print "quit" + try: + self.dataObject.do_exit () + except: + pass return True def do_quit (self, data): + try: + self.dataObject.do_exit () + except: + pass return True def postcmd (self, stop, line): diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 3a7a564e19..3f8d3c9cfe 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -85,6 +85,8 @@ class managementChannel: """ Given a channel on an established AMQP broker connection, this method opens a session and performs all of the declarations and bindings needed to participate in the management protocol. """ + self.enabled = True + self.ssn = ssn self.sessionId = ssn.name self.topicName = "mgmt-%s" % self.sessionId self.replyName = "repl-%s" % self.sessionId @@ -115,16 +117,24 @@ class managementChannel: ssn.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) ssn.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) + def shutdown (self): + self.enabled = False + self.ssn.message_cancel (destination="tdest") + self.ssn.message_cancel (destination="rdest") + def topicCb (self, msg): """ Receive messages via the topic queue on this channel. """ - self.tcb (self, msg) + if self.enabled: + self.tcb (self, msg) def replyCb (self, msg): """ Receive messages via the reply queue on this channel. """ - self.rcb (self, msg) + if self.enabled: + self.rcb (self, msg) def send (self, exchange, msg): - self.qpidChannel.message_transfer (destination=exchange, message=msg) + if self.enabled: + self.qpidChannel.message_transfer (destination=exchange, message=msg) def accept (self, msg): self.qpidChannel.message_accept(RangedSet(msg.id)) @@ -193,6 +203,7 @@ class managementClient: def removeChannel (self, mch): """ Remove a previously added channel from management. """ + mch.shutdown () self.channels.remove (mch) def callMethod (self, channel, userSequence, objId, className, methodName, args=None): diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 327ae96a26..c908483354 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -22,7 +22,7 @@ import qpid import socket import struct -import uuid +import os from qpid.management import managementChannel, managementClient from threading import Lock from disp import Display @@ -171,6 +171,7 @@ class ManagementData: self.lastUnit = None self.methodSeq = 1 self.methodsPending = {} + self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.broker = Broker (host) self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) @@ -179,10 +180,10 @@ class ManagementData: self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, self.instHandler, self.methodReply) self.mclient.schemaListener (self.schemaHandler) - self.mch = self.mclient.addChannel (self.conn.session(str(uuid.uuid4()))) + self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) def close (self): - self.mclient.removeChannel (self.mch) + pass def refName (self, oid): if oid == 0: @@ -626,3 +627,6 @@ class ManagementData: methodName = tokens[1] args = tokens[2:] self.callMethod (userOid, methodName, args) + + def do_exit (self): + self.mclient.removeChannel (self.mch) diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index e893dbbd87..b2ab617244 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -50,6 +50,7 @@ class ManagementTest (TestBase010): self.assertEqual (res.statusText, "OK") self.assertEqual (res.sequence, seq) self.assertEqual (res.body, body) + mc.removeChannel (mch) def test_system_object (self): session = self.session @@ -60,6 +61,7 @@ class ManagementTest (TestBase010): mc.syncWaitForStable (mch) systems = mc.syncGetObjects (mch, "system") self.assertEqual (len (systems), 1) + mc.removeChannel (mch) def test_standard_exchanges (self): session = self.session @@ -81,6 +83,7 @@ class ManagementTest (TestBase010): self.assertEqual (exchange.type, "headers") exchange = self.findExchange (exchanges, "qpid.management") self.assertEqual (exchange.type, "topic") + mc.removeChannel (mch) def findExchange (self, exchanges, name): for exchange in exchanges: -- cgit v1.2.1 From 92b42a12235a27887b6955b04983489af9334de3 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 24 Apr 2008 21:05:06 +0000 Subject: Correct expected error codes for final 0-10 spec git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@651421 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index ac915f7863..c54622e091 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -167,7 +167,7 @@ class MessageTests(TestBase010): session.message_subscribe(destination="second", queue="test-queue-2") self.fail("Expected consume request to fail due to previous exclusive consumer") except SessionException, e: - self.assertEquals(403, e.args[0].error_code) + self.assertEquals(405, e.args[0].error_code) def test_consume_exclusive2(self): """ @@ -180,7 +180,7 @@ class MessageTests(TestBase010): session.message_subscribe(destination="second", queue="test-queue-2", exclusive=True) self.fail("Expected exclusive consume request to fail due to previous consumer") except SessionException, e: - self.assertEquals(403, e.args[0].error_code) + self.assertEquals(405, e.args[0].error_code) def test_consume_queue_not_found(self): """ -- cgit v1.2.1 From 8464d20e48292a369bc43ece52e7a996dd895008 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 29 Apr 2008 20:24:48 +0000 Subject: QPID-979: added backwards compatible uuid to qpid.datatypes git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@652086 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/datatypes.py | 39 ++++++++++++++++++++++++++++++++++++++- qpid/python/tests/datatypes.py | 9 +++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 0893269174..36b290700b 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -17,7 +17,7 @@ # under the License. # -import threading +import threading, struct class Struct: @@ -172,3 +172,40 @@ class Future: def is_set(self): return self._set.isSet() + +try: + import uuid + def random_uuid(): + return uuid.uuid4().get_bytes() +except ImportError: + import random + def random_uuid(): + bytes = [random.randint(0, 255) for i in xrange(16)] + + # From RFC4122, the version bits are set to 0100 + bytes[7] &= 0x0F + bytes[7] |= 0x40 + + # From RFC4122, the top two bits of byte 8 get set to 01 + bytes[8] &= 0x3F + bytes[8] |= 0x80 + return "".join(map(chr, bytes)) + +def uuid4(): + return UUID(random_uuid()) + +class UUID: + + def __init__(self, bytes): + self.bytes = bytes + + def __cmp__(self, other): + if isinstance(other, UUID): + return cmp(self.bytes, other.bytes) + raise NotImplemented() + + def __str__(self): + return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack("!LHHHHL", self.bytes) + + def __repr__(self): + return "UUID(%r)" % str(self) diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index 7844cf4d10..e4d6723d95 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -100,3 +100,12 @@ class RangedSetTest(TestCase): range = a.ranges[0] assert range.lower == 0 assert range.upper == 8 + +class UUIDTest(TestCase): + + def test(self): + # this test is kind of lame, but it does excercise the basic + # functionality of the class + u = uuid4() + for i in xrange(1024): + assert u != uuid4() -- cgit v1.2.1 From 80a3340f9a51da26ee22f163b0ae474e42c0faa1 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 29 Apr 2008 21:00:18 +0000 Subject: QPID-981: allow id and exclude list to be passed through when creating a bridge with qpid-route git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@652114 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index a7fbb17777..b08293fa00 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -30,7 +30,7 @@ from qpid.connection import Connection from qpid.util import connect def Usage (): - print "Usage: qpid-route [OPTIONS] add " + print "Usage: qpid-route [OPTIONS] add [id] [exclude-list]" print " qpid-route [OPTIONS] del " print " qpid-route [OPTIONS] list " #print " qpid-route [OPTIONS] load " @@ -100,7 +100,7 @@ class RouteManager: return link return None - def AddRoute (self, srcBroker, exchange, routingKey): + def AddRoute (self, srcBroker, exchange, routingKey, id, excludes): self.src = Broker (srcBroker) mc = self.mclient @@ -138,6 +138,8 @@ class RouteManager: bridgeArgs["src"] = exchange bridgeArgs["dest"] = exchange bridgeArgs["key"] = routingKey + bridgeArgs["id"] = id + bridgeArgs["excludes"] = excludes bridgeArgs["src_is_queue"] = 0 bridgeArgs["src_is_local"] = 0 res = mc.syncCallMethod (self.mch, link.id, link.classKey, "bridge", bridgeArgs) @@ -253,11 +255,18 @@ if cmd != "load": rm = RouteManager (cargs[1]) rm.ConnectToBroker () -if cmd == "add" or cmd == "del": +if cmd == "add": + if nargs < 5 or nargs > 7: + Usage () + + id = "" + excludes = "" + if nargs > 5: id = cargs[5] + if nargs > 6: excludes = cargs[6] + rm.AddRoute (cargs[2], cargs[3], cargs[4], id, excludes) +elif cmd == "del": if nargs != 5: Usage () - if cmd == "add": - rm.AddRoute (cargs[2], cargs[3], cargs[4]) else: rm.DelRoute (cargs[2], cargs[3], cargs[4]) else: -- cgit v1.2.1 From 9262e03f9381782009a4da0f50d36aaabd20bbe3 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 30 Apr 2008 14:16:38 +0000 Subject: QPID-988 and QPID-989: fixes to framing for final 0-10 spec git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@652386 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 3 ++- qpid/python/qpid/framer.py | 6 ------ 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 0ba3341665..1c76666de1 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -156,6 +156,7 @@ class Codec(Packer): def write_map(self, m): sc = StringCodec(self.spec) + sc.write_uint32(len(m)) for k, v in m.items(): type = self.spec.encoding(v.__class__) if type == None: @@ -163,10 +164,10 @@ class Codec(Packer): sc.write_str8(k) sc.write_uint8(type.code) type.encode(sc, v) - # XXX: need to put in count when CPP supports it self.write_vbin32(sc.encoded) def read_map(self): sc = StringCodec(self.spec, self.read_vbin32()) + count = sc.read_uint32() result = {} while sc.encoded: k = sc.read_str8() diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index fb0e677cee..78a29235cb 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -131,8 +131,6 @@ class Framer(Packer): track = frame.track & 0x0F self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) self.write(frame.payload) - # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ - self.write("\xCE") frm.debug("SENT %s", frame) finally: self.sock_lock.release() @@ -141,10 +139,6 @@ class Framer(Packer): flags, type, size, track, channel = self.unpack(Frame.HEADER) if flags & 0xF0: raise FramingError() payload = self.read(size - struct.calcsize(Frame.HEADER)) - # XXX: NOT 0-10 FINAL, TEMPORARY WORKAROUND for C++ - end = self.read(1) - if end != "\xCE": - raise FramingError() frame = Frame(flags, type, track, channel, payload) frm.debug("RECV %s", frame) return frame -- cgit v1.2.1 From 5fe0458b26fdf5ec233d8181201d3673a15006ae Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 1 May 2008 10:15:35 +0000 Subject: QPID-966: applied patch from rajith; altered to use uuid as session name; updated verify scripts for automated testing; re-enabled automated testing in c++ build git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@652469 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 31 +++++----- qpid/python/examples/direct/direct_consumer.py | 32 ++++++---- qpid/python/examples/direct/direct_producer.py | 34 ++++++----- qpid/python/examples/direct/listener.py | 31 ++++++---- qpid/python/examples/fanout/fanout_consumer.py | 43 ++++++++------ qpid/python/examples/fanout/fanout_producer.py | 32 +++++----- qpid/python/examples/fanout/verify | 2 +- qpid/python/examples/fanout/verify.in | 4 +- qpid/python/examples/pubsub/topic_publisher.py | 59 ++++++++----------- qpid/python/examples/pubsub/topic_subscriber.py | 78 +++++++++++++------------ qpid/python/examples/pubsub/verify | 2 +- qpid/python/examples/pubsub/verify.in | 2 +- qpid/python/examples/request-response/client.py | 61 ++++++++++--------- qpid/python/examples/request-response/server.py | 57 ++++++++++-------- qpid/python/examples/request-response/verify | 2 +- qpid/python/examples/request-response/verify.in | 4 +- 16 files changed, 252 insertions(+), 222 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index f39f0c3349..7041ce2f24 100755 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -10,8 +10,10 @@ import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection from qpid.queue import Empty #----- Initialization ----------------------------------- @@ -20,17 +22,20 @@ from qpid.queue import Empty host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session.session_open() +session = conn.session(str(randint(1,64*1024))) #----- Create a queue ------------------------------------- @@ -38,15 +43,13 @@ session.session_open() # on the broker. Published messages are sent to the AMQP queue, # from which messages are delivered to consumers. # -# queue_bind() determines which messages are routed to a queue. -# Route all messages with the routing key "routing_key" to +# exchange_bind() determines which messages are routed to a queue. +# Route all messages with the binding key "routing_key" to # the AMQP queue named "message_queue". session.queue_declare(queue="message_queue") -session.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="routing_key") +session.exchange_bind(exchange="amq.direct", queue="message_queue", binding_key="routing_key") #----- Cleanup --------------------------------------------- -session.session_close() - - +session.close(timeout=10) diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index 85c1db0a93..91d85cee1a 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -8,8 +8,11 @@ import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet from qpid.queue import Empty @@ -19,17 +22,20 @@ from qpid.queue import Empty host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session.session_open() +session = conn.session(str(randint(1,64*1024))) #----- Read from queue -------------------------------------------- @@ -39,7 +45,7 @@ session.session_open() # The consumer tag identifies the client-side queue. consumer_tag = "consumer1" -queue = client.queue(consumer_tag) +queue = session.incoming(consumer_tag) # Call message_consume() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will @@ -57,7 +63,8 @@ content = "" # Content of the last message read message = None while content != final: message = queue.get(timeout=10) - content = message.content.body + content = message.body + session.message_accept(RangedSet(message.id)) print content # Messages are not removed from the queue until they are @@ -66,11 +73,12 @@ while content != final: # acknowledged. This is more efficient, because there are fewer # network round-trips. -message.complete(cumulative=True) +#message.complete(cumulative=True) +# ? Is there an equivakent to the above in the new API ? #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. # -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index 2c07bfd8e7..7c4e30d96e 100755 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -8,8 +8,11 @@ import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message from qpid.queue import Empty #----- Initialization ----------------------------------- @@ -18,34 +21,33 @@ from qpid.queue import Empty host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session.session_open() +session = conn.session(str(randint(1,64*1024))) #----- Publish some messages ------------------------------ # Create some messages and put them on the broker. +props = session.delivery_properties(routing_key="routing_key") for i in range(10): - message = Content("message " + str(i)) - message["routing_key"] = "routing_key" - session.message_transfer(destination="amq.direct", content=message) + session.message_transfer("amq.direct",None, None, Message(props,"message " + str(i))) -final="That's all, folks!" -message = Content(final) -message["routing_key"] = "routing_key" -session.message_transfer(destination="amq.direct", content=message) +session.message_transfer("amq.direct",None,None, Message(props,"That's all, folks!")) #----- Cleanup -------------------------------------------- # Clean up before exiting so there are no open threads. -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index 2dbd502fa0..aa60b1c501 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -9,8 +9,11 @@ import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet from qpid.queue import Empty from time import sleep @@ -24,7 +27,8 @@ class Receiver: return self.finalReceived def Handler (self, message): - content = message.content.body + content = message.body + session.message_accept(RangedSet(message.id)) print content if content == "That's all, folks!": self.finalReceived = True @@ -34,7 +38,7 @@ class Receiver: # up to and including the one identified by the delivery tag are # acknowledged. This is more efficient, because there are fewer # network round-trips. - message.complete(cumulative=True) + #message.complete(cumulative=True) #----- Initialization -------------------------------------- @@ -43,17 +47,20 @@ class Receiver: host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session.session_open() +session = conn.session(str(randint(1,64*1024))) #----- Read from queue -------------------------------------------- @@ -63,7 +70,7 @@ session.session_open() # The consumer tag identifies the client-side queue. consumer_tag = "consumer1" -queue = client.queue(consumer_tag) +queue = session.incoming(consumer_tag) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will @@ -85,4 +92,4 @@ while not receiver.isFinal (): # Clean up before exiting so there are no open threads. # -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index ef24bf35b2..b82d8045ff 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -5,21 +5,22 @@ This AMQP client reads messages from a message queue named "message_queue". """ -import base64 import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty #----- Functions ------------------------------------------- -def dump_queue(client, queue_name): +def dump_queue(session, queue_name): print "Messages queue: " + queue_name consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = client.queue(consumer_tag) + queue = session.incoming(queue_name) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to a local client queue. The broker will @@ -37,7 +38,8 @@ def dump_queue(client, queue_name): while True: try: message = queue.get(timeout=10) - content = message.content.body + content = message.body + session.message_accept(RangedSet(message.id)) print "Response: " + content except Empty: print "No more messages!" @@ -53,8 +55,8 @@ def dump_queue(client, queue_name): # by the delivery tag are acknowledged. This is more efficient, # because there are fewer network round-trips. - if message != 0: - message.complete(cumulative=True) + #if message != 0: + # message.complete(cumulative=True) #----- Initialization -------------------------------------- @@ -63,36 +65,39 @@ def dump_queue(client, queue_name): host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session_info = session.session_open() -session_id = session_info.session_id +session_id = str(uuid4()) +session = conn.session(session_id) #----- Main Body -- ---------------------------------------- # Make a unique queue name for my queue from the session ID. -my_queue = base64.urlsafe_b64encode(session_id) +my_queue = session_id session.queue_declare(queue=my_queue) # Bind my queue to the fanout exchange. No routing key is required # the fanout exchange copies messages unconditionally to every # bound queue -session.queue_bind(queue=my_queue, exchange="amq.fanout") +session.exchange_bind(queue=my_queue, exchange="amq.fanout") # Dump the messages on the queue. -dump_queue(client, my_queue) +dump_queue(session, my_queue) #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. # -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 9864c776c1..1b5ea6995e 100755 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -5,11 +5,13 @@ Publishes messages to an AMQP direct exchange, using the routing key "routing_key" """ - import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message from qpid.queue import Empty #----- Initialization ----------------------------------- @@ -18,32 +20,32 @@ from qpid.queue import Empty host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session.session_open() +session = conn.session(str(randint(1,64*1024))) #----- Publish some messages ------------------------------ # Create some messages and put them on the broker. for i in range(10): - message = Content(body="message " + str(i)) - session.message_transfer(destination="amq.fanout", content=message) + session.message_transfer("amq.fanout", None, None ,Message("message " + str(i))) -final="That's all, folks!" -message=Content(final) -session.message_transfer(destination="amq.fanout", content=message) +session.message_transfer("amq.fanout", None, None, Message("That's all, folks!")) #----- Cleanup -------------------------------------------- # Clean up before exiting so there are no open threads. -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/fanout/verify b/qpid/python/examples/fanout/verify index 7650853e11..6a3132a94f 100644 --- a/qpid/python/examples/fanout/verify +++ b/qpid/python/examples/fanout/verify @@ -2,4 +2,4 @@ background "Subscribed" ./fanout_consumer.py background "Subscribed" ./fanout_consumer.py clients ./fanout_producer.py -outputs ./fanout_producer.py.out "./fanout_consumer.py.out | remove_uuid64" "./fanout_consumer.pyX.out | remove_uuid64" +outputs ./fanout_producer.py.out "./fanout_consumer.py.out | remove_uuid" "./fanout_consumer.pyX.out | remove_uuid" diff --git a/qpid/python/examples/fanout/verify.in b/qpid/python/examples/fanout/verify.in index d5067b3850..30dfeb9e69 100644 --- a/qpid/python/examples/fanout/verify.in +++ b/qpid/python/examples/fanout/verify.in @@ -1,5 +1,5 @@ ==== fanout_producer.py.out -==== fanout_consumer.py.out | remove_uuid64 +==== fanout_consumer.py.out | remove_uuid Messages queue: Subscribed to queue Response: message 0 @@ -14,7 +14,7 @@ Response: message 8 Response: message 9 Response: That's all, folks! No more messages! -==== fanout_consumer.pyX.out | remove_uuid64 +==== fanout_consumer.pyX.out | remove_uuid Messages queue: Subscribed to queue Response: message 0 diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py index e302d58ad4..b79896eaf6 100755 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -9,8 +9,11 @@ import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message from qpid.queue import Empty #----- Initialization ----------------------------------- @@ -18,18 +21,20 @@ from qpid.queue import Empty # Set parameters for login. host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session.session_open() +session = conn.session(str(randint(1,64*1024))) #----- Publish some messages ------------------------------ @@ -37,44 +42,30 @@ session.session_open() # topic exchange. The routing keys are "usa.news", "usa.weather", # "europe.news", and "europe.weather". +def send_msg(routing_key): + props = session.delivery_properties(routing_key=routing_key) + for i in range(5): + session.message_transfer("amq.topic", None, None, Message(props,"message " + str(i))) # usa.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.news" - session.message_transfer(destination="amq.topic", content=message) +send_msg("usa.news") # usa.weather - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "usa.weather" - session.message_transfer(destination="amq.topic", content=message) +send_msg("usa.weather") # europe.news - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.news" - session.message_transfer(destination="amq.topic", content=message) +send_msg("europe.news") # europe.weather - -for i in range(5): - message = Content("message " + str(i)) - message["routing_key"] = "europe.weather" - session.message_transfer(destination="amq.topic", content=message) +send_msg("europe.weather") # Signal termination - -message = Content("That's all, folks!") -message["routing_key"] = "control" -session.message_transfer(destination="amq.topic", content=message) +props = session.delivery_properties(routing_key="control") +session.message_transfer("amq.topic",None, None, Message(props,"That's all, folks!")) #----- Cleanup -------------------------------------------- # Clean up before exiting so there are no open threads. -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index a5c05ba177..6908be5471 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -3,24 +3,25 @@ topic_subscriber.py This subscriber creates private queues and binds them - to the topics "usa.#", "europe.#", "#.news", and "#.weather". + to the topics 'usa.#', 'europe.#', '#.news', and '#.weather'. """ -import base64 -import sys import qpid -from qpid.client import Client -from qpid.content import Content +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty #----- Functions ------------------------------------------- -def dump_queue(client, queue_name): +def dump_queue(queue_name): print "Messages queue: " + queue_name consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = client.queue(consumer_tag) + queue = session.incoming(consumer_tag) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to a local client queue. The broker will @@ -37,11 +38,12 @@ def dump_queue(client, queue_name): while content != final: try: message = queue.get() - content = message.content.body + content = message.body + session.message_accept(RangedSet(message.id)) print content except Empty: - if message != 0: - message.complete(cumulative=True) + #if message != 0: + # message.complete(cumulative=True) print "No more messages!" return @@ -52,8 +54,8 @@ def dump_queue(client, queue_name): # by the delivery tag are acknowledged. This is more efficient, # because there are fewer network round-trips. - if message != 0: - message.complete(cumulative=True) + #if message != 0: + # message.complete(cumulative=True) #----- Initialization -------------------------------------- @@ -62,27 +64,29 @@ def dump_queue(client, queue_name): host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session_info = session.session_open() -session_id = session_info.session_id +session_id = str(uuid4()) +session = conn.session(session_id) #----- Main Body -- ---------------------------------------- -news = "news" + base64.urlsafe_b64encode(session_id) -weather = "weather" + base64.urlsafe_b64encode(session_id) -usa = "usa" + base64.urlsafe_b64encode(session_id) -europe = "europe" + base64.urlsafe_b64encode(session_id) +news = "news" + session_id +weather = "weather" + session_id +usa = "usa" + session_id +europe = "europe" + session_id session.queue_declare(queue=news, exclusive=True) session.queue_declare(queue=weather, exclusive=True) @@ -94,17 +98,17 @@ session.queue_declare(queue=europe, exclusive=True) # The '#' symbol matches one component of a multipart name, e.g. "#.news" matches # "europe.news" or "usa.news". -session.queue_bind(exchange="amq.topic", queue=news, routing_key="#.news") -session.queue_bind(exchange="amq.topic", queue=weather, routing_key="#.weather") -session.queue_bind(exchange="amq.topic", queue=usa, routing_key="usa.#") -session.queue_bind(exchange="amq.topic", queue=europe, routing_key="europe.#") +session.exchange_bind(exchange="amq.topic", queue=news, binding_key="#.news") +session.exchange_bind(exchange="amq.topic", queue=weather, binding_key="#.weather") +session.exchange_bind(exchange="amq.topic", queue=usa, binding_key="usa.#") +session.exchange_bind(exchange="amq.topic", queue=europe, binding_key="europe.#") # Bind each queue to the control queue so we know when to stop -session.queue_bind(exchange="amq.topic", queue=news, routing_key="control") -session.queue_bind(exchange="amq.topic", queue=weather, routing_key="control") -session.queue_bind(exchange="amq.topic", queue=usa, routing_key="control") -session.queue_bind(exchange="amq.topic", queue=europe, routing_key="control") +session.exchange_bind(exchange="amq.topic", queue=news, binding_key="control") +session.exchange_bind(exchange="amq.topic", queue=weather, binding_key="control") +session.exchange_bind(exchange="amq.topic", queue=usa, binding_key="control") +session.exchange_bind(exchange="amq.topic", queue=europe, binding_key="control") # Remind the user to start the topic producer @@ -113,13 +117,13 @@ sys.stdout.flush() # Call dump_queue to print messages from each queue -dump_queue(client, news) -dump_queue(client, weather) -dump_queue(client, usa) -dump_queue(client, europe) +dump_queue(news) +dump_queue(weather) +dump_queue(usa) +dump_queue(europe) #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/pubsub/verify b/qpid/python/examples/pubsub/verify index bef233b4ff..963d2e32e1 100644 --- a/qpid/python/examples/pubsub/verify +++ b/qpid/python/examples/pubsub/verify @@ -1,4 +1,4 @@ # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify background "Queues created" ./topic_subscriber.py clients ./topic_publisher.py -outputs ./topic_publisher.py.out "topic_subscriber.py.out | remove_uuid64 | sort" +outputs ./topic_publisher.py.out "topic_subscriber.py.out | remove_uuid | sort" diff --git a/qpid/python/examples/pubsub/verify.in b/qpid/python/examples/pubsub/verify.in index 69de08d17c..2f6da09ec5 100644 --- a/qpid/python/examples/pubsub/verify.in +++ b/qpid/python/examples/pubsub/verify.in @@ -1,5 +1,5 @@ ==== topic_publisher.py.out -==== topic_subscriber.py.out | remove_uuid64 | sort +==== topic_subscriber.py.out | remove_uuid | sort message 0 message 0 message 0 diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 6561bb6fee..8f7d430d1b 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -6,22 +6,22 @@ """ -import base64 - import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty #----- Functions ------------------------------------------- -def dump_queue(client, queue_name): +def dump_queue(queue_name): print "Messages queue: " + queue_name consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = client.queue(consumer_tag) + queue = session.incoming(consumer_tag) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to a local client queue. The broker will @@ -36,7 +36,8 @@ def dump_queue(client, queue_name): while True: try: message = queue.get(timeout=10) - content = message.content.body + content = message.body + session.message_accept(RangedSet(message.id)) print "Response: " + content except Empty: print "No more messages!" @@ -52,8 +53,8 @@ def dump_queue(client, queue_name): # by the delivery tag are acknowledged. This is more efficient, # because there are fewer network round-trips. - if message != 0: - message.complete(cumulative=True) + #if message != 0: + # message.complete(cumulative=True) #----- Initialization -------------------------------------- @@ -62,21 +63,21 @@ def dump_queue(client, queue_name): host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" +amqp_spec="" -# Create a client and log in to it. - -spec = qpid.spec.load(amqp_spec) -client = Client(host, port, spec) -client.start({"LOGIN": user, "PASSWORD": password}) +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" -# Open the session. Save the session id. +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() -session = client.session() -session_info = session.session_open() -session_id = session_info.session_id +session_id = str(uuid4()) +session = conn.session(session_id) #----- Main Body -- ---------------------------------------- @@ -84,9 +85,9 @@ session_id = session_info.session_id # same string as the name of the queue and the name of the routing # key. -replyTo = "ReplyTo:" + base64.urlsafe_b64encode(session_id) +replyTo = "ReplyTo:" + session_id session.queue_declare(queue=replyTo, exclusive=True) -session.queue_bind(exchange="amq.direct", queue=replyTo, routing_key=replyTo) +session.exchange_bind(exchange="amq.direct", queue=replyTo, binding_key=replyTo) # Send some messages to the server's request queue @@ -95,22 +96,20 @@ lines = ["Twas brilling, and the slithy toves", "All mimsy were the borogroves,", "And the mome raths outgrabe."] -for l in lines: - print "Request: " + l - request=Content(l) - request["routing_key"] = "request" - request["reply_to"] = client.spec.struct("reply_to") - request["reply_to"]["exchange_name"] = "amq.direct" - request["reply_to"]["routing_key"] = replyTo - session.message_transfer(destination="amq.direct", content=request) +for ln in lines: + print "Request: " + ln + mp = session.message_properties() + mp.reply_to = session.reply_to("amq.direct", replyTo) + dp = session.delivery_properties(routing_key="request") + session.message_transfer("amq.direct", None, None, Message(mp,dp,ln)) # Now see what messages the server sent to our replyTo queue -dump_queue(client, replyTo) +dump_queue(replyTo) #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 04b147d003..71c3161495 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -4,52 +4,61 @@ Server for a client/server example """ - import qpid import sys -from qpid.client import Client -from qpid.content import Content +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty #----- Functions ------------------------------------------- +def getProperty(msg, name): + for h in msg.headers: + if hasattr(h, name): return getattr(h, name) + return None def respond(session, request): # The routing key for the response is the request's reply-to # property. The body for the response is the request's body, # converted to upper case. - - response=Content(request.body.upper()) - response["routing_key"] = request["reply_to"]["routing_key"] - session.message_transfer(destination=request["reply_to"]["exchange_name"], content=response) + reply_to = getProperty(request,"reply_to") + if reply_to == None: + raise Exception("reply to property needs to be there") + + props = session.delivery_properties(routing_key=reply_to["routing_key"]) + session.message_transfer(reply_to["exchange"],None, None, Message(props,request.body.upper())) #----- Initialization -------------------------------------- # Set parameters for login - host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" user="guest" password="guest" - -# Create a client and log in to it. - -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) - -# Create a session and open it. - -session = client.session() -session.session_open() +amqp_spec="" + +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# Create a connection. +conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) +conn.start() + +session_id = str(uuid4()) +session = conn.session(session_id) #----- Main Body -- ---------------------------------------- # Create a request queue and subscribe to it session.queue_declare(queue="request", exclusive=True) -session.queue_bind(exchange="amq.direct", queue="request", routing_key="request") +session.exchange_bind(exchange="amq.direct", queue="request", binding_key="request") dest = "request_destination" @@ -66,7 +75,7 @@ sys.stdout.flush() # Respond to each request -queue = client.queue(dest) +queue = session.incoming(dest) # If we get a message, send it back to the user (as indicated in the # ReplyTo property) @@ -74,8 +83,8 @@ queue = client.queue(dest) while True: try: request = queue.get(timeout=100) - respond(session, request.content) - request.complete() + respond(session, request) + session.message_accept(RangedSet(request.id)) except Empty: print "No more messages!" break; @@ -85,4 +94,4 @@ while True: # Clean up before exiting so there are no open threads. -session.session_close() +session.close(timeout=10) diff --git a/qpid/python/examples/request-response/verify b/qpid/python/examples/request-response/verify index 2a2d479077..cf8151d4e4 100644 --- a/qpid/python/examples/request-response/verify +++ b/qpid/python/examples/request-response/verify @@ -2,4 +2,4 @@ background "Request server running" ./server.py clients ./client.py kill %% # Must kill the server. -outputs "./client.py.out | remove_uuid64" " server.py.out | remove_uuid64" +outputs "./client.py.out | remove_uuid" " server.py.out | remove_uuid" diff --git a/qpid/python/examples/request-response/verify.in b/qpid/python/examples/request-response/verify.in index f681253b3c..8d7f732ec8 100644 --- a/qpid/python/examples/request-response/verify.in +++ b/qpid/python/examples/request-response/verify.in @@ -1,4 +1,4 @@ -==== client.py.out | remove_uuid64 +==== client.py.out | remove_uuid Request: Twas brilling, and the slithy toves Request: Did gyre and gimble in the wabe. Request: All mimsy were the borogroves, @@ -9,6 +9,6 @@ Response: DID GYRE AND GIMBLE IN THE WABE. Response: ALL MIMSY WERE THE BOROGROVES, Response: AND THE MOME RATHS OUTGRABE. No more messages! -==== server.py.out | remove_uuid64 +==== server.py.out | remove_uuid Request server running - run your client now. (Times out after 100 seconds ...) -- cgit v1.2.1 From 657c6071b11a0319f78dcb96ef2978733c93c1a2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 1 May 2008 13:10:58 +0000 Subject: Remove preview tests (no longer required) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@652506 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10_preview/__init__.py | 33 - .../tests_0-10_preview/alternate_exchange.py | 179 ----- qpid/python/tests_0-10_preview/broker.py | 111 --- qpid/python/tests_0-10_preview/dtx.py | 645 ---------------- qpid/python/tests_0-10_preview/example.py | 95 --- qpid/python/tests_0-10_preview/exchange.py | 335 --------- qpid/python/tests_0-10_preview/execution.py | 29 - qpid/python/tests_0-10_preview/message.py | 834 --------------------- qpid/python/tests_0-10_preview/persistence.py | 67 -- qpid/python/tests_0-10_preview/query.py | 227 ------ qpid/python/tests_0-10_preview/queue.py | 338 --------- qpid/python/tests_0-10_preview/testlib.py | 66 -- qpid/python/tests_0-10_preview/tx.py | 231 ------ 13 files changed, 3190 deletions(-) delete mode 100644 qpid/python/tests_0-10_preview/__init__.py delete mode 100644 qpid/python/tests_0-10_preview/alternate_exchange.py delete mode 100644 qpid/python/tests_0-10_preview/broker.py delete mode 100644 qpid/python/tests_0-10_preview/dtx.py delete mode 100644 qpid/python/tests_0-10_preview/example.py delete mode 100644 qpid/python/tests_0-10_preview/exchange.py delete mode 100644 qpid/python/tests_0-10_preview/execution.py delete mode 100644 qpid/python/tests_0-10_preview/message.py delete mode 100644 qpid/python/tests_0-10_preview/persistence.py delete mode 100644 qpid/python/tests_0-10_preview/query.py delete mode 100644 qpid/python/tests_0-10_preview/queue.py delete mode 100644 qpid/python/tests_0-10_preview/testlib.py delete mode 100644 qpid/python/tests_0-10_preview/tx.py (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10_preview/__init__.py b/qpid/python/tests_0-10_preview/__init__.py deleted file mode 100644 index f0acf9c632..0000000000 --- a/qpid/python/tests_0-10_preview/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Do not delete - marks this directory as a python package. - -# -# 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. -# - -from alternate_exchange import * -from broker import * -from dtx import * -from example import * -from exchange import * -from execution import * -from management import * -from message import * -from query import * -from queue import * -from testlib import * -from tx import * diff --git a/qpid/python/tests_0-10_preview/alternate_exchange.py b/qpid/python/tests_0-10_preview/alternate_exchange.py deleted file mode 100644 index 83f8d85811..0000000000 --- a/qpid/python/tests_0-10_preview/alternate_exchange.py +++ /dev/null @@ -1,179 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class AlternateExchangeTests(TestBase): - """ - Tests for the new mechanism for message returns introduced in 0-10 - and available in 0-9 for preview - """ - - def test_unroutable(self): - """ - Test that unroutable messages are delivered to the alternate-exchange if specified - """ - channel = self.channel - #create an exchange with an alternate defined - channel.exchange_declare(exchange="secondary", type="fanout") - channel.exchange_declare(exchange="primary", type="direct", alternate_exchange="secondary") - - #declare, bind (to the alternate exchange) and consume from a queue for 'returned' messages - channel.queue_declare(queue="returns", exclusive=True, auto_delete=True) - channel.queue_bind(queue="returns", exchange="secondary") - self.subscribe(destination="a", queue="returns") - returned = self.client.queue("a") - - #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages - channel.queue_declare(queue="processed", exclusive=True, auto_delete=True) - channel.queue_bind(queue="processed", exchange="primary", routing_key="my-key") - self.subscribe(destination="b", queue="processed") - processed = self.client.queue("b") - - #publish to the primary exchange - #...one message that makes it to the 'processed' queue: - channel.message_transfer(destination="primary", content=Content("Good", properties={'routing_key':"my-key"})) - #...and one that does not: - channel.message_transfer(destination="primary", content=Content("Bad", properties={'routing_key':"unused-key"})) - - #delete the exchanges - channel.exchange_delete(exchange="primary") - channel.exchange_delete(exchange="secondary") - - #verify behaviour - self.assertEqual("Good", processed.get(timeout=1).content.body) - self.assertEqual("Bad", returned.get(timeout=1).content.body) - self.assertEmpty(processed) - self.assertEmpty(returned) - - def test_queue_delete(self): - """ - Test that messages in a queue being deleted are delivered to the alternate-exchange if specified - """ - channel = self.channel - #set up a 'dead letter queue': - channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="deleted", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="dlq", queue="deleted") - self.subscribe(destination="dlq", queue="deleted") - dlq = self.client.queue("dlq") - - #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="delete-me", alternate_exchange="dlq") - #send it some messages: - channel.message_transfer(content=Content("One", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("Two", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("Three", properties={'routing_key':"delete-me"})) - #delete it: - channel.queue_delete(queue="delete-me") - #delete the dlq exchange: - channel.exchange_delete(exchange="dlq") - - #check the messages were delivered to the dlq: - self.assertEqual("One", dlq.get(timeout=1).content.body) - self.assertEqual("Two", dlq.get(timeout=1).content.body) - self.assertEqual("Three", dlq.get(timeout=1).content.body) - self.assertEmpty(dlq) - - - def test_immediate(self): - """ - Test that messages in a queue being deleted are delivered to the alternate-exchange if specified - """ - channel = self.channel - #set up a 'dead letter queue': - channel.exchange_declare(exchange="dlq", type="fanout") - channel.queue_declare(queue="immediate", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="dlq", queue="immediate") - self.subscribe(destination="dlq", queue="immediate") - dlq = self.client.queue("dlq") - - #create a queue using the dlq as its alternate exchange: - channel.queue_declare(queue="no-consumers", alternate_exchange="dlq", exclusive=True, auto_delete=True) - #send it some messages: - #TODO: WE HAVE LOST THE IMMEDIATE FLAG; FIX THIS ONCE ITS BACK - channel.message_transfer(content=Content("no one wants me", properties={'routing_key':"no-consumers"})) - - #check the messages were delivered to the dlq: - self.assertEqual("no one wants me", dlq.get(timeout=1).content.body) - self.assertEmpty(dlq) - - #cleanup: - channel.queue_delete(queue="no-consumers") - channel.exchange_delete(exchange="dlq") - - - def test_delete_while_used_by_queue(self): - """ - Ensure an exchange still in use as an alternate-exchange for a - queue can't be deleted - """ - channel = self.channel - channel.exchange_declare(exchange="alternate", type="fanout") - channel.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") - try: - channel.exchange_delete(exchange="alternate") - self.fail("Expected deletion of in-use alternate-exchange to fail") - except Closed, e: - #cleanup: - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="alternate") - channel.session_close() - other.close() - - self.assertConnectionException(530, e.args[0]) - - - - def test_delete_while_used_by_exchange(self): - """ - Ensure an exchange still in use as an alternate-exchange for - another exchange can't be deleted - """ - channel = self.channel - channel.exchange_declare(exchange="alternate", type="fanout") - channel.exchange_declare(exchange="e", type="fanout", alternate_exchange="alternate") - try: - channel.exchange_delete(exchange="alternate") - #cleanup: - channel.exchange_delete(exchange="e") - self.fail("Expected deletion of in-use alternate-exchange to fail") - except Closed, e: - #cleanup: - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="e") - channel.exchange_delete(exchange="alternate") - channel.session_close() - other.close() - - self.assertConnectionException(530, e.args[0]) - - - def assertEmpty(self, queue): - try: - msg = queue.get(timeout=1) - self.fail("Queue not empty: " + msg) - except Empty: None - diff --git a/qpid/python/tests_0-10_preview/broker.py b/qpid/python/tests_0-10_preview/broker.py deleted file mode 100644 index 99936ba742..0000000000 --- a/qpid/python/tests_0-10_preview/broker.py +++ /dev/null @@ -1,111 +0,0 @@ -# -# 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. -# -from qpid.client import Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class BrokerTests(TestBase): - """Tests for basic Broker functionality""" - - def test_ack_and_no_ack(self): - """ - First, this test tries to receive a message with a no-ack - consumer. Second, this test tries to explicitly receive and - acknowledge a message with an acknowledging consumer. - """ - ch = self.channel - self.queue_declare(ch, queue = "myqueue") - - # No ack consumer - ctag = "tag1" - self.subscribe(ch, queue = "myqueue", destination = ctag) - body = "test no-ack" - ch.message_transfer(content = Content(body, properties = {"routing_key" : "myqueue"})) - msg = self.client.queue(ctag).get(timeout = 5) - self.assert_(msg.content.body == body) - - # Acknowledging consumer - self.queue_declare(ch, queue = "otherqueue") - ctag = "tag2" - self.subscribe(ch, queue = "otherqueue", destination = ctag, confirm_mode = 1) - ch.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) - ch.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) - body = "test ack" - ch.message_transfer(content = Content(body, properties = {"routing_key" : "otherqueue"})) - msg = self.client.queue(ctag).get(timeout = 5) - msg.complete() - self.assert_(msg.content.body == body) - - def test_simple_delivery_immediate(self): - """ - Test simple message delivery where consume is issued before publish - """ - channel = self.channel - self.exchange_declare(channel, exchange="test-exchange", type="direct") - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - consumer_tag = "tag1" - self.subscribe(queue="test-queue", destination=consumer_tag) - queue = self.client.queue(consumer_tag) - - body = "Immediate Delivery" - channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) - msg = queue.get(timeout=5) - self.assert_(msg.content.body == body) - - # TODO: Ensure we fail if immediate=True and there's no consumer. - - - def test_simple_delivery_queued(self): - """ - Test basic message delivery where publish is issued before consume - (i.e. requires queueing of the message) - """ - channel = self.channel - self.exchange_declare(channel, exchange="test-exchange", type="direct") - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - body = "Queued Delivery" - channel.message_transfer(destination="test-exchange", content = Content(body, properties = {"routing_key" : "key"})) - - consumer_tag = "tag1" - self.subscribe(queue="test-queue", destination=consumer_tag) - queue = self.client.queue(consumer_tag) - msg = queue.get(timeout=5) - self.assert_(msg.content.body == body) - - def test_invalid_channel(self): - channel = self.client.channel(200) - try: - channel.queue_declare(exclusive=True) - self.fail("Expected error on queue_declare for invalid channel") - except Closed, e: - self.assertConnectionException(504, e.args[0]) - - def test_closed_channel(self): - channel = self.client.channel(200) - channel.session_open() - channel.session_close() - try: - channel.queue_declare(exclusive=True) - self.fail("Expected error on queue_declare for closed channel") - except Closed, e: - if isinstance(e.args[0], str): self.fail(e) - self.assertConnectionException(504, e.args[0]) diff --git a/qpid/python/tests_0-10_preview/dtx.py b/qpid/python/tests_0-10_preview/dtx.py deleted file mode 100644 index f84f91c75a..0000000000 --- a/qpid/python/tests_0-10_preview/dtx.py +++ /dev/null @@ -1,645 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from struct import pack, unpack -from time import sleep - -class DtxTests(TestBase): - """ - Tests for the amqp dtx related classes. - - Tests of the form test_simple_xxx test the basic transactional - behaviour. The approach here is to 'swap' a message from one queue - to another by consuming and re-publishing in the same - transaction. That transaction is then completed in different ways - and the appropriate result verified. - - The other tests enforce more specific rules and behaviour on a - per-method or per-field basis. - """ - - XA_RBROLLBACK = 1 - XA_RBTIMEOUT = 2 - XA_OK = 0 - tx_counter = 0 - - def reset_channel(self): - self.channel.session_close() - self.channel = self.client.channel(self.channel.id + 1) - self.channel.session_open() - - def test_simple_commit(self): - """ - Test basic one-phase commit behaviour. - """ - channel = self.channel - tx = self.xid("my-xid") - self.txswap(tx, "commit") - - #neither queue should have any messages accessible - self.assertMessageCount(0, "queue-a") - self.assertMessageCount(0, "queue-b") - - #commit - self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).status) - - #should close and reopen channel to ensure no unacked messages are held - self.reset_channel() - - #check result - self.assertMessageCount(0, "queue-a") - self.assertMessageCount(1, "queue-b") - self.assertMessageId("commit", "queue-b") - - def test_simple_prepare_commit(self): - """ - Test basic two-phase commit behaviour. - """ - channel = self.channel - tx = self.xid("my-xid") - self.txswap(tx, "prepare-commit") - - #prepare - self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) - - #neither queue should have any messages accessible - self.assertMessageCount(0, "queue-a") - self.assertMessageCount(0, "queue-b") - - #commit - self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).status) - - self.reset_channel() - - #check result - self.assertMessageCount(0, "queue-a") - self.assertMessageCount(1, "queue-b") - self.assertMessageId("prepare-commit", "queue-b") - - - def test_simple_rollback(self): - """ - Test basic rollback behaviour. - """ - channel = self.channel - tx = self.xid("my-xid") - self.txswap(tx, "rollback") - - #neither queue should have any messages accessible - self.assertMessageCount(0, "queue-a") - self.assertMessageCount(0, "queue-b") - - #rollback - self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) - - self.reset_channel() - - #check result - self.assertMessageCount(1, "queue-a") - self.assertMessageCount(0, "queue-b") - self.assertMessageId("rollback", "queue-a") - - def test_simple_prepare_rollback(self): - """ - Test basic rollback behaviour after the transaction has been prepared. - """ - channel = self.channel - tx = self.xid("my-xid") - self.txswap(tx, "prepare-rollback") - - #prepare - self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).status) - - #neither queue should have any messages accessible - self.assertMessageCount(0, "queue-a") - self.assertMessageCount(0, "queue-b") - - #rollback - self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) - - self.reset_channel() - - #check result - self.assertMessageCount(1, "queue-a") - self.assertMessageCount(0, "queue-b") - self.assertMessageId("prepare-rollback", "queue-a") - - def test_select_required(self): - """ - check that an error is flagged if select is not issued before - start or end - """ - channel = self.channel - tx = self.xid("dummy") - try: - channel.dtx_demarcation_start(xid=tx) - - #if we get here we have failed, but need to do some cleanup: - channel.dtx_demarcation_end(xid=tx) - channel.dtx_coordination_rollback(xid=tx) - self.fail("Channel not selected for use with dtx, expected exception!") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_start_already_known(self): - """ - Verify that an attempt to start an association with a - transaction that is already known is not allowed (unless the - join flag is set). - """ - #create two channels on different connection & select them for use with dtx: - channel1 = self.channel - channel1.dtx_demarcation_select() - - other = self.connect() - channel2 = other.channel(1) - channel2.session_open() - channel2.dtx_demarcation_select() - - #create a xid - tx = self.xid("dummy") - #start work on one channel under that xid: - channel1.dtx_demarcation_start(xid=tx) - #then start on the other without the join set - failed = False - try: - channel2.dtx_demarcation_start(xid=tx) - except Closed, e: - failed = True - error = e - - #cleanup: - if not failed: - channel2.dtx_demarcation_end(xid=tx) - other.close() - channel1.dtx_demarcation_end(xid=tx) - channel1.dtx_coordination_rollback(xid=tx) - - #verification: - if failed: self.assertConnectionException(503, e.args[0]) - else: self.fail("Xid already known, expected exception!") - - def test_forget_xid_on_completion(self): - """ - Verify that a xid is 'forgotten' - and can therefore be used - again - once it is completed. - """ - #do some transactional work & complete the transaction - self.test_simple_commit() - # channel has been reset, so reselect for use with dtx - self.channel.dtx_demarcation_select() - - #start association for the same xid as the previously completed txn - tx = self.xid("my-xid") - self.channel.dtx_demarcation_start(xid=tx) - self.channel.dtx_demarcation_end(xid=tx) - self.channel.dtx_coordination_rollback(xid=tx) - - def test_start_join_and_resume(self): - """ - Ensure the correct error is signalled when both the join and - resume flags are set on starting an association between a - channel and a transcation. - """ - channel = self.channel - channel.dtx_demarcation_select() - tx = self.xid("dummy") - try: - channel.dtx_demarcation_start(xid=tx, join=True, resume=True) - #failed, but need some cleanup: - channel.dtx_demarcation_end(xid=tx) - channel.dtx_coordination_rollback(xid=tx) - self.fail("Join and resume both set, expected exception!") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def test_start_join(self): - """ - Verify 'join' behaviour, where a channel is associated with a - transaction that is already associated with another channel. - """ - #create two channels & select them for use with dtx: - channel1 = self.channel - channel1.dtx_demarcation_select() - - channel2 = self.client.channel(2) - channel2.session_open() - channel2.dtx_demarcation_select() - - #setup - channel1.queue_declare(queue="one", exclusive=True, auto_delete=True) - channel1.queue_declare(queue="two", exclusive=True, auto_delete=True) - channel1.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) - channel1.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) - - #create a xid - tx = self.xid("dummy") - #start work on one channel under that xid: - channel1.dtx_demarcation_start(xid=tx) - #then start on the other with the join flag set - channel2.dtx_demarcation_start(xid=tx, join=True) - - #do work through each channel - self.swap(channel1, "one", "two")#swap 'a' from 'one' to 'two' - self.swap(channel2, "two", "one")#swap 'b' from 'two' to 'one' - - #mark end on both channels - channel1.dtx_demarcation_end(xid=tx) - channel2.dtx_demarcation_end(xid=tx) - - #commit and check - channel1.dtx_coordination_commit(xid=tx, one_phase=True) - self.assertMessageCount(1, "one") - self.assertMessageCount(1, "two") - self.assertMessageId("a", "two") - self.assertMessageId("b", "one") - - - def test_suspend_resume(self): - """ - Test suspension and resumption of an association - """ - channel = self.channel - channel.dtx_demarcation_select() - - #setup - channel.queue_declare(queue="one", exclusive=True, auto_delete=True) - channel.queue_declare(queue="two", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) - channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) - - tx = self.xid("dummy") - - channel.dtx_demarcation_start(xid=tx) - self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' - channel.dtx_demarcation_end(xid=tx, suspend=True) - - channel.dtx_demarcation_start(xid=tx, resume=True) - self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' - channel.dtx_demarcation_end(xid=tx) - - #commit and check - channel.dtx_coordination_commit(xid=tx, one_phase=True) - self.assertMessageCount(1, "one") - self.assertMessageCount(1, "two") - self.assertMessageId("a", "two") - self.assertMessageId("b", "one") - - def test_suspend_start_end_resume(self): - """ - Test suspension and resumption of an association with work - done on another transaction when the first transaction is - suspended - """ - channel = self.channel - channel.dtx_demarcation_select() - - #setup - channel.queue_declare(queue="one", exclusive=True, auto_delete=True) - channel.queue_declare(queue="two", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key':"one", 'message_id':"a"}, body="DtxMessage")) - channel.message_transfer(content=Content(properties={'routing_key':"two", 'message_id':"b"}, body="DtxMessage")) - - tx = self.xid("dummy") - - channel.dtx_demarcation_start(xid=tx) - self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two' - channel.dtx_demarcation_end(xid=tx, suspend=True) - - channel.dtx_demarcation_start(xid=tx, resume=True) - self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one' - channel.dtx_demarcation_end(xid=tx) - - #commit and check - channel.dtx_coordination_commit(xid=tx, one_phase=True) - self.assertMessageCount(1, "one") - self.assertMessageCount(1, "two") - self.assertMessageId("a", "two") - self.assertMessageId("b", "one") - - def test_end_suspend_and_fail(self): - """ - Verify that the correct error is signalled if the suspend and - fail flag are both set when disassociating a transaction from - the channel - """ - channel = self.channel - channel.dtx_demarcation_select() - tx = self.xid("suspend_and_fail") - channel.dtx_demarcation_start(xid=tx) - try: - channel.dtx_demarcation_end(xid=tx, suspend=True, fail=True) - self.fail("Suspend and fail both set, expected exception!") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - #cleanup - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.dtx_coordination_rollback(xid=tx) - channel.session_close() - other.close() - - - def test_end_unknown_xid(self): - """ - Verifies that the correct exception is thrown when an attempt - is made to end the association for a xid not previously - associated with the channel - """ - channel = self.channel - channel.dtx_demarcation_select() - tx = self.xid("unknown-xid") - try: - channel.dtx_demarcation_end(xid=tx) - self.fail("Attempted to end association with unknown xid, expected exception!") - except Closed, e: - #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming... - self.assertConnectionException(503, e.args[0]) - - def test_end(self): - """ - Verify that the association is terminated by end and subsequent - operations are non-transactional - """ - channel = self.client.channel(2) - channel.session_open() - channel.queue_declare(queue="tx-queue", exclusive=True, auto_delete=True) - - #publish a message under a transaction - channel.dtx_demarcation_select() - tx = self.xid("dummy") - channel.dtx_demarcation_start(xid=tx) - channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"one"}, body="DtxMessage")) - channel.dtx_demarcation_end(xid=tx) - - #now that association with txn is ended, publish another message - channel.message_transfer(content=Content(properties={'routing_key':"tx-queue", 'message_id':"two"}, body="DtxMessage")) - - #check the second message is available, but not the first - self.assertMessageCount(1, "tx-queue") - self.subscribe(channel, queue="tx-queue", destination="results", confirm_mode=1) - msg = self.client.queue("results").get(timeout=1) - self.assertEqual("two", msg.content['message_id']) - channel.message_cancel(destination="results") - #ack the message then close the channel - msg.complete() - channel.session_close() - - channel = self.channel - #commit the transaction and check that the first message (and - #only the first message) is then delivered - channel.dtx_coordination_commit(xid=tx, one_phase=True) - self.assertMessageCount(1, "tx-queue") - self.assertMessageId("one", "tx-queue") - - def test_invalid_commit_one_phase_true(self): - """ - Test that a commit with one_phase = True is rejected if the - transaction in question has already been prepared. - """ - other = self.connect() - tester = other.channel(1) - tester.session_open() - tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - tester.dtx_demarcation_select() - tx = self.xid("dummy") - tester.dtx_demarcation_start(xid=tx) - tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - tester.dtx_demarcation_end(xid=tx) - tester.dtx_coordination_prepare(xid=tx) - failed = False - try: - tester.dtx_coordination_commit(xid=tx, one_phase=True) - except Closed, e: - failed = True - error = e - - if failed: - self.channel.dtx_coordination_rollback(xid=tx) - self.assertConnectionException(503, e.args[0]) - else: - tester.session_close() - other.close() - self.fail("Invalid use of one_phase=True, expected exception!") - - def test_invalid_commit_one_phase_false(self): - """ - Test that a commit with one_phase = False is rejected if the - transaction in question has not yet been prepared. - """ - """ - Test that a commit with one_phase = True is rejected if the - transaction in question has already been prepared. - """ - other = self.connect() - tester = other.channel(1) - tester.session_open() - tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - tester.dtx_demarcation_select() - tx = self.xid("dummy") - tester.dtx_demarcation_start(xid=tx) - tester.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - tester.dtx_demarcation_end(xid=tx) - failed = False - try: - tester.dtx_coordination_commit(xid=tx, one_phase=False) - except Closed, e: - failed = True - error = e - - if failed: - self.channel.dtx_coordination_rollback(xid=tx) - self.assertConnectionException(503, e.args[0]) - else: - tester.session_close() - other.close() - self.fail("Invalid use of one_phase=False, expected exception!") - - def test_implicit_end(self): - """ - Test that an association is implicitly ended when the channel - is closed (whether by exception or explicit client request) - and the transaction in question is marked as rollback only. - """ - channel1 = self.channel - channel2 = self.client.channel(2) - channel2.session_open() - - #setup: - channel2.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - tx = self.xid("dummy") - - channel2.dtx_demarcation_select() - channel2.dtx_demarcation_start(xid=tx) - channel2.message_subscribe(queue="dummy", destination="dummy", confirm_mode=1) - channel2.message_flow(destination="dummy", unit=0, value=1) - channel2.message_flow(destination="dummy", unit=1, value=0xFFFFFFFF) - self.client.queue("dummy").get(timeout=1).complete() - channel2.message_cancel(destination="dummy") - channel2.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="whatever")) - channel2.session_close() - - self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).status) - channel1.dtx_coordination_rollback(xid=tx) - - def test_get_timeout(self): - """ - Check that get-timeout returns the correct value, (and that a - transaction with a timeout can complete normally) - """ - channel = self.channel - tx = self.xid("dummy") - - channel.dtx_demarcation_select() - channel.dtx_demarcation_start(xid=tx) - self.assertEqual(0, channel.dtx_coordination_get_timeout(xid=tx).timeout) - channel.dtx_coordination_set_timeout(xid=tx, timeout=60) - self.assertEqual(60, channel.dtx_coordination_get_timeout(xid=tx).timeout) - self.assertEqual(self.XA_OK, channel.dtx_demarcation_end(xid=tx).status) - self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).status) - - def test_set_timeout(self): - """ - Test the timeout of a transaction results in the expected - behaviour - """ - #open new channel to allow self.channel to be used in checking te queue - channel = self.client.channel(2) - channel.session_open() - #setup: - tx = self.xid("dummy") - channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':"timeout"}, body="DtxMessage")) - - channel.dtx_demarcation_select() - channel.dtx_demarcation_start(xid=tx) - self.swap(channel, "queue-a", "queue-b") - channel.dtx_coordination_set_timeout(xid=tx, timeout=2) - sleep(3) - #check that the work has been rolled back already - self.assertMessageCount(1, "queue-a") - self.assertMessageCount(0, "queue-b") - self.assertMessageId("timeout", "queue-a") - #check the correct codes are returned when we try to complete the txn - self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_demarcation_end(xid=tx).status) - self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_coordination_rollback(xid=tx).status) - - - - def test_recover(self): - """ - Test basic recover behaviour - """ - channel = self.channel - - channel.dtx_demarcation_select() - channel.queue_declare(queue="dummy", exclusive=True, auto_delete=True) - - prepared = [] - for i in range(1, 10): - tx = self.xid("tx%s" % (i)) - channel.dtx_demarcation_start(xid=tx) - channel.message_transfer(content=Content(properties={'routing_key':"dummy"}, body="message%s" % (i))) - channel.dtx_demarcation_end(xid=tx) - if i in [2, 5, 6, 8]: - channel.dtx_coordination_prepare(xid=tx) - prepared.append(tx) - else: - channel.dtx_coordination_rollback(xid=tx) - - xids = channel.dtx_coordination_recover().in_doubt - - #rollback the prepared transactions returned by recover - for x in xids: - channel.dtx_coordination_rollback(xid=x) - - #validate against the expected list of prepared transactions - actual = set(xids) - expected = set(prepared) - intersection = actual.intersection(expected) - - if intersection != expected: - missing = expected.difference(actual) - extra = actual.difference(expected) - for x in missing: - channel.dtx_coordination_rollback(xid=x) - self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) - - def test_bad_resume(self): - """ - Test that a resume on a session not selected for use with dtx fails - """ - channel = self.channel - try: - channel.dtx_demarcation_start(resume=True) - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def xid(self, txid): - DtxTests.tx_counter += 1 - branchqual = "v%s" % DtxTests.tx_counter - return pack('!LBB', 0, len(txid), len(branchqual)) + txid + branchqual - - def txswap(self, tx, id): - channel = self.channel - #declare two queues: - channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) - #put message with specified id on one queue: - channel.message_transfer(content=Content(properties={'routing_key':"queue-a", 'message_id':id}, body="DtxMessage")) - - #start the transaction: - channel.dtx_demarcation_select() - self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_start(xid=tx).status) - - #'swap' the message from one queue to the other, under that transaction: - self.swap(self.channel, "queue-a", "queue-b") - - #mark the end of the transactional work: - self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_end(xid=tx).status) - - def swap(self, channel, src, dest): - #consume from src: - channel.message_subscribe(destination="temp-swap", queue=src, confirm_mode=1) - channel.message_flow(destination="temp-swap", unit=0, value=1) - channel.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) - msg = self.client.queue("temp-swap").get(timeout=1) - channel.message_cancel(destination="temp-swap") - msg.complete(); - - #re-publish to dest - channel.message_transfer(content=Content(properties={'routing_key':dest, 'message_id':msg.content['message_id']}, - body=msg.content.body)) - - def assertMessageCount(self, expected, queue): - self.assertEqual(expected, self.channel.queue_query(queue=queue).message_count) - - def assertMessageId(self, expected, queue): - self.channel.message_subscribe(queue=queue, destination="results") - self.channel.message_flow(destination="results", unit=0, value=1) - self.channel.message_flow(destination="results", unit=1, value=0xFFFFFFFF) - self.assertEqual(expected, self.client.queue("results").get(timeout=1).content['message_id']) - self.channel.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10_preview/example.py b/qpid/python/tests_0-10_preview/example.py deleted file mode 100644 index da5ee2441f..0000000000 --- a/qpid/python/tests_0-10_preview/example.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# 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. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class ExampleTest (TestBase): - """ - An example Qpid test, illustrating the unittest framework and the - python Qpid client. The test class must inherit TestBase. The - test code uses the Qpid client to interact with a qpid broker and - verify it behaves as expected. - """ - - def test_example(self): - """ - An example test. Note that test functions must start with 'test_' - to be recognized by the test framework. - """ - - # By inheriting TestBase, self.client is automatically connected - # and self.channel is automatically opened as channel(1) - # Other channel methods mimic the protocol. - channel = self.channel - - # Now we can send regular commands. If you want to see what the method - # arguments mean or what other commands are available, you can use the - # python builtin help() method. For example: - #help(chan) - #help(chan.exchange_declare) - - # If you want browse the available protocol methods without being - # connected to a live server you can use the amqp-doc utility: - # - # Usage amqp-doc [] [ ... ] - # - # Options: - # -e, --regexp use regex instead of glob when matching - - # Now that we know what commands are available we can use them to - # interact with the server. - - # Here we use ordinal arguments. - self.exchange_declare(channel, 0, "test", "direct") - - # Here we use keyword arguments. - self.queue_declare(channel, queue="test-queue") - channel.queue_bind(queue="test-queue", exchange="test", routing_key="key") - - # Call Channel.basic_consume to register as a consumer. - # All the protocol methods return a message object. The message object - # has fields corresponding to the reply method fields, plus a content - # field that is filled if the reply includes content. In this case the - # interesting field is the consumer_tag. - channel.message_subscribe(queue="test-queue", destination="consumer_tag") - channel.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) - channel.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) - - # We can use the Client.queue(...) method to access the queue - # corresponding to our consumer_tag. - queue = self.client.queue("consumer_tag") - - # Now lets publish a message and see if our consumer gets it. To do - # this we need to import the Content class. - sent = Content("Hello World!") - sent["routing_key"] = "key" - channel.message_transfer(destination="test", content=sent) - - # Now we'll wait for the message to arrive. We can use the timeout - # argument in case the server hangs. By default queue.get() will wait - # until a message arrives or the connection to the server dies. - msg = queue.get(timeout=10) - - # And check that we got the right response with assertEqual - self.assertEqual(sent.body, msg.content.body) - - # Now acknowledge the message. - msg.complete() - diff --git a/qpid/python/tests_0-10_preview/exchange.py b/qpid/python/tests_0-10_preview/exchange.py deleted file mode 100644 index 86c39b7736..0000000000 --- a/qpid/python/tests_0-10_preview/exchange.py +++ /dev/null @@ -1,335 +0,0 @@ -# -# 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. -# - -""" -Tests for exchange behaviour. - -Test classes ending in 'RuleTests' are derived from rules in amqp.xml. -""" - -import Queue, logging -from qpid.testlib import TestBase -from qpid.content import Content -from qpid.client import Closed - - -class StandardExchangeVerifier: - """Verifies standard exchange behavior. - - Used as base class for classes that test standard exchanges.""" - - def verifyDirectExchange(self, ex): - """Verify that ex behaves like a direct exchange.""" - self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex, routing_key="k") - self.assertPublishConsume(exchange=ex, queue="q", routing_key="k") - try: - self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk") - self.fail("Expected Empty exception") - except Queue.Empty: None # Expected - - def verifyFanOutExchange(self, ex): - """Verify that ex behaves like a fanout exchange.""" - self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex) - self.queue_declare(queue="p") - self.channel.queue_bind(queue="p", exchange=ex) - for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex) - - def verifyTopicExchange(self, ex): - """Verify that ex behaves like a topic exchange""" - self.queue_declare(queue="a") - self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*") - q = self.consume("a") - self.assertPublishGet(q, ex, "a.b.x") - self.assertPublishGet(q, ex, "a.x.b.x") - self.assertPublishGet(q, ex, "a.x.x.b.x") - # Shouldn't match - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b.x.y"})) - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"x.a.b.x"})) - self.channel.message_transfer(destination=ex, content=Content(properties={'routing_key':"a.b"})) - self.assert_(q.empty()) - - def verifyHeadersExchange(self, ex): - """Verify that ex is a headers exchange""" - self.queue_declare(queue="q") - self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} ) - q = self.consume("q") - headers = {"name":"fred", "age":3} - self.assertPublishGet(q, exchange=ex, properties=headers) - self.channel.message_transfer(destination=ex) # No headers, won't deliver - self.assertEmpty(q); - - -class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier): - """ - The server SHOULD implement these standard exchange types: topic, headers. - - Client attempts to declare an exchange with each of these standard types. - """ - - def testDirect(self): - """Declare and test a direct exchange""" - self.exchange_declare(0, exchange="d", type="direct") - self.verifyDirectExchange("d") - - def testFanout(self): - """Declare and test a fanout exchange""" - self.exchange_declare(0, exchange="f", type="fanout") - self.verifyFanOutExchange("f") - - def testTopic(self): - """Declare and test a topic exchange""" - self.exchange_declare(0, exchange="t", type="topic") - self.verifyTopicExchange("t") - - def testHeaders(self): - """Declare and test a headers exchange""" - self.exchange_declare(0, exchange="h", type="headers") - self.verifyHeadersExchange("h") - - -class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier): - """ - The server MUST, in each virtual host, pre-declare an exchange instance - for each standard exchange type that it implements, where the name of the - exchange instance is amq. followed by the exchange type name. - - Client creates a temporary queue and attempts to bind to each required - exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if - those types are defined). - """ - def testAmqDirect(self): self.verifyDirectExchange("amq.direct") - - def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout") - - def testAmqTopic(self): self.verifyTopicExchange("amq.topic") - - def testAmqMatch(self): self.verifyHeadersExchange("amq.match") - -class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier): - """ - The server MUST predeclare a direct exchange to act as the default exchange - for content Publish methods and for default queue bindings. - - Client checks that the default exchange is active by specifying a queue - binding with no exchange name, and publishing a message with a suitable - routing key but without specifying the exchange name, then ensuring that - the message arrives in the queue correctly. - """ - def testDefaultExchange(self): - # Test automatic binding by queue name. - self.queue_declare(queue="d") - self.assertPublishConsume(queue="d", routing_key="d") - # Test explicit bind to default queue - self.verifyDirectExchange("") - - -# TODO aconway 2006-09-27: Fill in empty tests: - -class DefaultAccessRuleTests(TestBase): - """ - The server MUST NOT allow clients to access the default exchange except - by specifying an empty exchange name in the Queue.Bind and content Publish - methods. - """ - -class ExtensionsRuleTests(TestBase): - """ - The server MAY implement other exchange types as wanted. - """ - - -class DeclareMethodMinimumRuleTests(TestBase): - """ - The server SHOULD support a minimum of 16 exchanges per virtual host and - ideally, impose no limit except as defined by available resources. - - The client creates as many exchanges as it can until the server reports - an error; the number of exchanges successfuly created must be at least - sixteen. - """ - - -class DeclareMethodTicketFieldValidityRuleTests(TestBase): - """ - The client MUST provide a valid access ticket giving "active" access to - the realm in which the exchange exists or will be created, or "passive" - access if the if-exists flag is set. - - Client creates access ticket with wrong access rights and attempts to use - in this method. - """ - - -class DeclareMethodExchangeFieldReservedRuleTests(TestBase): - """ - Exchange names starting with "amq." are reserved for predeclared and - standardised exchanges. The client MUST NOT attempt to create an exchange - starting with "amq.". - - - """ - - -class DeclareMethodTypeFieldTypedRuleTests(TestBase): - """ - Exchanges cannot be redeclared with different types. The client MUST not - attempt to redeclare an existing exchange with a different type than used - in the original Exchange.Declare method. - - - """ - - -class DeclareMethodTypeFieldSupportRuleTests(TestBase): - """ - The client MUST NOT attempt to create an exchange with a type that the - server does not support. - - - """ - - -class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase): - """ - If set, and the exchange does not already exist, the server MUST raise a - channel exception with reply code 404 (not found). - """ - def test(self): - try: - self.channel.exchange_declare(exchange="humpty_dumpty", passive=True) - self.fail("Expected 404 for passive declaration of unknown exchange.") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - -class DeclareMethodDurableFieldSupportRuleTests(TestBase): - """ - The server MUST support both durable and transient exchanges. - - - """ - - -class DeclareMethodDurableFieldStickyRuleTests(TestBase): - """ - The server MUST ignore the durable field if the exchange already exists. - - - """ - - -class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase): - """ - The server MUST ignore the auto-delete field if the exchange already - exists. - - - """ - - -class DeleteMethodTicketFieldValidityRuleTests(TestBase): - """ - The client MUST provide a valid access ticket giving "active" access - rights to the exchange's access realm. - - Client creates access ticket with wrong access rights and attempts to use - in this method. - """ - - -class DeleteMethodExchangeFieldExistsRuleTests(TestBase): - """ - The client MUST NOT attempt to delete an exchange that does not exist. - """ - - -class HeadersExchangeTests(TestBase): - """ - Tests for headers exchange functionality. - """ - def setUp(self): - TestBase.setUp(self) - self.queue_declare(queue="q") - self.q = self.consume("q") - - def myAssertPublishGet(self, headers): - self.assertPublishGet(self.q, exchange="amq.match", properties=headers) - - def myBasicPublish(self, headers): - self.channel.message_transfer(destination="amq.match", content=Content("foobar", properties={'application_headers':headers})) - - def testMatchAll(self): - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3}) - self.myAssertPublishGet({"name":"fred", "age":3}) - self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"}) - - # None of these should match - self.myBasicPublish({}) - self.myBasicPublish({"name":"barney"}) - self.myBasicPublish({"name":10}) - self.myBasicPublish({"name":"fred", "age":2}) - self.assertEmpty(self.q) - - def testMatchAny(self): - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3}) - self.myAssertPublishGet({"name":"fred"}) - self.myAssertPublishGet({"name":"fred", "ignoreme":10}) - self.myAssertPublishGet({"ignoreme":10, "age":3}) - - # Wont match - self.myBasicPublish({}) - self.myBasicPublish({"irrelevant":0}) - self.assertEmpty(self.q) - - -class MiscellaneousErrorsTests(TestBase): - """ - Test some miscellaneous error conditions - """ - def testTypeNotKnown(self): - try: - self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") - self.fail("Expected 503 for declaration of unknown exchange type.") - except Closed, e: - self.assertConnectionException(503, e.args[0]) - - def testDifferentDeclaredType(self): - self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") - try: - self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic") - self.fail("Expected 530 for redeclaration of exchange with different type.") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - #cleanup - other = self.connect() - c2 = other.channel(1) - c2.session_open() - c2.exchange_delete(exchange="test_different_declared_type_exchange") - -class ExchangeTests(TestBase): - def testHeadersBindNoMatchArg(self): - self.channel.queue_declare(queue="q", exclusive=True, auto_delete=True) - try: - self.channel.queue_bind(queue="q", exchange="amq.match", arguments={"name":"fred" , "age":3} ) - self.fail("Expected failure for missing x-match arg.") - except Closed, e: - self.assertConnectionException(541, e.args[0]) diff --git a/qpid/python/tests_0-10_preview/execution.py b/qpid/python/tests_0-10_preview/execution.py deleted file mode 100644 index 3ff6d8ea65..0000000000 --- a/qpid/python/tests_0-10_preview/execution.py +++ /dev/null @@ -1,29 +0,0 @@ -# -# 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. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class ExecutionTests (TestBase): - def test_flush(self): - channel = self.channel - for i in [1, 2, 3]: - channel.message_transfer( - content=Content(properties={'routing_key':str(i)})) - assert(channel.completion.wait(channel.completion.command_id, timeout=1)) diff --git a/qpid/python/tests_0-10_preview/message.py b/qpid/python/tests_0-10_preview/message.py deleted file mode 100644 index a3d32bdb2d..0000000000 --- a/qpid/python/tests_0-10_preview/message.py +++ /dev/null @@ -1,834 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from qpid.reference import Reference, ReferenceId - -class MessageTests(TestBase): - """Tests for 'methods' on the amqp message 'class'""" - - def test_consume_no_local(self): - """ - Test that the no_local flag is honoured in the consume method - """ - channel = self.channel - #setup, declare two queues: - channel.queue_declare(queue="test-queue-1a", exclusive=True, auto_delete=True) - channel.queue_declare(queue="test-queue-1b", exclusive=True, auto_delete=True) - #establish two consumers one of which excludes delivery of locally sent messages - self.subscribe(destination="local_included", queue="test-queue-1a") - self.subscribe(destination="local_excluded", queue="test-queue-1b", no_local=True) - - #send a message - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1a"}, body="consume_no_local")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-1b"}, body="consume_no_local")) - - #check the queues of the two consumers - excluded = self.client.queue("local_excluded") - included = self.client.queue("local_included") - msg = included.get(timeout=1) - self.assertEqual("consume_no_local", msg.content.body) - try: - excluded.get(timeout=1) - self.fail("Received locally published message though no_local=true") - except Empty: None - - def test_consume_no_local_awkward(self): - - """ - If an exclusive queue gets a no-local delivered to it, that - message could 'block' delivery of subsequent messages or it - could be left on the queue, possibly never being consumed - (this is the case for example in the qpid JMS mapping of - topics). This test excercises a Qpid C++ broker hack that - deletes such messages. - """ - - channel = self.channel - #setup: - channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) - #establish consumer which excludes delivery of locally sent messages - self.subscribe(destination="local_excluded", queue="test-queue", no_local=True) - - #send a 'local' message - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="local")) - - #send a non local message - other = self.connect() - channel2 = other.channel(1) - channel2.session_open() - channel2.message_transfer(content=Content(properties={'routing_key' : "test-queue"}, body="foreign")) - channel2.session_close() - other.close() - - #check that the second message only is delivered - excluded = self.client.queue("local_excluded") - msg = excluded.get(timeout=1) - self.assertEqual("foreign", msg.content.body) - try: - excluded.get(timeout=1) - self.fail("Received extra message") - except Empty: None - #check queue is empty - self.assertEqual(0, channel.queue_query(queue="test-queue").message_count) - - - def test_consume_exclusive(self): - """ - Test that the exclusive flag is honoured in the consume method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-2", exclusive=True, auto_delete=True) - - #check that an exclusive consumer prevents other consumer being created: - self.subscribe(destination="first", queue="test-queue-2", exclusive=True) - try: - self.subscribe(destination="second", queue="test-queue-2") - self.fail("Expected consume request to fail due to previous exclusive consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - #open new channel and cleanup last consumer: - channel = self.client.channel(2) - channel.session_open() - - #check that an exclusive consumer cannot be created if a consumer already exists: - self.subscribe(channel, destination="first", queue="test-queue-2") - try: - self.subscribe(destination="second", queue="test-queue-2", exclusive=True) - self.fail("Expected exclusive consume request to fail due to previous consumer") - except Closed, e: - self.assertChannelException(403, e.args[0]) - - def test_consume_queue_errors(self): - """ - Test error conditions associated with the queue field of the consume method: - """ - channel = self.channel - try: - #queue specified but doesn't exist: - self.subscribe(queue="invalid-queue", destination="") - self.fail("Expected failure when consuming from non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - channel = self.client.channel(2) - channel.session_open() - try: - #queue not specified and none previously declared for channel: - self.subscribe(channel, queue="", destination="") - self.fail("Expected failure when consuming from unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_consume_unique_consumers(self): - """ - Ensure unique consumer tags are enforced - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-3", exclusive=True, auto_delete=True) - - #check that attempts to use duplicate tags are detected and prevented: - self.subscribe(destination="first", queue="test-queue-3") - try: - self.subscribe(destination="first", queue="test-queue-3") - self.fail("Expected consume request to fail due to non-unique tag") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - def test_cancel(self): - """ - Test compliance of the basic.cancel method - """ - channel = self.channel - #setup, declare a queue: - channel.queue_declare(queue="test-queue-4", exclusive=True, auto_delete=True) - self.subscribe(destination="my-consumer", queue="test-queue-4") - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="One")) - - #cancel should stop messages being delivered - channel.message_cancel(destination="my-consumer") - channel.message_transfer(content=Content(properties={'routing_key' : "test-queue-4"}, body="Two")) - myqueue = self.client.queue("my-consumer") - msg = myqueue.get(timeout=1) - self.assertEqual("One", msg.content.body) - try: - msg = myqueue.get(timeout=1) - self.fail("Got message after cancellation: " + msg) - except Empty: None - - #cancellation of non-existant consumers should be handled without error - channel.message_cancel(destination="my-consumer") - channel.message_cancel(destination="this-never-existed") - - - def test_ack(self): - """ - Test basic ack/recover behaviour - """ - channel = self.channel - channel.queue_declare(queue="test-ack-queue", exclusive=True, auto_delete=True) - - self.subscribe(queue="test-ack-queue", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") - - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="One")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Two")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Three")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Four")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-ack-queue"}, body="Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - msg2.complete(cumulative=True)#One and Two - msg4.complete(cumulative=False) - - channel.message_recover(requeue=False) - - msg3b = queue.get(timeout=1) - msg5b = queue.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - - def test_recover(self): - """ - Test recover behaviour - """ - channel = self.channel - channel.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.fanout", queue="queue-a") - channel.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.fanout", queue="queue-b") - - self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) - self.subscribe(queue="queue-b", destination="confirmed", confirm_mode=0) - confirmed = self.client.queue("confirmed") - unconfirmed = self.client.queue("unconfirmed") - - data = ["One", "Two", "Three", "Four", "Five"] - for d in data: - channel.message_transfer(destination="amq.fanout", content=Content(body=d)) - - for q in [confirmed, unconfirmed]: - for d in data: - self.assertEqual(d, q.get(timeout=1).content.body) - self.assertEmpty(q) - - channel.message_recover(requeue=False) - - self.assertEmpty(confirmed) - - while len(data): - msg = None - for d in data: - msg = unconfirmed.get(timeout=1) - self.assertEqual(d, msg.content.body) - self.assertEqual(True, msg.content['redelivered']) - self.assertEmpty(unconfirmed) - data.remove(msg.content.body) - msg.complete(cumulative=False) - channel.message_recover(requeue=False) - - - def test_recover_requeue(self): - """ - Test requeing on recovery - """ - channel = self.channel - channel.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) - - self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") - - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Two")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - msg2.complete(cumulative=True) #One and Two - msg4.complete(cumulative=False) #Four - - channel.message_cancel(destination="consumer_tag") - - #publish a new message - channel.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Six")) - #requeue unacked messages (Three and Five) - channel.message_recover(requeue=True) - - self.subscribe(queue="test-requeue", destination="consumer_tag") - queue2 = self.client.queue("consumer_tag") - - msg3b = queue2.get(timeout=1) - msg5b = queue2.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - self.assertEqual(True, msg3b.content['redelivered']) - self.assertEqual(True, msg5b.content['redelivered']) - - self.assertEqual("Six", queue2.get(timeout=1).content.body) - - try: - extra = queue2.get(timeout=1) - self.fail("Got unexpected message in second queue: " + extra.content.body) - except Empty: None - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message in original queue: " + extra.content.body) - except Empty: None - - - def test_qos_prefetch_count(self): - """ - Test that the prefetch count specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-count", exclusive=True, auto_delete=True) - subscription = self.subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") - - #set prefetch to 5: - channel.message_qos(prefetch_count=5) - - #publish 10 messages: - for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-count"}, body="Message %d" % i)) - - #only 5 messages should have been delivered: - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - msg.complete() - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - msg.complete() - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - - - def test_qos_prefetch_size(self): - """ - Test that the prefetch size specified is honoured - """ - #setup: declare queue and subscribe - channel = self.channel - channel.queue_declare(queue="test-prefetch-size", exclusive=True, auto_delete=True) - subscription = self.subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) - queue = self.client.queue("consumer_tag") - - #set prefetch to 50 bytes (each message is 9 or 10 bytes): - channel.message_qos(prefetch_size=50) - - #publish 10 messages: - for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body="Message %d" % i)) - - #only 5 messages should have been delivered (i.e. 45 bytes worth): - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - msg.complete() - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - msg.complete() - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - #make sure that a single oversized message still gets delivered - large = "abcdefghijklmnopqrstuvwxyz" - large = large + "-" + large; - channel.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body=large)) - msg = queue.get(timeout=1) - self.assertEqual(large, msg.content.body) - - def test_reject(self): - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True, alternate_exchange="amq.fanout") - channel.queue_declare(queue = "r", exclusive=True, auto_delete=True) - channel.queue_bind(queue = "r", exchange = "amq.fanout") - - self.subscribe(queue = "q", destination = "consumer", confirm_mode = 1) - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body="blah, blah")) - msg = self.client.queue("consumer").get(timeout = 1) - self.assertEquals(msg.content.body, "blah, blah") - channel.message_reject([msg.command_id, msg.command_id]) - - self.subscribe(queue = "r", destination = "checker") - msg = self.client.queue("checker").get(timeout = 1) - self.assertEquals(msg.content.body, "blah, blah") - - def test_credit_flow_messages(self): - """ - Test basic credit based flow control with unit = message - """ - #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c") - channel.message_flow_mode(mode = 0, destination = "c") - #send batch of messages to queue - for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) - - #set message credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 0, value = 5, destination = "c") - #set infinite byte credit - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") - #check that expected number were received - q = self.client.queue("c") - for i in range(1, 6): - self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) - self.assertEmpty(q) - - #increase credit again and check more are received - for i in range(6, 11): - channel.message_flow(unit = 0, value = 1, destination = "c") - self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) - self.assertEmpty(q) - - def test_credit_flow_bytes(self): - """ - Test basic credit based flow control with unit = bytes - """ - #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c") - channel.message_flow_mode(mode = 0, destination = "c") - #send batch of messages to queue - for i in range(10): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) - - #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 35 - - #set byte credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 1, value = msg_size*5, destination = "c") - #set infinite message credit - channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") - #check that expected number were received - q = self.client.queue("c") - for i in range(5): - self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") - self.assertEmpty(q) - - #increase credit again and check more are received - for i in range(5): - channel.message_flow(unit = 1, value = msg_size, destination = "c") - self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") - self.assertEmpty(q) - - - def test_window_flow_messages(self): - """ - Test basic window based flow control with unit = message - """ - #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) - channel.message_flow_mode(mode = 1, destination = "c") - #send batch of messages to queue - for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %d" % i)) - - #set message credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 0, value = 5, destination = "c") - #set infinite byte credit - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") - #check that expected number were received - q = self.client.queue("c") - for i in range(1, 6): - msg = q.get(timeout = 1) - self.assertDataEquals(channel, msg, "Message %d" % i) - self.assertEmpty(q) - - #acknowledge messages and check more are received - msg.complete(cumulative=True) - for i in range(6, 11): - self.assertDataEquals(channel, q.get(timeout = 1), "Message %d" % i) - self.assertEmpty(q) - - - def test_window_flow_bytes(self): - """ - Test basic window based flow control with unit = bytes - """ - #declare an exclusive queue - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - #create consumer (for now that defaults to infinite credit) - channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1) - channel.message_flow_mode(mode = 1, destination = "c") - #send batch of messages to queue - for i in range(10): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "abcdefgh")) - - #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 40 - - #set byte credit to finite amount (less than enough for all messages) - channel.message_flow(unit = 1, value = msg_size*5, destination = "c") - #set infinite message credit - channel.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") - #check that expected number were received - q = self.client.queue("c") - msgs = [] - for i in range(5): - msg = q.get(timeout = 1) - msgs.append(msg) - self.assertDataEquals(channel, msg, "abcdefgh") - self.assertEmpty(q) - - #ack each message individually and check more are received - for i in range(5): - msg = msgs.pop() - msg.complete(cumulative=False) - self.assertDataEquals(channel, q.get(timeout = 1), "abcdefgh") - self.assertEmpty(q) - - def test_subscribe_not_acquired(self): - """ - Test the not-acquired modes works as expected for a simple case - """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - for i in range(1, 6): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) - - self.subscribe(queue = "q", destination = "a", acquire_mode = 1) - self.subscribe(queue = "q", destination = "b", acquire_mode = 1) - - for i in range(6, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "Message %s" % i)) - - #both subscribers should see all messages - qA = self.client.queue("a") - qB = self.client.queue("b") - for i in range(1, 11): - for q in [qA, qB]: - msg = q.get(timeout = 1) - self.assertEquals("Message %s" % i, msg.content.body) - msg.complete() - - #messages should still be on the queue: - self.assertEquals(10, channel.queue_query(queue = "q").message_count) - - def test_acquire(self): - """ - Test explicit acquire function - """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "acquire me")) - - self.subscribe(queue = "q", destination = "a", acquire_mode = 1, confirm_mode = 1) - msg = self.client.queue("a").get(timeout = 1) - #message should still be on the queue: - self.assertEquals(1, channel.queue_query(queue = "q").message_count) - - channel.message_acquire([msg.command_id, msg.command_id]) - #check that we get notification (i.e. message_acquired) - response = channel.control_queue.get(timeout=1) - self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) - #message should have been removed from the queue: - self.assertEquals(0, channel.queue_query(queue = "q").message_count) - msg.complete() - - - - - def test_release(self): - """ - Test explicit release function - """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "release me")) - - self.subscribe(queue = "q", destination = "a", acquire_mode = 0, confirm_mode = 1) - msg = self.client.queue("a").get(timeout = 1) - channel.message_cancel(destination = "a") - channel.message_release([msg.command_id, msg.command_id]) - msg.complete() - - #message should not have been removed from the queue: - self.assertEquals(1, channel.queue_query(queue = "q").message_count) - - def test_release_ordering(self): - """ - Test order of released messages is as expected - """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - for i in range (1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "released message %s" % (i))) - - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - channel.message_flow(unit = 0, value = 10, destination = "a") - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - queue = self.client.queue("a") - first = queue.get(timeout = 1) - for i in range (2, 10): - self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) - last = queue.get(timeout = 1) - self.assertEmpty(queue) - channel.message_release([first.command_id, last.command_id]) - last.complete()#will re-allocate credit, as in window mode - for i in range (1, 11): - self.assertEquals("released message %s" % (i), queue.get(timeout = 1).content.body) - - def test_ranged_ack(self): - """ - Test acking of messages ranges - """ - channel = self.channel - channel.queue_declare(queue = "q", exclusive=True, auto_delete=True) - for i in range (1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message %s" % (i))) - - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - channel.message_flow(unit = 0, value = 10, destination = "a") - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - queue = self.client.queue("a") - for i in range (1, 11): - self.assertEquals("message %s" % (i), queue.get(timeout = 1).content.body) - self.assertEmpty(queue) - - #ack all but the third message (command id 2) - channel.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=[0,1,3,6,7,7,8,9]) - channel.message_recover() - self.assertEquals("message 3", queue.get(timeout = 1).content.body) - self.assertEmpty(queue) - - def test_subscribe_not_acquired_2(self): - channel = self.channel - - #publish some messages - self.queue_declare(queue = "q", exclusive=True, auto_delete=True) - for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) - - #consume some of them - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1) - channel.message_flow_mode(mode = 0, destination = "a") - channel.message_flow(unit = 0, value = 5, destination = "a") - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - - queue = self.client.queue("a") - for i in range(1, 6): - msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) - msg.complete() - self.assertEmpty(queue) - - #now create a not-acquired subscriber - channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - - #check it gets those not consumed - queue = self.client.queue("b") - channel.message_flow(unit = 0, value = 1, destination = "b") - for i in range(6, 11): - msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) - msg.complete() - channel.message_flow(unit = 0, value = 1, destination = "b") - self.assertEmpty(queue) - - #check all 'browsed' messages are still on the queue - self.assertEqual(5, channel.queue_query(queue="q").message_count) - - def test_subscribe_not_acquired_3(self): - channel = self.channel - - #publish some messages - self.queue_declare(queue = "q", exclusive=True, auto_delete=True) - for i in range(1, 11): - channel.message_transfer(content=Content(properties={'routing_key' : "q"}, body = "message-%d" % (i))) - - #create a not-acquired subscriber - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - channel.message_flow(unit = 0, value = 10, destination = "a") - - #browse through messages - queue = self.client.queue("a") - for i in range(1, 11): - msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) - if (i % 2): - #try to acquire every second message - channel.message_acquire([msg.command_id, msg.command_id]) - #check that acquire succeeds - response = channel.control_queue.get(timeout=1) - self.assertEquals(response.transfers, [msg.command_id, msg.command_id]) - msg.complete() - self.assertEmpty(queue) - - #create a second not-acquired subscriber - channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - channel.message_flow(unit = 0, value = 1, destination = "b") - #check it gets those not consumed - queue = self.client.queue("b") - for i in [2,4,6,8,10]: - msg = queue.get(timeout = 1) - self.assertEquals("message-%d" % (i), msg.content.body) - msg.complete() - channel.message_flow(unit = 0, value = 1, destination = "b") - self.assertEmpty(queue) - - #check all 'browsed' messages are still on the queue - self.assertEqual(5, channel.queue_query(queue="q").message_count) - - def test_release_unacquired(self): - channel = self.channel - - #create queue - self.queue_declare(queue = "q", exclusive=True, auto_delete=True, durable=True) - - #send message - channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) - - #create two 'browsers' - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - channel.message_flow(unit = 0, value = 10, destination = "a") - queueA = self.client.queue("a") - - channel.message_subscribe(queue = "q", destination = "b", confirm_mode = 1, acquire_mode=1) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - channel.message_flow(unit = 0, value = 10, destination = "b") - queueB = self.client.queue("b") - - #have each browser release the message - msgA = queueA.get(timeout = 1) - channel.message_release([msgA.command_id, msgA.command_id]) - - msgB = queueB.get(timeout = 1) - channel.message_release([msgB.command_id, msgB.command_id]) - - #cancel browsers - channel.message_cancel(destination = "a") - channel.message_cancel(destination = "b") - - #create consumer - channel.message_subscribe(queue = "q", destination = "c", confirm_mode = 1, acquire_mode=0) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") - channel.message_flow(unit = 0, value = 10, destination = "c") - queueC = self.client.queue("c") - #consume the message then ack it - msgC = queueC.get(timeout = 1) - msgC.complete() - #ensure there are no other messages - self.assertEmpty(queueC) - - def test_no_size(self): - self.queue_declare(queue = "q", exclusive=True, auto_delete=True) - - ch = self.channel - ch.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) - - ch.message_subscribe(queue = "q", destination="d", confirm_mode = 0) - ch.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "d") - ch.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "d") - - queue = self.client.queue("d") - msg = queue.get(timeout = 3) - self.assertEquals("message-body", msg.content.body) - - def assertDataEquals(self, channel, msg, expected): - self.assertEquals(expected, msg.content.body) - - def assertEmpty(self, queue): - try: - extra = queue.get(timeout=1) - self.fail("Queue not empty, contains: " + extra.content.body) - except Empty: None - -class SizelessContent(Content): - - def size(self): - return None diff --git a/qpid/python/tests_0-10_preview/persistence.py b/qpid/python/tests_0-10_preview/persistence.py deleted file mode 100644 index ad578474eb..0000000000 --- a/qpid/python/tests_0-10_preview/persistence.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class PersistenceTests(TestBase): - def test_delete_queue_after_publish(self): - channel = self.channel - channel.synchronous = False - - #create queue - channel.queue_declare(queue = "q", auto_delete=True, durable=True) - - #send message - for i in range(1, 10): - channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) - - channel.synchronous = True - #explicitly delete queue - channel.queue_delete(queue = "q") - - def test_ack_message_from_deleted_queue(self): - channel = self.channel - channel.synchronous = False - - #create queue - channel.queue_declare(queue = "q", auto_delete=True, durable=True) - - #send message - channel.message_transfer(content=Content(properties={'routing_key' : "q", 'delivery_mode':2}, body = "my-message")) - - #create consumer - channel.message_subscribe(queue = "q", destination = "a", confirm_mode = 1, acquire_mode=0) - channel.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - channel.message_flow(unit = 0, value = 10, destination = "a") - queue = self.client.queue("a") - - #consume the message, cancel subscription (triggering auto-delete), then ack it - msg = queue.get(timeout = 5) - channel.message_cancel(destination = "a") - msg.complete() - - def test_queue_deletion(self): - channel = self.channel - channel.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True) - channel.queue_bind(exchange="amq.topic", queue="durable-subscriber-queue", routing_key="xyz") - channel.message_transfer(destination= "amq.topic", content=Content(properties={'routing_key' : "xyz", 'delivery_mode':2}, body = "my-message")) - channel.queue_delete(queue = "durable-subscriber-queue") - diff --git a/qpid/python/tests_0-10_preview/query.py b/qpid/python/tests_0-10_preview/query.py deleted file mode 100644 index eba2ee6dd1..0000000000 --- a/qpid/python/tests_0-10_preview/query.py +++ /dev/null @@ -1,227 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class QueryTests(TestBase): - """Tests for various query methods introduced in 0-10 and available in 0-9 for preview""" - - def test_exchange_query(self): - """ - Test that the exchange_query method works as expected - """ - channel = self.channel - #check returned type for the standard exchanges - self.assert_type("direct", channel.exchange_query(name="amq.direct")) - self.assert_type("topic", channel.exchange_query(name="amq.topic")) - self.assert_type("fanout", channel.exchange_query(name="amq.fanout")) - self.assert_type("headers", channel.exchange_query(name="amq.match")) - self.assert_type("direct", channel.exchange_query(name="")) - #declare an exchange - channel.exchange_declare(exchange="my-test-exchange", type= "direct", durable=False) - #check that the result of a query is as expected - response = channel.exchange_query(name="my-test-exchange") - self.assert_type("direct", response) - self.assertEqual(False, response.durable) - self.assertEqual(False, response.not_found) - #delete the exchange - channel.exchange_delete(exchange="my-test-exchange") - #check that the query now reports not-found - self.assertEqual(True, channel.exchange_query(name="my-test-exchange").not_found) - - def assert_type(self, expected_type, response): - self.assertEqual(expected_type, response.__getattr__("type")) - - def test_binding_query_direct(self): - """ - Test that the binding_query method works as expected with the direct exchange - """ - self.binding_query_with_key("amq.direct") - - def test_binding_query_topic(self): - """ - Test that the binding_query method works as expected with the direct exchange - """ - self.binding_query_with_key("amq.topic") - - def binding_query_with_key(self, exchange_name): - channel = self.channel - #setup: create two queues - channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) - channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) - - channel.queue_bind(exchange=exchange_name, queue="used-queue", routing_key="used-key") - - # test detection of any binding to specific queue - response = channel.binding_query(exchange=exchange_name, queue="used-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - - # test detection of specific binding to any queue - response = channel.binding_query(exchange=exchange_name, routing_key="used-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.key_not_matched) - - # test detection of specific binding to specific queue - response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="used-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - self.assertEqual(False, response.key_not_matched) - - # test unmatched queue, unspecified binding - response = channel.binding_query(exchange=exchange_name, queue="unused-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - - # test unspecified queue, unmatched binding - response = channel.binding_query(exchange=exchange_name, routing_key="unused-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.key_not_matched) - - # test matched queue, unmatched binding - response = channel.binding_query(exchange=exchange_name, queue="used-queue", routing_key="unused-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - self.assertEqual(True, response.key_not_matched) - - # test unmatched queue, matched binding - response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="used-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - self.assertEqual(False, response.key_not_matched) - - # test unmatched queue, unmatched binding - response = channel.binding_query(exchange=exchange_name, queue="unused-queue", routing_key="unused-key") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - self.assertEqual(True, response.key_not_matched) - - #test exchange not found - self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) - - #test queue not found - self.assertEqual(True, channel.binding_query(exchange=exchange_name, queue="unknown-queue").queue_not_found) - - - def test_binding_query_fanout(self): - """ - Test that the binding_query method works as expected with fanout exchange - """ - channel = self.channel - #setup - channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) - channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.fanout", queue="used-queue") - - # test detection of any binding to specific queue - response = channel.binding_query(exchange="amq.fanout", queue="used-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - - # test unmatched queue, unspecified binding - response = channel.binding_query(exchange="amq.fanout", queue="unused-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - - #test exchange not found - self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) - - #test queue not found - self.assertEqual(True, channel.binding_query(exchange="amq.fanout", queue="unknown-queue").queue_not_found) - - def test_binding_query_header(self): - """ - Test that the binding_query method works as expected with headers exchanges - """ - channel = self.channel - #setup - channel.queue_declare(queue="used-queue", exclusive=True, auto_delete=True) - channel.queue_declare(queue="unused-queue", exclusive=True, auto_delete=True) - channel.queue_bind(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"} ) - - # test detection of any binding to specific queue - response = channel.binding_query(exchange="amq.match", queue="used-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - - # test detection of specific binding to any queue - response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "a":"A"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.args_not_matched) - - # test detection of specific binding to specific queue - response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "a":"A"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - self.assertEqual(False, response.args_not_matched) - - # test unmatched queue, unspecified binding - response = channel.binding_query(exchange="amq.match", queue="unused-queue") - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - - # test unspecified queue, unmatched binding - response = channel.binding_query(exchange="amq.match", arguments={"x-match":"all", "b":"B"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.args_not_matched) - - # test matched queue, unmatched binding - response = channel.binding_query(exchange="amq.match", queue="used-queue", arguments={"x-match":"all", "b":"B"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(False, response.queue_not_matched) - self.assertEqual(True, response.args_not_matched) - - # test unmatched queue, matched binding - response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "a":"A"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - self.assertEqual(False, response.args_not_matched) - - # test unmatched queue, unmatched binding - response = channel.binding_query(exchange="amq.match", queue="unused-queue", arguments={"x-match":"all", "b":"B"}) - self.assertEqual(False, response.exchange_not_found) - self.assertEqual(False, response.queue_not_found) - self.assertEqual(True, response.queue_not_matched) - self.assertEqual(True, response.args_not_matched) - - #test exchange not found - self.assertEqual(True, channel.binding_query(exchange="unknown-exchange").exchange_not_found) - - #test queue not found - self.assertEqual(True, channel.binding_query(exchange="amq.match", queue="unknown-queue").queue_not_found) - diff --git a/qpid/python/tests_0-10_preview/queue.py b/qpid/python/tests_0-10_preview/queue.py deleted file mode 100644 index 7b3590d11b..0000000000 --- a/qpid/python/tests_0-10_preview/queue.py +++ /dev/null @@ -1,338 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class QueueTests(TestBase): - """Tests for 'methods' on the amqp queue 'class'""" - - def test_purge(self): - """ - Test that the purge method removes messages from the queue - """ - channel = self.channel - #setup, declare a queue and add some messages to it: - channel.exchange_declare(exchange="test-exchange", type="direct") - channel.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) - channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key") - channel.message_transfer(destination="test-exchange", content=Content("one", properties={'routing_key':"key"})) - channel.message_transfer(destination="test-exchange", content=Content("two", properties={'routing_key':"key"})) - channel.message_transfer(destination="test-exchange", content=Content("three", properties={'routing_key':"key"})) - - #check that the queue now reports 3 messages: - channel.queue_declare(queue="test-queue") - reply = channel.queue_query(queue="test-queue") - self.assertEqual(3, reply.message_count) - - #now do the purge, then test that three messages are purged and the count drops to 0 - channel.queue_purge(queue="test-queue"); - reply = channel.queue_query(queue="test-queue") - self.assertEqual(0, reply.message_count) - - #send a further message and consume it, ensuring that the other messages are really gone - channel.message_transfer(destination="test-exchange", content=Content("four", properties={'routing_key':"key"})) - self.subscribe(queue="test-queue", destination="tag") - queue = self.client.queue("tag") - msg = queue.get(timeout=1) - self.assertEqual("four", msg.content.body) - - #check error conditions (use new channels): - channel = self.client.channel(2) - channel.session_open() - try: - #queue specified but doesn't exist: - channel.queue_purge(queue="invalid-queue") - self.fail("Expected failure when purging non-existent queue") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - channel = self.client.channel(3) - channel.session_open() - try: - #queue not specified and none previously declared for channel: - channel.queue_purge() - self.fail("Expected failure when purging unspecified queue") - except Closed, e: - self.assertConnectionException(530, e.args[0]) - - #cleanup - other = self.connect() - channel = other.channel(1) - channel.session_open() - channel.exchange_delete(exchange="test-exchange") - - def test_declare_exclusive(self): - """ - Test that the exclusive field is honoured in queue.declare - """ - # TestBase.setUp has already opened channel(1) - c1 = self.channel - # Here we open a second separate connection: - other = self.connect() - c2 = other.channel(1) - c2.session_open() - - #declare an exclusive queue: - c1.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) - try: - #other connection should not be allowed to declare this: - c2.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) - self.fail("Expected second exclusive queue_declare to raise a channel exception") - except Closed, e: - self.assertChannelException(405, e.args[0]) - - - def test_declare_passive(self): - """ - Test that the passive field is honoured in queue.declare - """ - channel = self.channel - #declare an exclusive queue: - channel.queue_declare(queue="passive-queue-1", exclusive=True, auto_delete=True) - channel.queue_declare(queue="passive-queue-1", passive=True) - try: - #other connection should not be allowed to declare this: - channel.queue_declare(queue="passive-queue-2", passive=True) - self.fail("Expected passive declaration of non-existant queue to raise a channel exception") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - - def test_bind(self): - """ - Test various permutations of the queue.bind method - """ - channel = self.channel - channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) - - #straightforward case, both exchange & queue exist so no errors expected: - channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1") - - #use the queue name where the routing key is not specified: - channel.queue_bind(queue="queue-1", exchange="amq.direct") - - #try and bind to non-existant exchange - try: - channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1") - self.fail("Expected bind to non-existant exchange to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - #need to reopen a channel: - channel = self.client.channel(2) - channel.session_open() - - #try and bind non-existant queue: - try: - channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1") - self.fail("Expected bind of non-existant queue to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - def test_unbind_direct(self): - self.unbind_test(exchange="amq.direct", routing_key="key") - - def test_unbind_topic(self): - self.unbind_test(exchange="amq.topic", routing_key="key") - - def test_unbind_fanout(self): - self.unbind_test(exchange="amq.fanout") - - def test_unbind_headers(self): - self.unbind_test(exchange="amq.match", args={ "x-match":"all", "a":"b"}, headers={"a":"b"}) - - def unbind_test(self, exchange, routing_key="", args=None, headers={}): - #bind two queues and consume from them - channel = self.channel - - channel.queue_declare(queue="queue-1", exclusive=True, auto_delete=True) - channel.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) - - self.subscribe(queue="queue-1", destination="queue-1") - self.subscribe(queue="queue-2", destination="queue-2") - - queue1 = self.client.queue("queue-1") - queue2 = self.client.queue("queue-2") - - channel.queue_bind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) - channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args) - - #send a message that will match both bindings - channel.message_transfer(destination=exchange, - content=Content("one", properties={'routing_key':routing_key, 'application_headers':headers})) - - #unbind first queue - channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args) - - #send another message - channel.message_transfer(destination=exchange, - content=Content("two", properties={'routing_key':routing_key, 'application_headers':headers})) - - #check one queue has both messages and the other has only one - self.assertEquals("one", queue1.get(timeout=1).content.body) - try: - msg = queue1.get(timeout=1) - self.fail("Got extra message: %s" % msg.content.body) - except Empty: pass - - self.assertEquals("one", queue2.get(timeout=1).content.body) - self.assertEquals("two", queue2.get(timeout=1).content.body) - try: - msg = queue2.get(timeout=1) - self.fail("Got extra message: " + msg) - except Empty: pass - - - def test_delete_simple(self): - """ - Test core queue deletion behaviour - """ - channel = self.channel - - #straight-forward case: - channel.queue_declare(queue="delete-me") - channel.message_transfer(content=Content("a", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("b", properties={'routing_key':"delete-me"})) - channel.message_transfer(content=Content("c", properties={'routing_key':"delete-me"})) - channel.queue_delete(queue="delete-me") - #check that it has gone be declaring passively - try: - channel.queue_declare(queue="delete-me", passive=True) - self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - #check attempted deletion of non-existant queue is handled correctly: - channel = self.client.channel(2) - channel.session_open() - try: - channel.queue_delete(queue="i-dont-exist", if_empty=True) - self.fail("Expected delete of non-existant queue to fail") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - - - def test_delete_ifempty(self): - """ - Test that if_empty field of queue_delete is honoured - """ - channel = self.channel - - #create a queue and add a message to it (use default binding): - channel.queue_declare(queue="delete-me-2") - channel.queue_declare(queue="delete-me-2", passive=True) - channel.message_transfer(content=Content("message", properties={'routing_key':"delete-me-2"})) - - #try to delete, but only if empty: - try: - channel.queue_delete(queue="delete-me-2", if_empty=True) - self.fail("Expected delete if_empty to fail for non-empty queue") - except Closed, e: - self.assertChannelException(406, e.args[0]) - - #need new channel now: - channel = self.client.channel(2) - channel.session_open() - - #empty queue: - self.subscribe(channel, destination="consumer_tag", queue="delete-me-2") - queue = self.client.queue("consumer_tag") - msg = queue.get(timeout=1) - self.assertEqual("message", msg.content.body) - channel.message_cancel(destination="consumer_tag") - - #retry deletion on empty queue: - channel.queue_delete(queue="delete-me-2", if_empty=True) - - #check that it has gone by declaring passively: - try: - channel.queue_declare(queue="delete-me-2", passive=True) - self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - def test_delete_ifunused(self): - """ - Test that if_unused field of queue_delete is honoured - """ - channel = self.channel - - #create a queue and register a consumer: - channel.queue_declare(queue="delete-me-3") - channel.queue_declare(queue="delete-me-3", passive=True) - self.subscribe(destination="consumer_tag", queue="delete-me-3") - - #need new channel now: - channel2 = self.client.channel(2) - channel2.session_open() - #try to delete, but only if empty: - try: - channel2.queue_delete(queue="delete-me-3", if_unused=True) - self.fail("Expected delete if_unused to fail for queue with existing consumer") - except Closed, e: - self.assertChannelException(406, e.args[0]) - - - channel.message_cancel(destination="consumer_tag") - channel.queue_delete(queue="delete-me-3", if_unused=True) - #check that it has gone by declaring passively: - try: - channel.queue_declare(queue="delete-me-3", passive=True) - self.fail("Queue has not been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - - def test_autodelete_shared(self): - """ - Test auto-deletion (of non-exclusive queues) - """ - channel = self.channel - other = self.connect() - channel2 = other.channel(1) - channel2.session_open() - - channel.queue_declare(queue="auto-delete-me", auto_delete=True) - - #consume from both channels - reply = channel.basic_consume(queue="auto-delete-me") - channel2.basic_consume(queue="auto-delete-me") - - #implicit cancel - channel2.session_close() - - #check it is still there - channel.queue_declare(queue="auto-delete-me", passive=True) - - #explicit cancel => queue is now unused again: - channel.basic_cancel(consumer_tag=reply.consumer_tag) - - #NOTE: this assumes there is no timeout in use - - #check that it has gone be declaring passively - try: - channel.queue_declare(queue="auto-delete-me", passive=True) - self.fail("Expected queue to have been deleted") - except Closed, e: - self.assertChannelException(404, e.args[0]) - - diff --git a/qpid/python/tests_0-10_preview/testlib.py b/qpid/python/tests_0-10_preview/testlib.py deleted file mode 100644 index a0355c4ce0..0000000000 --- a/qpid/python/tests_0-10_preview/testlib.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# 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. -# - -# -# Tests for the testlib itself. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from Queue import Empty - -import sys -from traceback import * - -def mytrace(frame, event, arg): - print_stack(frame); - print "====" - return mytrace - -class TestBaseTest(TestBase): - """Verify TestBase functions work as expected""" - - def testAssertEmptyPass(self): - """Test assert empty works""" - self.queue_declare(queue="empty") - q = self.consume("empty") - self.assertEmpty(q) - try: - q.get(timeout=1) - self.fail("Queue is not empty.") - except Empty: None # Ignore - - def testAssertEmptyFail(self): - self.queue_declare(queue="full") - q = self.consume("full") - self.channel.message_transfer(content=Content("", properties={'routing_key':"full"})) - try: - self.assertEmpty(q); - self.fail("assertEmpty did not assert on non-empty queue") - except AssertionError: None # Ignore - - def testMessageProperties(self): - """Verify properties are passed with message""" - props={"x":1, "y":2} - self.queue_declare(queue="q") - q = self.consume("q") - self.assertPublishGet(q, routing_key="q", properties=props) - - - diff --git a/qpid/python/tests_0-10_preview/tx.py b/qpid/python/tests_0-10_preview/tx.py deleted file mode 100644 index 3fd1065af3..0000000000 --- a/qpid/python/tests_0-10_preview/tx.py +++ /dev/null @@ -1,231 +0,0 @@ -# -# 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. -# -from qpid.client import Client, Closed -from qpid.queue import Empty -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class TxTests(TestBase): - """ - Tests for 'methods' on the amqp tx 'class' - """ - - def test_commit(self): - """ - Test that commited publishes are delivered and commited acks are not re-delivered - """ - channel2 = self.client.channel(2) - channel2.session_open() - self.perform_txn_work(channel2, "tx-commit-a", "tx-commit-b", "tx-commit-c") - channel2.tx_commit() - channel2.session_close() - - #use a different channel with new subscriptions to ensure - #there is no redelivery of acked messages: - channel = self.channel - channel.tx_select() - - self.subscribe(channel, queue="tx-commit-a", destination="qa", confirm_mode=1) - queue_a = self.client.queue("qa") - - self.subscribe(channel, queue="tx-commit-b", destination="qb", confirm_mode=1) - queue_b = self.client.queue("qb") - - self.subscribe(channel, queue="tx-commit-c", destination="qc", confirm_mode=1) - queue_c = self.client.queue("qc") - - #check results - for i in range(1, 5): - msg = queue_c.get(timeout=1) - self.assertEqual("TxMessage %d" % i, msg.content.body) - msg.complete() - - msg = queue_b.get(timeout=1) - self.assertEqual("TxMessage 6", msg.content.body) - msg.complete() - - msg = queue_a.get(timeout=1) - self.assertEqual("TxMessage 7", msg.content.body) - msg.complete() - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - #cleanup - channel.tx_commit() - - def test_auto_rollback(self): - """ - Test that a channel closed with an open transaction is effectively rolled back - """ - channel2 = self.client.channel(2) - channel2.session_open() - queue_a, queue_b, queue_c = self.perform_txn_work(channel2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - channel2.session_close() - channel = self.channel - channel.tx_select() - - self.subscribe(channel, queue="tx-autorollback-a", destination="qa", confirm_mode=1) - queue_a = self.client.queue("qa") - - self.subscribe(channel, queue="tx-autorollback-b", destination="qb", confirm_mode=1) - queue_b = self.client.queue("qb") - - self.subscribe(channel, queue="tx-autorollback-c", destination="qc", confirm_mode=1) - queue_c = self.client.queue("qc") - - #check results - for i in range(1, 5): - msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - msg.complete() - - msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - msg.complete() - - msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - msg.complete() - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - #cleanup - channel.tx_commit() - - def test_rollback(self): - """ - Test that rolled back publishes are not delivered and rolled back acks are re-delivered - """ - channel = self.channel - queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - #stop subscriptions (ensures no delivery occurs during rollback as messages are requeued) - for d in ["sub_a", "sub_b", "sub_c"]: - channel.message_stop(destination=d) - - channel.tx_rollback() - - #restart susbcriptions - for d in ["sub_a", "sub_b", "sub_c"]: - channel.message_flow(destination=d, unit=0, value=0xFFFFFFFF) - channel.message_flow(destination=d, unit=1, value=0xFFFFFFFF) - - #check results - for i in range(1, 5): - msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - msg.complete() - - msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - msg.complete() - - msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - msg.complete() - - for q in [queue_a, queue_b, queue_c]: - try: - extra = q.get(timeout=1) - self.fail("Got unexpected message: " + extra.content.body) - except Empty: None - - #cleanup - channel.tx_commit() - - def perform_txn_work(self, channel, name_a, name_b, name_c): - """ - Utility method that does some setup and some work under a transaction. Used for testing both - commit and rollback - """ - #setup: - channel.queue_declare(queue=name_a, exclusive=True, auto_delete=True) - channel.queue_declare(queue=name_b, exclusive=True, auto_delete=True) - channel.queue_declare(queue=name_c, exclusive=True, auto_delete=True) - - key = "my_key_" + name_b - topic = "my_topic_" + name_c - - channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key) - channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic) - - for i in range(1, 5): - channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"msg%d" % i}, body="Message %d" % i)) - - channel.message_transfer(destination="amq.direct", - content=Content(properties={'routing_key':key, 'message_id':"msg6"}, body="Message 6")) - channel.message_transfer(destination="amq.topic", - content=Content(properties={'routing_key':topic, 'message_id':"msg7"}, body="Message 7")) - - channel.tx_select() - - #consume and ack messages - self.subscribe(channel, queue=name_a, destination="sub_a", confirm_mode=1) - queue_a = self.client.queue("sub_a") - for i in range(1, 5): - msg = queue_a.get(timeout=1) - self.assertEqual("Message %d" % i, msg.content.body) - - msg.complete() - - self.subscribe(channel, queue=name_b, destination="sub_b", confirm_mode=1) - queue_b = self.client.queue("sub_b") - msg = queue_b.get(timeout=1) - self.assertEqual("Message 6", msg.content.body) - msg.complete() - - sub_c = self.subscribe(channel, queue=name_c, destination="sub_c", confirm_mode=1) - queue_c = self.client.queue("sub_c") - msg = queue_c.get(timeout=1) - self.assertEqual("Message 7", msg.content.body) - msg.complete() - - #publish messages - for i in range(1, 5): - channel.message_transfer(destination="amq.topic", - content=Content(properties={'routing_key':topic, 'message_id':"tx-msg%d" % i}, - body="TxMessage %d" % i)) - - channel.message_transfer(destination="amq.direct", - content=Content(properties={'routing_key':key, 'message_id':"tx-msg6"}, - body="TxMessage 6")) - channel.message_transfer(content=Content(properties={'routing_key':name_a, 'message_id':"tx-msg7"}, - body="TxMessage 7")) - return queue_a, queue_b, queue_c -- cgit v1.2.1 From 5fdf51377ac57b621016c044325445b9726b3c2b Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Thu, 1 May 2008 14:36:03 +0000 Subject: applying Ted Ross's patch to handle unicode encoding and management message ordering issues git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@652535 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 51 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 3f8d3c9cfe..3595fdfc3c 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -99,8 +99,6 @@ class managementChannel: ssn.queue_declare (queue=self.topicName, exclusive=True, auto_delete=True) ssn.queue_declare (queue=self.replyName, exclusive=True, auto_delete=True) - ssn.exchange_bind (exchange="qpid.management", - queue=self.topicName, binding_key="mgmt.#") ssn.exchange_bind (exchange="amq.direct", queue=self.replyName, binding_key=self.replyName) ssn.message_subscribe (queue=self.topicName, destination="tdest") @@ -383,7 +381,7 @@ class managementClient: elif typecode == 5: data = codec.read_uint8 () elif typecode == 6: - data = codec.read_str8 () + data = str (codec.read_str8 ()) elif typecode == 7: data = codec.read_vbin32 () elif typecode == 8: # ABSTIME @@ -422,10 +420,13 @@ class managementClient: if ch.reqsOutstanding == 0: if self.ctrlCb != None: self.ctrlCb (ch.context, self.CTRL_SCHEMA_LOADED, None) + ch.ssn.exchange_bind (exchange="qpid.management", + queue=ch.topicName, binding_key="mgmt.#") + def handleMethodReply (self, ch, codec, sequence): status = codec.read_uint32 () - sText = codec.read_str8 () + sText = str (codec.read_str8 ()) data = self.seqMgr.release (sequence) if data == None: @@ -461,7 +462,7 @@ class managementClient: def handleCommandComplete (self, ch, codec, seq): code = codec.read_uint32 () - text = codec.read_str8 () + text = str (codec.read_str8 ()) data = (seq, code, text) context = self.seqMgr.release (seq) if context == "outstanding": @@ -488,7 +489,7 @@ class managementClient: ch.send ("qpid.management", smsg) def handlePackageInd (self, ch, codec): - pname = codec.read_str8 () + pname = str (codec.read_str8 ()) if pname not in self.packages: self.packages[pname] = {} @@ -502,8 +503,8 @@ class managementClient: ch.send ("qpid.management", smsg) def handleClassInd (self, ch, codec): - pname = codec.read_str8 () - cname = codec.read_str8 () + pname = str (codec.read_str8 ()) + cname = str (codec.read_str8 ()) hash = codec.read_bin128 () if pname not in self.packages: return @@ -523,8 +524,8 @@ class managementClient: def parseSchema (self, ch, codec): """ Parse a received schema-description message. """ self.decOutstanding (ch) - packageName = codec.read_str8 () - className = codec.read_str8 () + packageName = str (codec.read_str8 ()) + className = str (codec.read_str8 ()) hash = codec.read_bin128 () configCount = codec.read_uint16 () instCount = codec.read_uint16 () @@ -550,7 +551,7 @@ class managementClient: for idx in range (configCount): ft = codec.read_map () - name = ft["name"] + name = str (ft["name"]) type = ft["type"] access = ft["access"] index = ft["index"] @@ -562,7 +563,7 @@ class managementClient: for key, value in ft.items (): if key == "unit": - unit = value + unit = str (value) elif key == "min": min = value elif key == "max": @@ -570,42 +571,42 @@ class managementClient: elif key == "maxlen": maxlen = value elif key == "desc": - desc = value + desc = str (value) config = (name, type, unit, desc, access, index, min, max, maxlen) configs.append (config) for idx in range (instCount): ft = codec.read_map () - name = ft["name"] + name = str (ft["name"]) type = ft["type"] unit = None desc = None for key, value in ft.items (): if key == "unit": - unit = value + unit = str (value) elif key == "desc": - desc = value + desc = str (value) inst = (name, type, unit, desc) insts.append (inst) for idx in range (methodCount): ft = codec.read_map () - mname = ft["name"] + mname = str (ft["name"]) argCount = ft["argCount"] if "desc" in ft: - mdesc = ft["desc"] + mdesc = str (ft["desc"]) else: mdesc = None args = [] for aidx in range (argCount): ft = codec.read_map () - name = ft["name"] + name = str (ft["name"]) type = ft["type"] - dir = ft["dir"].upper () + dir = str (ft["dir"].upper ()) unit = None min = None max = None @@ -615,7 +616,7 @@ class managementClient: for key, value in ft.items (): if key == "unit": - unit = value + unit = str (value) elif key == "min": min = value elif key == "max": @@ -623,9 +624,9 @@ class managementClient: elif key == "maxlen": maxlen = value elif key == "desc": - desc = value + desc = str (value) elif key == "default": - default = value + default = str (value) arg = (name, type, dir, unit, desc, min, max, maxlen, default) args.append (arg) @@ -648,8 +649,8 @@ class managementClient: if cls == 'I' and self.instCb == None: return - packageName = codec.read_str8 () - className = codec.read_str8 () + packageName = str (codec.read_str8 ()) + className = str (codec.read_str8 ()) hash = codec.read_bin128 () classKey = (packageName, className, hash) -- cgit v1.2.1 From d36df4fb1dab7af3b5e2de772dcf1b0a8289b0f9 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 5 May 2008 16:57:31 +0000 Subject: Updated for latest 0-10 spec and added two extra queue options to set the flow to disk policy trigger size/count git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@653527 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 03a0fd8538..e916ee0f6c 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -28,6 +28,7 @@ from threading import Condition from qpid.management import managementClient from qpid.peer import Closed from qpid.connection import Connection +from qpid.datatypes import uuid4 from qpid.util import connect from time import sleep @@ -38,9 +39,14 @@ _host = "localhost" _durable = False _fileCount = 8 _fileSize = 24 +_maxQueueSize = None +_maxQueueCount= None + FILECOUNT = "qpid.file_count" FILESIZE = "qpid.file_size" +MAX_QUEUE_SIZE = "qpid.max_size" +MAX_QUEUE_COUNT = "qpid.max_count" def Usage (): print "Usage: qpid-config [OPTIONS]" @@ -65,6 +71,8 @@ def Usage (): print " --durable Queue is durable" print " --file-count N (8) Number of files in queue's persistence journal" print " --file-size N (24) File size in pages (64Kib/page)" + print " --max-queue-size N Maximum in-memory queue size as bytes" + print " --max-queue-count N Maximum in-memory queue size as a number of messages" print print "Add Exchange Options:" print " --durable Exchange is durable" @@ -100,6 +108,7 @@ class BrokerManager: self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) self.conn.start () + self.session = self.conn.session(str(uuid4())) self.mclient = managementClient (self.spec) self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) except socket.error, e: @@ -224,7 +233,7 @@ class BrokerManager: ename = args[1] try: - self.channel.exchange_declare (exchange=ename, type=etype, durable=_durable) + self.session.exchange_declare (exchange=ename, type=etype, durable=_durable) except Closed, e: print "Failed:", e @@ -235,7 +244,7 @@ class BrokerManager: ename = args[0] try: - self.channel.exchange_delete (exchange=ename) + self.session.exchange_delete (exchange=ename) except Closed, e: print "Failed:", e @@ -249,8 +258,13 @@ class BrokerManager: declArgs[FILECOUNT] = _fileCount declArgs[FILESIZE] = _fileSize + if _maxQueueSize: + declArgs[MAX_QUEUE_SIZE] = _maxQueueSize + if _maxQueueCount: + declArgs[MAX_QUEUE_COUNT] = _maxQueueCount + try: - self.channel.queue_declare (queue=qname, durable=_durable, arguments=declArgs) + self.session.queue_declare (queue=qname, durable=_durable, arguments=declArgs) except Closed, e: print "Failed:", e @@ -261,7 +275,7 @@ class BrokerManager: qname = args[0] try: - self.channel.queue_delete (queue=qname) + self.session.queue_delete (queue=qname) except Closed, e: print "Failed:", e @@ -276,7 +290,7 @@ class BrokerManager: key = args[2] try: - self.channel.queue_bind (queue=qname, exchange=ename, routing_key=key) + self.session.exchange_bind (queue=qname, exchange=ename, routing_key=key) except Closed, e: print "Failed:", e @@ -291,7 +305,7 @@ class BrokerManager: key = args[2] try: - self.channel.queue_unbind (queue=qname, exchange=ename, routing_key=key) + self.session.exchange_unbind (queue=qname, exchange=ename, routing_key=key) except Closed, e: print "Failed:", e @@ -318,7 +332,7 @@ def YN (bool): ## try: - longOpts = ("durable", "spec-file=", "bindings", "broker-addr=", "file-count=", "file-size=") + longOpts = ("durable", "spec-file=", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=") (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:a:b", longOpts) except: Usage () @@ -336,6 +350,10 @@ for opt in optlist: _fileCount = int (opt[1]) if opt[0] == "--file-size": _fileSize = int (opt[1]) + if opt[0] == "--max-queue-size": + _maxQueueSize = int (opt[1]) + if opt[0] == "--max-queue-count": + _maxQueueCount = int (opt[1]) nargs = len (cargs) bm = BrokerManager () -- cgit v1.2.1 From e6ced35fbdd3385e2a660f5b40366f0382796d01 Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Tue, 6 May 2008 16:24:46 +0000 Subject: This is a fix for QPID-1031 I added read/write methods for datetime that calls int64. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@653830 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 1c76666de1..68573921d7 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -102,12 +102,16 @@ class Codec(Packer): def write_int64(self, n): self.pack("!q", n) + def read_datetime(self): + return self.read_uint64() + def write_datetime(self, n): + self.write_uint64(n) + def read_double(self): return self.unpack("!d") def write_double(self, d): self.pack("!d", d) - def read_vbin8(self): return self.read(self.read_uint8()) def write_vbin8(self, b): -- cgit v1.2.1 From 495e58750c29364c6b7f48210a55eca32e3340c8 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 6 May 2008 18:31:11 +0000 Subject: QPID-1033: made loading of the spec file not fail if the results cannot be cached, e.g. due to an unwritable directory git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@653875 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec010.py | 14 +++++++++++--- qpid/python/tests/spec010.py | 12 ++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 03f60edc4e..1668729876 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -621,6 +621,7 @@ class Loader: def load(xml): fname = xml + ".pcl" + if os.path.exists(fname) and mtime(fname) > mtime(__file__): file = open(fname, "r") s = cPickle.load(file) @@ -630,7 +631,14 @@ def load(xml): s = doc["amqp"].dispatch(Loader()) s.register() s.resolve() - file = open(fname, "w") - cPickle.dump(s, file) - file.close() + + try: + file = open(fname, "w") + except IOError: + file = None + + if file: + cPickle.dump(s, file) + file.close() + return s diff --git a/qpid/python/tests/spec010.py b/qpid/python/tests/spec010.py index ff29bd8cea..b08ad5e925 100644 --- a/qpid/python/tests/spec010.py +++ b/qpid/python/tests/spec010.py @@ -17,6 +17,7 @@ # under the License. # +import os, tempfile, shutil, stat from unittest import TestCase from qpid.spec010 import load from qpid.codec010 import Codec, StringCodec @@ -70,3 +71,14 @@ class SpecTest(TestCase): xid.encode(sc, st) assert sc.encoded == '\x00\x00\x00\x10\x06\x04\x07\x00\x00\x00\x00\x00\x03gid\x03bid' assert xid.decode(sc).__dict__ == st.__dict__ + + def testLoadReadOnly(self): + spec = "amqp.0-10-qpid-errata.xml" + f = testrunner.get_spec_file(spec) + dest = tempfile.mkdtemp() + shutil.copy(f, dest) + shutil.copy(os.path.join(os.path.dirname(f), "amqp.0-10.dtd"), dest) + os.chmod(dest, stat.S_IRUSR | stat.S_IXUSR) + fname = os.path.join(dest, spec) + load(fname) + assert not os.path.exists("%s.pcl" % fname) -- cgit v1.2.1 From be27b4690096741eab9bc5a78628fc5a01585a6e Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Tue, 6 May 2008 20:19:22 +0000 Subject: This patch was attached to QPID-953. It allows to specify a comma separated list of queue names to filter with -f flag. Also I removed getopt and added optparse as it provides a more easy way of handling CLI functionality including a free help function. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@653904 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-queue-stats | 76 ++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 32 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index ff28e5b50c..813c6e0cd2 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -20,8 +20,9 @@ # import os -import getopt +import optparse import sys +import re import socket import qpid from threading import Condition @@ -31,21 +32,6 @@ from qpid.connection import Connection from qpid.util import connect from time import sleep -defspecpath = "/usr/share/amqp/amqp.0-10.xml" -specpath = defspecpath -host = "localhost" - -def Usage (): - print "Usage: qpid-queue-stats [OPTIONS]" - print - print "Options:" - print " -a default: localhost" - print " broker-addr is in the form: hostname | ip-address [:]" - print " ex: localhost, 10.1.1.7:10000, broker-host:10000" - print " -s default:", defspecpath - print - sys.exit (1) - class Broker: def __init__ (self, text): colon = text.find (":") @@ -78,13 +64,15 @@ class BrokerManager: self.src = None self.broker = None self.objects = {} + self.filter = None + self.specpath="/usr/share/amqp/amqp.0-10.xml" def SetBroker (self, broker): self.broker = broker def ConnectToBroker (self): try: - self.spec = qpid.spec.load (specpath) + self.spec = qpid.spec.load (self.specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) self.conn.start () @@ -94,6 +82,12 @@ class BrokerManager: print "Connect Error:", e exit (1) + def setFilter(self,filter): + self.filter = filter + + def setSpecpath(self,spec): + self.specpath = spec + def Disconnect (self): self.mclient.removeChannel (self.mchannel) @@ -120,6 +114,16 @@ class BrokerManager: self.objects[obj.id] = (name, obj, None) return + if len(self.filter) > 0 : + match = False + + for x in self.filter: + if x.match(name): + match = True + break + if match == False: + return + if last == None: lastSample = first else: @@ -148,19 +152,27 @@ class BrokerManager: ## ## Main Program ## +def main(): + p = optparse.OptionParser() + p.add_option('--broker-address','-a', default='localhost' , help='broker-addr is in the form: hostname | ip-address [:] \n ex: localhost, 10.1.1.7:10000, broker-host:10000') + p.add_option('--amqp-spec-file','-s', default='"/usr/share/amqp/amqp.0-10.xml', help='the path to the amqp spec file') + p.add_option('--filter','-f' ,default=None ,help='a list of comma separated queue names (regex are accepted) to show') + + options, arguments = p.parse_args() + + host = options.broker_address + specpath = options.amqp_spec_file + filter = [] + if options.filter != None: + for s in options.filter.split(","): + filter.append(re.compile(s)) + + bm = BrokerManager () + bm.SetBroker (Broker (host)) + bm.setSpecpath(specpath) + bm.setFilter(filter) + bm.Display() + +if __name__ == '__main__': + main() -try: - (optlist, cargs) = getopt.getopt (sys.argv[1:], "s:a:") -except: - Usage () - -for opt in optlist: - if opt[0] == "-s": - specpath = opt[1] - if opt[0] == "-a": - host = opt[1] - -nargs = len (cargs) -bm = BrokerManager () -bm.SetBroker (Broker (host)) -bm.Display () -- cgit v1.2.1 From 5829e87a54c3bb8972ea993882976f188d866587 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 7 May 2008 16:03:08 +0000 Subject: QPID-979: added convenience accessors for headers git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654158 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/request-response/server.py | 21 ++++------ qpid/python/qpid/datatypes.py | 31 ++++++++++++++- qpid/python/tests/datatypes.py | 53 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 14 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 71c3161495..4377571248 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -7,29 +7,24 @@ import qpid import sys import os -from random import randint from qpid.util import connect from qpid.connection import Connection from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty #----- Functions ------------------------------------------- -def getProperty(msg, name): - for h in msg.headers: - if hasattr(h, name): return getattr(h, name) - return None - def respond(session, request): # The routing key for the response is the request's reply-to # property. The body for the response is the request's body, # converted to upper case. - reply_to = getProperty(request,"reply_to") + message_properties = request.get("message_properties") + reply_to = message_properties.reply_to if reply_to == None: - raise Exception("reply to property needs to be there") - - props = session.delivery_properties(routing_key=reply_to["routing_key"]) + raise Exception("reply to property needs to be there") + + props = session.delivery_properties(routing_key=reply_to["routing_key"]) session.message_transfer(reply_to["exchange"],None, None, Message(props,request.body.upper())) #----- Initialization -------------------------------------- @@ -40,16 +35,16 @@ port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 user="guest" password="guest" amqp_spec="" - + try: amqp_spec = os.environ["AMQP_SPEC"] except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" - + # Create a connection. conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) conn.start() - + session_id = str(uuid4()) session = conn.session(session_id) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 36b290700b..ba9eaa053f 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -72,11 +72,40 @@ class Message: else: self.body = None if len(args) > 1: - self.headers = args[:-1] + self.headers = list(args[:-1]) else: self.headers = None self.id = None + def has(self, name): + return self.get(name) != None + + def get(self, name): + if self.headers: + for h in self.headers: + if h._type.name == name: + return h + return None + + def set(self, header): + if self.headers is None: + self.headers = [] + idx = 0 + while idx < len(self.headers): + if self.headers[idx]._type == header._type: + self.headers[idx] = header + return + idx += 1 + self.headers.append(header) + + def clear(self, name): + idx = 0 + while idx < len(self.headers): + if self.headers[idx]._type.name == name: + del self.headers[idx] + return + idx += 1 + def __repr__(self): args = [] if self.headers: diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index e4d6723d95..5c6ee82283 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -18,6 +18,8 @@ # from unittest import TestCase +from qpid.testlib import testrunner +from qpid.spec010 import load from qpid.datatypes import * class RangedSetTest(TestCase): @@ -109,3 +111,54 @@ class UUIDTest(TestCase): u = uuid4() for i in xrange(1024): assert u != uuid4() + +class MessageTest(TestCase): + + def setUp(self): + self.spec = load(testrunner.get_spec_file("amqp.0-10-qpid-errata.xml")) + self.mp = Struct(self.spec["message.message_properties"]) + self.dp = Struct(self.spec["message.delivery_properties"]) + self.fp = Struct(self.spec["message.fragment_properties"]) + + def testHas(self): + m = Message(self.mp, self.dp, self.fp, "body") + assert m.has("message_properties") + assert m.has("delivery_properties") + assert m.has("fragment_properties") + + def testGet(self): + m = Message(self.mp, self.dp, self.fp, "body") + assert m.get("message_properties") == self.mp + assert m.get("delivery_properties") == self.dp + assert m.get("fragment_properties") == self.fp + + def testSet(self): + m = Message(self.mp, self.dp, "body") + assert m.get("fragment_properties") is None + m.set(self.fp) + assert m.get("fragment_properties") == self.fp + + def testSetOnEmpty(self): + m = Message("body") + assert m.get("delivery_properties") is None + m.set(self.dp) + assert m.get("delivery_properties") == self.dp + + def testSetReplace(self): + m = Message(self.mp, self.dp, self.fp, "body") + dp = Struct(self.spec["message.delivery_properties"]) + assert m.get("delivery_properties") == self.dp + assert m.get("delivery_properties") != dp + m.set(dp) + assert m.get("delivery_properties") != self.dp + assert m.get("delivery_properties") == dp + + def testClear(self): + m = Message(self.mp, self.dp, self.fp, "body") + assert m.get("message_properties") == self.mp + assert m.get("delivery_properties") == self.dp + assert m.get("fragment_properties") == self.fp + m.clear("fragment_properties") + assert m.get("fragment_properties") is None + assert m.get("message_properties") == self.mp + assert m.get("delivery_properties") == self.dp -- cgit v1.2.1 From f57945fc290b3599c0c292930189945dcc30b0aa Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 8 May 2008 20:52:28 +0000 Subject: QPID-979: added access to enums through the session so that symbolic constants can be used rather than hard coded ones; also added default loading of the spec git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654618 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 11 ++- qpid/python/qpid/invoker.py | 26 ++++-- qpid/python/qpid/session.py | 12 +-- qpid/python/qpid/spec.py | 13 +++ qpid/python/qpid/spec010.py | 16 ++++ qpid/python/tests_0-10/alternate_exchange.py | 12 +-- qpid/python/tests_0-10/broker.py | 16 ++-- qpid/python/tests_0-10/dtx.py | 12 +-- qpid/python/tests_0-10/example.py | 4 +- qpid/python/tests_0-10/exchange.py | 4 +- qpid/python/tests_0-10/message.py | 114 +++++++++++++-------------- qpid/python/tests_0-10/persistence.py | 4 +- qpid/python/tests_0-10/queue.py | 16 ++-- qpid/python/tests_0-10/tx.py | 8 +- 14 files changed, 156 insertions(+), 112 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index dc72cd9cb8..39f882e9c3 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -25,7 +25,8 @@ from assembler import Assembler, Segment from codec010 import StringCodec from session import Session from invoker import Invoker -from spec010 import Control, Command +from spec010 import Control, Command, load +from spec import default from exceptions import * from logging import getLogger import delegates @@ -44,8 +45,10 @@ def server(*args): class Connection(Assembler): - def __init__(self, sock, spec, delegate=client): + def __init__(self, sock, spec=None, delegate=client): Assembler.__init__(self, sock) + if spec == None: + spec = load(default()) self.spec = spec self.track = self.spec["track"] @@ -162,9 +165,9 @@ class Channel(Invoker): def resolve_method(self, name): inst = self.connection.spec.instructions.get(name) if inst is not None and isinstance(inst, Control): - return inst + return self.METHOD, inst else: - return None + return self.ERROR, None def invoke(self, type, args, kwargs): ctl = type.new(args, kwargs) diff --git a/qpid/python/qpid/invoker.py b/qpid/python/qpid/invoker.py index 9e6f6943d8..2d9e45179e 100644 --- a/qpid/python/qpid/invoker.py +++ b/qpid/python/qpid/invoker.py @@ -17,16 +17,26 @@ # under the License. # +# TODO: need a better naming for this class now that it does the value +# stuff class Invoker: - def resolve_method(self, name): - pass - - def __getattr__(self, name): - resolved = self.resolve_method(name) - if resolved == None: - raise AttributeError("%s instance has no attribute '%s'" % - (self.__class__.__name__, name)) + def METHOD(self, name, resolved): method = lambda *args, **kwargs: self.invoke(resolved, args, kwargs) self.__dict__[name] = method return method + + def VALUE(self, name, resolved): + self.__dict__[name] = resolved + return resolved + + def ERROR(self, name, resolved): + raise AttributeError("%s instance has no attribute '%s'" % + (self.__class__.__name__, name)) + + def resolve_method(self, name): + return ERROR, None + + def __getattr__(self, name): + disp, resolved = self.resolve_method(name) + return disp(name, resolved) diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 11249ca435..f8ac98b96e 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -112,15 +112,17 @@ class Session(Invoker): def resolve_method(self, name): cmd = self.spec.instructions.get(name) if cmd is not None and cmd.track == self.spec["track.command"].value: - return cmd + return self.METHOD, cmd else: # XXX for st in self.spec.structs.values(): if st.name == name: - return st - if self.spec.structs_by_name.has_key(name): - return self.spec.structs_by_name[name] - return None + return self.METHOD, st + if self.spec.structs_by_name.has_key(name): + return self.METHOD, self.spec.structs_by_name[name] + if self.spec.enums.has_key(name): + return self.VALUE, self.spec.enums[name] + return self.ERROR, None def invoke(self, type, args, kwargs): # XXX diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 64a14b0f61..152763b762 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -31,6 +31,19 @@ situations. import os, mllib, spec08, spec010 +def default(): + try: + specfile = os.environ["AMQP_SPEC"] + return specfile + except KeyError: + try: + from AMQP_SPEC import location as specfile + return specfile + except ImportError: + raise Exception("unable to locate the amqp specification, please set " + "the AMQP_SPEC environment variable or supply a " + "configured AMQP_SPEC.py") + def load(specfile, *errata): for name in (specfile,) + errata: if not os.path.exists(name): diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 1668729876..fb625eab65 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -166,6 +166,15 @@ class Domain(Type, Lookup): def decode(self, codec): return self.type.decode(codec) +class Enum: + + def __init__(self, name): + self.name = name + + def __repr__(self): + return "%s(%s)" % (self.name, ", ".join([k for k in self.__dict__.keys() + if k != "name"])) + class Choice(Named, Node): def __init__(self, name, value, children): @@ -177,6 +186,12 @@ class Choice(Named, Node): Named.register(self, node) node.choices[self.value] = self Node.register(self) + try: + enum = node.spec.enums[node.name] + except KeyError: + enum = Enum(node.name) + node.spec.enums[node.name] = enum + setattr(enum, self.name, self.value) class Composite(Type, Coded): @@ -450,6 +465,7 @@ class Spec(Node): self.commands = {} self.structs = {} self.structs_by_name = {} + self.enums = {} def encoding(self, klass): if Spec.ENCODINGS.has_key(klass): diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index c177c3deb7..9cf331c110 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -41,16 +41,16 @@ class AlternateExchangeTests(TestBase010): session.queue_declare(queue="returns", exclusive=True, auto_delete=True) session.exchange_bind(queue="returns", exchange="secondary") session.message_subscribe(destination="a", queue="returns") - session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="a", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFF) returned = session.incoming("a") #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages session.queue_declare(queue="processed", exclusive=True, auto_delete=True) session.exchange_bind(queue="processed", exchange="primary", binding_key="my-key") session.message_subscribe(destination="b", queue="processed") - session.message_flow(destination="b", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="b", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="b", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="b", unit=session.credit_unit.byte, value=0xFFFFFFFF) processed = session.incoming("b") #publish to the primary exchange @@ -81,8 +81,8 @@ class AlternateExchangeTests(TestBase010): session.queue_declare(queue="deleted", exclusive=True, auto_delete=True) session.exchange_bind(exchange="dlq", queue="deleted") session.message_subscribe(destination="dlq", queue="deleted") - session.message_flow(destination="dlq", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="dlq", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="dlq", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="dlq", unit=session.credit_unit.byte, value=0xFFFFFFFF) dlq = session.incoming("dlq") #create a queue using the dlq as its alternate exchange: diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index 25cf1241ec..d4aa57765c 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -36,8 +36,8 @@ class BrokerTests(TestBase010): # No ack consumer ctag = "tag1" session.message_subscribe(queue = "myqueue", destination = ctag) - session.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) - session.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFF) body = "test no-ack" session.message_transfer(message=Message(session.delivery_properties(routing_key="myqueue"), body)) msg = session.incoming(ctag).get(timeout = 5) @@ -47,8 +47,8 @@ class BrokerTests(TestBase010): session.queue_declare(queue = "otherqueue", exclusive=True, auto_delete=True) ctag = "tag2" session.message_subscribe(queue = "otherqueue", destination = ctag, accept_mode = 1) - session.message_flow(destination=ctag, unit=0, value=0xFFFFFFFF) - session.message_flow(destination=ctag, unit=1, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFF) body = "test ack" session.message_transfer(message=Message(session.delivery_properties(routing_key="otherqueue"), body)) msg = session.incoming(ctag).get(timeout = 5) @@ -64,8 +64,8 @@ class BrokerTests(TestBase010): session.exchange_bind(queue="test-queue", exchange="amq.fanout") consumer_tag = "tag1" session.message_subscribe(queue="test-queue", destination=consumer_tag) - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = consumer_tag) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = consumer_tag) queue = session.incoming(consumer_tag) body = "Immediate Delivery" @@ -86,8 +86,8 @@ class BrokerTests(TestBase010): consumer_tag = "tag1" session.message_subscribe(queue="test-queue", destination=consumer_tag) - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = consumer_tag) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = consumer_tag) queue = session.incoming(consumer_tag) msg = queue.get(timeout=5) self.assert_(msg.body == body) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 042df521ae..531f8a8f66 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -493,8 +493,8 @@ class DtxTests(TestBase010): session2.dtx_select() session2.dtx_start(xid=tx) session2.message_subscribe(queue="dummy", destination="dummy") - session2.message_flow(destination="dummy", unit=0, value=1) - session2.message_flow(destination="dummy", unit=1, value=0xFFFFFFFF) + session2.message_flow(destination="dummy", unit=session2.credit_unit.message, value=1) + session2.message_flow(destination="dummy", unit=session2.credit_unit.byte, value=0xFFFFFFFF) msg = session2.incoming("dummy").get(timeout=1) session2.message_accept(RangedSet(msg.id)) session2.message_cancel(destination="dummy") @@ -629,8 +629,8 @@ class DtxTests(TestBase010): def swap(self, session, src, dest): #consume from src: session.message_subscribe(destination="temp-swap", queue=src) - session.message_flow(destination="temp-swap", unit=0, value=1) - session.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="temp-swap", unit=session.credit_unit.message, value=1) + session.message_flow(destination="temp-swap", unit=session.credit_unit.byte, value=0xFFFFFFFF) msg = session.incoming("temp-swap").get(timeout=1) session.message_cancel(destination="temp-swap") session.message_accept(RangedSet(msg.id)) @@ -646,8 +646,8 @@ class DtxTests(TestBase010): def assertMessageId(self, expected, queue): self.session.message_subscribe(queue=queue, destination="results") - self.session.message_flow(destination="results", unit=0, value=1) - self.session.message_flow(destination="results", unit=1, value=0xFFFFFFFF) + self.session.message_flow(destination="results", unit=self.session.credit_unit.message, value=1) + self.session.message_flow(destination="results", unit=self.session.credit_unit.byte, value=0xFFFFFFFF) self.assertEqual(expected, self.getMessageProperty(self.session.incoming("results").get(timeout=1), 'correlation_id')) self.session.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index 1e140a285d..83d208192b 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -69,8 +69,8 @@ class ExampleTest (TestBase010): # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. session.message_subscribe(queue="test-queue", destination="consumer_tag") - session.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) # We can use the session.incoming(...) method to access the messages # delivered for our consumer_tag. diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 991da17ed4..4b5dc78143 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -108,8 +108,8 @@ class TestHelper(TestBase010): else: self.uniqueTag += 1 consumer_tag = "tag" + str(self.uniqueTag) self.session.message_subscribe(queue=queueName, destination=consumer_tag) - self.session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) - self.session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + self.session.message_flow(destination=consumer_tag, unit=self.session.credit_unit.message, value=0xFFFFFFFF) + self.session.message_flow(destination=consumer_tag, unit=self.session.credit_unit.byte, value=0xFFFFFFFF) return self.session.incoming(consumer_tag) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index c54622e091..5f2ee7264f 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -229,8 +229,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-4"), "One")) session.message_subscribe(destination="my-consumer", queue="test-queue-4") - session.message_flow(destination="my-consumer", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="my-consumer", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="my-consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="my-consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) #should flush here @@ -258,8 +258,8 @@ class MessageTests(TestBase010): session.queue_declare(queue="test-ack-queue", auto_delete=True) session.message_subscribe(queue = "test-ack-queue", destination = "consumer") - session.message_flow(destination="consumer", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="consumer", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) queue = session.incoming("consumer") delivery_properties = session.delivery_properties(routing_key="test-ack-queue") @@ -289,8 +289,8 @@ class MessageTests(TestBase010): session.close(timeout=10) session = self.session - session.message_flow(destination="checker", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="checker", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFF) queue = session.incoming("checker") msg3b = queue.get(timeout=1) @@ -504,16 +504,16 @@ class MessageTests(TestBase010): session.exchange_bind(queue = "r", exchange = "amq.fanout") session.message_subscribe(queue = "q", destination = "consumer") - session.message_flow(destination="consumer", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="consumer", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "blah, blah")) msg = session.incoming("consumer").get(timeout = 1) self.assertEquals(msg.body, "blah, blah") session.message_reject(RangedSet(msg.id)) session.message_subscribe(queue = "r", destination = "checker") - session.message_flow(destination="checker", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="checker", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFF) msg = session.incoming("checker").get(timeout = 1) self.assertEquals(msg.body, "blah, blah") @@ -532,9 +532,9 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) - session.message_flow(unit = 0, value = 5, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 5, destination = "c") #set infinite byte credit - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = session.incoming("c") for i in range(1, 6): @@ -543,7 +543,7 @@ class MessageTests(TestBase010): #increase credit again and check more are received for i in range(6, 11): - session.message_flow(unit = 0, value = 1, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 1, destination = "c") self.assertDataEquals(session, q.get(timeout = 1), "Message %d" % i) self.assertEmpty(q) @@ -565,9 +565,9 @@ class MessageTests(TestBase010): msg_size = 21 #set byte credit to finite amount (less than enough for all messages) - session.message_flow(unit = 1, value = msg_size*5, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") #set infinite message credit - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = session.incoming("c") for i in range(5): @@ -576,7 +576,7 @@ class MessageTests(TestBase010): #increase credit again and check more are received for i in range(5): - session.message_flow(unit = 1, value = msg_size, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = msg_size, destination = "c") self.assertDataEquals(session, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) @@ -596,9 +596,9 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %d" % i)) #set message credit to finite amount (less than enough for all messages) - session.message_flow(unit = 0, value = 5, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 5, destination = "c") #set infinite byte credit - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = session.incoming("c") for i in range(1, 6): @@ -634,9 +634,9 @@ class MessageTests(TestBase010): msg_size = 19 #set byte credit to finite amount (less than enough for all messages) - session.message_flow(unit = 1, value = msg_size*5, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") #set infinite message credit - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "c") #check that expected number were received q = session.incoming("c") msgs = [] @@ -665,11 +665,11 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %s" % i)) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "a") - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") session.message_subscribe(queue = "q", destination = "b", acquire_mode = 1) - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "b") - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") for i in range(6, 11): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %s" % i)) @@ -700,8 +700,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) - session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="a", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFF) msg = session.incoming("a").get(timeout = 1) self.assertEquals("acquire me", msg.body) #message should still be on the queue: @@ -726,8 +726,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "release me")) session.message_subscribe(queue = "q", destination = "a") - session.message_flow(destination="a", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="a", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFF) msg = session.incoming("a").get(timeout = 1) self.assertEquals("release me", msg.body) session.message_cancel(destination = "a") @@ -746,8 +746,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "released message %s" % (i))) session.message_subscribe(queue = "q", destination = "a") - session.message_flow(unit = 0, value = 10, destination = "a") - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") queue = session.incoming("a") first = queue.get(timeout = 1) for i in range(2, 10): @@ -779,8 +779,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(delivery_properties, "message %s" % (i))) session.message_subscribe(queue = "q", destination = "a") - session.message_flow(unit = 0, value = 10, destination = "a") - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") queue = session.incoming("a") ids = [] for i in range (1, 11): @@ -805,8 +805,8 @@ class MessageTests(TestBase010): session.close(timeout=10) session = self.session - session.message_flow(destination="checker", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="checker", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFF) queue = session.incoming("checker") self.assertEquals("message 4", queue.get(timeout = 1).body) @@ -823,8 +823,8 @@ class MessageTests(TestBase010): #consume some of them session.message_subscribe(queue = "q", destination = "a") session.message_set_flow_mode(flow_mode = 0, destination = "a") - session.message_flow(unit = 0, value = 5, destination = "a") - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 5, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") queue = session.incoming("a") for i in range(1, 6): @@ -839,11 +839,11 @@ class MessageTests(TestBase010): #now create a not-acquired subscriber session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") #check it gets those not consumed queue = session.incoming("b") - session.message_flow(unit = 0, value = 1, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 1, destination = "b") for i in range(6, 11): msg = queue.get(timeout = 1) self.assertEquals("message-%d" % (i), msg.body) @@ -851,7 +851,7 @@ class MessageTests(TestBase010): #TODO: tidy up completion session.receiver._completed.add(msg.id) session.channel.session_completed(session.receiver._completed) - session.message_flow(unit = 0, value = 1, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 1, destination = "b") self.assertEmpty(queue) #check all 'browsed' messages are still on the queue @@ -867,8 +867,8 @@ class MessageTests(TestBase010): #create a not-acquired subscriber session.message_subscribe(queue = "q", destination = "a", acquire_mode=1) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - session.message_flow(unit = 0, value = 10, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") #browse through messages queue = session.incoming("a") @@ -889,8 +889,8 @@ class MessageTests(TestBase010): #create a second not-acquired subscriber session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - session.message_flow(unit = 0, value = 1, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 1, destination = "b") #check it gets those not consumed queue = session.incoming("b") for i in [2,4,6,8,10]: @@ -899,7 +899,7 @@ class MessageTests(TestBase010): session.message_release(RangedSet(msg.id)) session.receiver._completed.add(msg.id) session.channel.session_completed(session.receiver._completed) - session.message_flow(unit = 0, value = 1, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 1, destination = "b") self.assertEmpty(queue) #check all 'browsed' messages are still on the queue @@ -916,13 +916,13 @@ class MessageTests(TestBase010): #create two 'browsers' session.message_subscribe(queue = "q", destination = "a", acquire_mode=1) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - session.message_flow(unit = 0, value = 10, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") queueA = session.incoming("a") session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "b") - session.message_flow(unit = 0, value = 10, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "b") queueB = session.incoming("b") #have each browser release the message @@ -938,8 +938,8 @@ class MessageTests(TestBase010): #create consumer session.message_subscribe(queue = "q", destination = "c") - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "c") - session.message_flow(unit = 0, value = 10, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "c") queueC = session.incoming("c") #consume the message then ack it msgC = queueC.get(timeout = 1) @@ -950,12 +950,12 @@ class MessageTests(TestBase010): def test_no_size(self): self.queue_declare(queue = "q", exclusive=True, auto_delete=True) - ch = self.session - ch.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) + ssn = self.session + ssn.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) - ch.message_subscribe(queue = "q", destination="d", confirm_mode = 0) - ch.message_flow(unit = 0, value = 0xFFFFFFFF, destination = "d") - ch.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "d") + ssn.message_subscribe(queue = "q", destination="d", confirm_mode = 0) + ssn.message_flow(unit = ssn.credit_unit.message, value = 0xFFFFFFFF, destination = "d") + ssn.message_flow(unit = ssn.credit_unit.byte, value = 0xFFFFFFFF, destination = "d") queue = session.incoming("d") msg = queue.get(timeout = 3) @@ -969,8 +969,8 @@ class MessageTests(TestBase010): consumer_tag = "tag1" session.message_subscribe(queue="xyz", destination=consumer_tag) - session.message_flow(unit = 0, value = 0xFFFFFFFF, destination = consumer_tag) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = consumer_tag) queue = session.incoming(consumer_tag) msg = queue.get(timeout=1) self.assertEquals("", msg.body) diff --git a/qpid/python/tests_0-10/persistence.py b/qpid/python/tests_0-10/persistence.py index a4b5691910..815ad1f3dc 100644 --- a/qpid/python/tests_0-10/persistence.py +++ b/qpid/python/tests_0-10/persistence.py @@ -49,8 +49,8 @@ class PersistenceTests(TestBase010): #create consumer session.message_subscribe(queue = "q", destination = "a", accept_mode = 1, acquire_mode=0) - session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") - session.message_flow(unit = 0, value = 10, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") queue = session.incoming("a") #consume the message, cancel subscription (triggering auto-delete), then ack it diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 97e7a92b87..a3b23a1c32 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -49,8 +49,8 @@ class QueueTests(TestBase010): #send a further message and consume it, ensuring that the other messages are really gone session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "four")) session.message_subscribe(queue="test-queue", destination="tag") - session.message_flow(destination="tag", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="tag", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="tag", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) queue = session.incoming("tag") msg = queue.get(timeout=1) self.assertEqual("four", msg.body) @@ -166,11 +166,11 @@ class QueueTests(TestBase010): session.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) session.message_subscribe(queue="queue-1", destination="queue-1") - session.message_flow(destination="queue-1", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="queue-1", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="queue-1", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="queue-1", unit=session.credit_unit.byte, value=0xFFFFFFFF) session.message_subscribe(queue="queue-2", destination="queue-2") - session.message_flow(destination="queue-2", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="queue-2", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="queue-2", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="queue-2", unit=session.credit_unit.byte, value=0xFFFFFFFF) queue1 = session.incoming("queue-1") queue2 = session.incoming("queue-2") @@ -267,8 +267,8 @@ class QueueTests(TestBase010): #empty queue: session.message_subscribe(destination="consumer_tag", queue="delete-me-2") - session.message_flow(destination="consumer_tag", unit=0, value=0xFFFFFFFF) - session.message_flow(destination="consumer_tag", unit=1, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) queue = session.incoming("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.body) diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index 5aef2b00e8..da162d54ec 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -251,13 +251,13 @@ class TxTests(TestBase010): session = session or self.session consumer_tag = keys["destination"] session.message_subscribe(**keys) - session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) - session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=session.credit_unit.byte, value=0xFFFFFFFF) def enable_flow(self, tag, session=None): session = session or self.session - session.message_flow(destination=tag, unit=0, value=0xFFFFFFFF) - session.message_flow(destination=tag, unit=1, value=0xFFFFFFFF) + session.message_flow(destination=tag, unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination=tag, unit=session.credit_unit.byte, value=0xFFFFFFFF) def complete(self, session, msg): session.receiver._completed.add(msg.id)#TODO: this may be done automatically -- cgit v1.2.1 From 40b3ed4bed15924896496998fd6a41c35465bf8c Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 8 May 2008 21:20:43 +0000 Subject: QPID-979: switched to a more appropriate name for locating the spec git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654623 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index 152763b762..e6d914044c 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -33,16 +33,16 @@ import os, mllib, spec08, spec010 def default(): try: - specfile = os.environ["AMQP_SPEC"] - return specfile + amqp_spec = os.environ["AMQP_SPEC"] + return amqp_spec except KeyError: try: - from AMQP_SPEC import location as specfile - return specfile + from qpid_config import amqp_spec + return amqp_spec except ImportError: raise Exception("unable to locate the amqp specification, please set " - "the AMQP_SPEC environment variable or supply a " - "configured AMQP_SPEC.py") + "the AMQP_SPEC environment variable or supply " + "qpid_config.py on the PYTHONPATH") def load(specfile, *errata): for name in (specfile,) + errata: -- cgit v1.2.1 From a268f4a4dac7ad02c62706e556d82db231aee0b5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 8 May 2008 21:44:13 +0000 Subject: QPID-979: added qpid_config.py appropriate for devel checkout git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654637 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid_config.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 qpid/python/qpid_config.py (limited to 'qpid/python') diff --git a/qpid/python/qpid_config.py b/qpid/python/qpid_config.py new file mode 100644 index 0000000000..8f987e9962 --- /dev/null +++ b/qpid/python/qpid_config.py @@ -0,0 +1,23 @@ +# +# 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 os + +qpid_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +amqp_spec = os.path.join(qpid_home, "specs", "amqp.0-10-qpid-errata.xml") -- cgit v1.2.1 From 367283cac64f43994c6932d39e954cc02027b9d0 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 9 May 2008 07:45:50 +0000 Subject: Make ANONYMOUS the default mechanism git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654712 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index c958313671..f31d9a0f09 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -129,7 +129,7 @@ class Client(Delegate): self.connection.read_header() def connection_start(self, ch, start): - ch.connection_start_ok(client_properties=Client.PROPERTIES) + ch.connection_start_ok(client_properties=Client.PROPERTIES, mechanism="ANONYMOUS") def connection_tune(self, ch, tune): ch.connection_tune_ok() -- cgit v1.2.1 From 859f4e37a8b5b5f42861f5abec992251ff942db3 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 9 May 2008 09:29:03 +0000 Subject: QPID-1042: ensure delievery record is kept where accept_mode=not-required, acquire_mode=not-acquired and flow_mode=credit git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654737 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 5f2ee7264f..e21716d855 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -689,6 +689,32 @@ class MessageTests(TestBase010): #messages should still be on the queue: self.assertEquals(10, session.queue_query(queue = "q").message_count) + def test_acquire_with_no_accept_and_credit_flow(self): + """ + Test that messages recieved unacquired, with accept not + required in windowing mode can be acquired. + """ + session = self.session + session.queue_declare(queue = "q", exclusive=True, auto_delete=True) + + session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) + + session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1, accept_mode = 1) + session.message_set_flow_mode(flow_mode = session.flow_mode.credit, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + msg = session.incoming("a").get(timeout = 1) + self.assertEquals("acquire me", msg.body) + #message should still be on the queue: + self.assertEquals(1, session.queue_query(queue = "q").message_count) + + transfers = RangedSet(msg.id) + response = session.message_acquire(transfers) + #check that we get notification (i.e. message_acquired) + self.assert_(msg.id in response.transfers) + #message should have been removed from the queue: + self.assertEquals(0, session.queue_query(queue = "q").message_count) + def test_acquire(self): """ Test explicit acquire function @@ -696,7 +722,6 @@ class MessageTests(TestBase010): session = self.session session.queue_declare(queue = "q", exclusive=True, auto_delete=True) - #use fanout for now: session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) -- cgit v1.2.1 From 6e2a6d0e63a615a8c40d173effd0619b5c6989d8 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 9 May 2008 11:15:35 +0000 Subject: Enabled PLAIN authentication and setting of username and password for 0-10 python client. Added options to all command line tools to allow a username and password to be specified. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654759 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 22 +++++----------------- qpid/python/commands/qpid-queue-stats | 20 ++++---------------- qpid/python/commands/qpid-route | 21 ++++----------------- qpid/python/qpid/connection.py | 11 ++++++++--- qpid/python/qpid/delegates.py | 12 +++++++++++- qpid/python/qpid/managementdata.py | 22 ++++++++++++++-------- qpid/python/qpid/testlib.py | 3 ++- 7 files changed, 48 insertions(+), 63 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index e916ee0f6c..8f2fb7ff1b 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -26,6 +26,7 @@ import socket import qpid from threading import Condition from qpid.management import managementClient +from qpid.managementdata import Broker from qpid.peer import Closed from qpid.connection import Connection from qpid.datatypes import uuid4 @@ -62,8 +63,8 @@ def Usage (): print "Options:" print " -b [ --bindings ] Show bindings in queue or exchange list" print " -a [ --broker-addr ] Address (localhost) Address of qpidd broker" - print " broker-addr is in the form: hostname | ip-address [:]" - print " ex: localhost, 10.1.1.7:10000, broker-host:10000" + print " broker-addr is in the form: [username/password@] hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print " -s [ --spec-file] Path (" + _defspecpath + ")" print " AMQP specification file" print @@ -79,20 +80,6 @@ def Usage (): print sys.exit (1) -class Broker: - def __init__ (self, text): - colon = text.find (":") - if colon == -1: - host = text - self.port = 5672 - else: - host = text[:colon] - self.port = int (text[colon+1:]) - self.host = socket.gethostbyname (host) - - def name (self): - return self.host + ":" + str (self.port) - class BrokerManager: def __init__ (self): self.dest = None @@ -106,7 +93,8 @@ class BrokerManager: try: self.spec = qpid.spec.load (_specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) + self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, + username=self.broker.username, password=self.broker.password) self.conn.start () self.session = self.conn.session(str(uuid4())) self.mclient = managementClient (self.spec) diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 813c6e0cd2..8771c7dc5d 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -27,25 +27,12 @@ import socket import qpid from threading import Condition from qpid.management import managementClient +from qpid.managementdata import Broker from qpid.peer import Closed from qpid.connection import Connection from qpid.util import connect from time import sleep -class Broker: - def __init__ (self, text): - colon = text.find (":") - if colon == -1: - host = text - self.port = 5672 - else: - host = text[:colon] - self.port = int (text[colon+1:]) - self.host = socket.gethostbyname (host) - - def name (self): - return self.host + ":" + str (self.port) - class mgmtObject (object): """ Generic object that holds the contents of a management object with its attributes set as object attributes. """ @@ -74,7 +61,8 @@ class BrokerManager: try: self.spec = qpid.spec.load (self.specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) + self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, + username=self.broker.username, password=self.broker.password) self.conn.start () self.mclient = managementClient (self.spec, None, self.configCb, self.instCb) self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) @@ -154,7 +142,7 @@ class BrokerManager: ## def main(): p = optparse.OptionParser() - p.add_option('--broker-address','-a', default='localhost' , help='broker-addr is in the form: hostname | ip-address [:] \n ex: localhost, 10.1.1.7:10000, broker-host:10000') + p.add_option('--broker-address','-a', default='localhost' , help='broker-addr is in the form: [username/password@] hostname | ip-address [:] \n ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost') p.add_option('--amqp-spec-file','-s', default='"/usr/share/amqp/amqp.0-10.xml', help='the path to the amqp spec file') p.add_option('--filter','-f' ,default=None ,help='a list of comma separated queue names (regex are accepted) to show') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index b08293fa00..baa45a320f 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -25,6 +25,7 @@ import socket import qpid import os from qpid.management import managementClient +from qpid.managementdata import Broker from qpid.peer import Closed from qpid.connection import Connection from qpid.util import connect @@ -41,8 +42,8 @@ def Usage (): print " -v [ --verbose ] Verbose output" print " -q [ --quiet ] Quiet output, don't print duplicate warnings" print - print " dest-broker and src-broker are in the form: hostname | ip-address [:]" - print " ex: localhost, 10.1.1.7:10000, broker-host:10000" + print " dest-broker and src-broker are in the form: [username/password@] hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print #print " If loading the route configuration from a file, the input file has one line per route" #print " in the form:" @@ -55,20 +56,6 @@ _specpath = "/usr/share/amqp/amqp.0-10.xml" _verbose = False _quiet = False -class Broker: - def __init__ (self, text): - colon = text.find (":") - if colon == -1: - host = text - self.port = 5672 - else: - host = text[:colon] - self.port = int (text[colon+1:]) - self.host = socket.gethostbyname (host) - - def name (self): - return self.host + ":" + str (self.port) - class RouteManager: def __init__ (self, destBroker): self.dest = Broker (destBroker) @@ -81,7 +68,7 @@ class RouteManager: try: self.spec = qpid.spec.load (_specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (broker.host, broker.port), self.spec) + self.conn = Connection (connect (broker.host, broker.port), self.spec, username=broker.username, password=broker.password) self.conn.start () self.mclient = managementClient (self.spec) self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 39f882e9c3..4ed430249b 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -37,6 +37,8 @@ class ChannelsBusy(Exception): pass class SessionBusy(Exception): pass +class ConnectionFailed(Exception): pass + def client(*args): return delegates.Client(*args) @@ -45,7 +47,7 @@ def server(*args): class Connection(Assembler): - def __init__(self, sock, spec=None, delegate=client): + def __init__(self, sock, spec=None, delegate=client, **args): Assembler.__init__(self, sock) if spec == None: spec = load(default()) @@ -58,13 +60,14 @@ class Connection(Assembler): self.condition = Condition() self.opened = False + self.failed = False self.thread = Thread(target=self.run) self.thread.setDaemon(True) self.channel_max = 65535 - self.delegate = delegate(self) + self.delegate = delegate(self, args) def attach(self, name, ch, delegate, force=False): self.lock.acquire() @@ -127,8 +130,10 @@ class Connection(Assembler): def start(self, timeout=None): self.delegate.start() self.thread.start() - if not wait(self.condition, lambda: self.opened, timeout): + if not wait(self.condition, lambda: self.opened or self.failed, timeout): raise Timeout() + if (self.failed): + raise ConnectionFailed() def run(self): # XXX: we don't really have a good way to exit this loop without diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index f31d9a0f09..cdff132219 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -52,6 +52,9 @@ class Delegate: def connection_close(self, ch, close): ch.connection_close_ok() self.connection.sock.close() + if not self.connection.opened: + self.connection.failed = True + notify(self.connection.condition) def connection_close_ok(self, ch, close_ok): self.connection.opened = False @@ -124,12 +127,19 @@ class Client(Delegate): "version": "development", "platform": os.name} + def __init__(self, connection, args={}): + Delegate.__init__(self, connection) + self.username = args.get('username', 'guest') + self.password = args.get('password', 'guest') + self.mechanism = args.get('mechanism', 'PLAIN') + def start(self): self.connection.write_header(self.spec.major, self.spec.minor) self.connection.read_header() def connection_start(self, ch, start): - ch.connection_start_ok(client_properties=Client.PROPERTIES, mechanism="ANONYMOUS") + r = "\0%s\0%s" % (self.username, self.password) + ch.connection_start_ok(client_properties=Client.PROPERTIES, mechanism=self.mechanism, response=r) def connection_tune(self, ch, tune): ch.connection_tune_ok() diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index c908483354..a0197ba7d2 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -20,6 +20,7 @@ # import qpid +import re import socket import struct import os @@ -32,14 +33,18 @@ from qpid.util import connect class Broker: def __init__ (self, text): - colon = text.find (":") - if colon == -1: - host = text - self.port = 5672 - else: - host = text[:colon] - self.port = int (text[colon+1:]) + rex = re.compile(r""" + # [ [ / ] @] [ : ] + ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) + match = rex.match(text) + if not match: raise ValueError("'%s' is not a valid broker url" % (text)) + user, password, host, port = match.groups() + self.host = socket.gethostbyname (host) + if port: self.port = int(port) + else: self.port = 5672 + self.username = user or "guest" + self.password = password or "guest" def name (self): return self.host + ":" + str (self.port) @@ -174,7 +179,8 @@ class ManagementData: self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.broker = Broker (host) - self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec) + self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, + username=self.broker.username, password=self.broker.password) self.conn.start () self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index adda1a650f..b5aa59f586 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -353,7 +353,8 @@ class TestBase010(unittest.TestCase): def setUp(self): spec = testrunner.spec - self.conn = Connection(connect(testrunner.host, testrunner.port), spec) + self.conn = Connection(connect(testrunner.host, testrunner.port), spec, + username=testrunner.user, password=testrunner.password) self.conn.start(timeout=10) self.session = self.conn.session("test-session", timeout=10) -- cgit v1.2.1 From a91fe82ffdb32f0db050c8c071379281295e5ca8 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 9 May 2008 18:40:13 +0000 Subject: QPID-1045: always notify incoming message queues of session closure and provide API for notifying listeners of closure; also preserve connection close code and report in errors git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654907 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 31 +++++++++++++------ qpid/python/qpid/delegates.py | 17 +++++------ qpid/python/qpid/exceptions.py | 1 + qpid/python/qpid/framer.py | 3 +- qpid/python/qpid/queue.py | 21 ++++++++----- qpid/python/qpid/session.py | 44 +++++++++++++++----------- qpid/python/tests/connection.py | 68 +++++++++++++++++++++++++++++++++++++++-- 7 files changed, 138 insertions(+), 47 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 4ed430249b..8d0e115458 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -19,8 +19,7 @@ import datatypes, session from threading import Thread, Condition, RLock -from util import wait -from framer import Closed +from util import wait, notify from assembler import Assembler, Segment from codec010 import StringCodec from session import Session @@ -39,11 +38,11 @@ class SessionBusy(Exception): pass class ConnectionFailed(Exception): pass -def client(*args): - return delegates.Client(*args) +def client(*args, **kwargs): + return delegates.Client(*args, **kwargs) -def server(*args): - return delegates.Server(*args) +def server(*args, **kwargs): + return delegates.Server(*args, **kwargs) class Connection(Assembler): @@ -61,13 +60,14 @@ class Connection(Assembler): self.condition = Condition() self.opened = False self.failed = False + self.close_code = (None, "connection aborted") self.thread = Thread(target=self.run) self.thread.setDaemon(True) self.channel_max = 65535 - self.delegate = delegate(self, args) + self.delegate = delegate(self, **args) def attach(self, name, ch, delegate, force=False): self.lock.acquire() @@ -101,6 +101,8 @@ class Connection(Assembler): ssn = self.sessions.pop(name, None) if ssn is not None: ssn.channel = None + ssn.closed() + notify(ssn.condition) return ssn finally: self.lock.release() @@ -127,13 +129,23 @@ class Connection(Assembler): finally: self.lock.release() + def detach_all(self): + self.lock.acquire() + try: + for ssn in self.attached.values(): + if self.close_code[0] != 200: + ssn.exceptions.append(self.close_code) + self.detach(ssn.name, ssn.channel) + finally: + self.lock.release() + def start(self, timeout=None): self.delegate.start() self.thread.start() if not wait(self.condition, lambda: self.opened or self.failed, timeout): raise Timeout() - if (self.failed): - raise ConnectionFailed() + if self.failed: + raise ConnectionFailed(*self.close_code) def run(self): # XXX: we don't really have a good way to exit this loop without @@ -142,6 +154,7 @@ class Connection(Assembler): try: seg = self.read_segment() except Closed: + self.detach_all() break self.delegate.received(seg) diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index cdff132219..6e6b55e36a 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -50,6 +50,7 @@ class Delegate: ssn.received(seg) def connection_close(self, ch, close): + self.connection.close_code = (close.reply_code, close.reply_text) ch.connection_close_ok() self.connection.sock.close() if not self.connection.opened: @@ -73,13 +74,11 @@ class Delegate: notify(ch.session.condition) def session_detach(self, ch, d): - self.connection.detach(d.name, ch) + ssn = self.connection.detach(d.name, ch) ch.session_detached(d.name) def session_detached(self, ch, d): - ssn = self.connection.detach(d.name, ch) - if ssn is not None: - notify(ch.session.condition) + self.connection.detach(d.name, ch) def session_command_point(self, ch, cp): ssn = ch.session @@ -127,11 +126,11 @@ class Client(Delegate): "version": "development", "platform": os.name} - def __init__(self, connection, args={}): - Delegate.__init__(self, connection) - self.username = args.get('username', 'guest') - self.password = args.get('password', 'guest') - self.mechanism = args.get('mechanism', 'PLAIN') + def __init__(self, connection, username="guest", password="guest", mechanism="PLAIN"): + Delegate.__init__(self, connection) + self.username = username + self.password = password + self.mechanism = mechanism def start(self): self.connection.write_header(self.spec.major, self.spec.minor) diff --git a/qpid/python/qpid/exceptions.py b/qpid/python/qpid/exceptions.py index 2136793d3b..7eaaf81ed4 100644 --- a/qpid/python/qpid/exceptions.py +++ b/qpid/python/qpid/exceptions.py @@ -17,4 +17,5 @@ # under the License. # +class Closed(Exception): pass class Timeout(Exception): pass diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index 78a29235cb..27ea3287f0 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -18,6 +18,7 @@ # import struct, socket +from exceptions import Closed from packer import Packer from threading import Lock from logging import getLogger @@ -66,8 +67,6 @@ class Frame: self.channel, self.payload) -class Closed(Exception): pass - class FramingError(Exception): pass class Framer(Packer): diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index ea8f00d091..a8a5c0d9ad 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -25,8 +25,7 @@ content of a queue can be notified if the queue is no longer in use. from Queue import Queue as BaseQueue, Empty, Full from threading import Thread - -class Closed(Exception): pass +from exceptions import Closed class Queue(BaseQueue): @@ -37,6 +36,7 @@ class Queue(BaseQueue): BaseQueue.__init__(self, *args, **kwargs) self.error = None self.listener = None + self.exc_listener = None self.thread = None def close(self, error = None): @@ -53,15 +53,20 @@ class Queue(BaseQueue): else: return result - def listen(self, listener): + def listen(self, listener, exc_listener = None): + if listener is None and exc_listener is not None: + raise ValueError("cannot set exception listener without setting listener") + self.listener = listener - if listener == None: - if self.thread != None: + self.exc_listener = exc_listener + + if listener is None: + if self.thread is not None: self.put(Queue.STOP) self.thread.join() self.thread = None else: - if self.thread == None: + if self.thread is None: self.thread = Thread(target = self.run) self.thread.setDaemon(True) self.thread.start() @@ -72,5 +77,7 @@ class Queue(BaseQueue): o = self.get() if o == Queue.STOP: break self.listener(o) - except Closed: + except Closed, e: + if self.exc_listener is not None: + self.exc_listener(e) break diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index f8ac98b96e..a1103e0428 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -52,7 +52,8 @@ class Session(Invoker): self.timeout = timeout self.channel = None self.invoke_lock = Lock() - self.closed = False + self._closing = False + self._closed = False self.condition = Condition() @@ -82,7 +83,9 @@ class Session(Invoker): def error(self): exc = self.exceptions[:] - if len(exc) == 1: + if len(exc) == 0: + return None + elif len(exc) == 1: return exc[0] else: return tuple(exc) @@ -102,13 +105,31 @@ class Session(Invoker): def close(self, timeout=None): self.invoke_lock.acquire() try: - self.closed = True + self._closing = True self.channel.session_detach(self.name) finally: self.invoke_lock.release() if not wait(self.condition, lambda: self.channel is None, timeout): raise Timeout() + def closed(self): + self.lock.acquire() + try: + if self._closed: return + self._closed = True + + error = self.error() + for id in self.results: + f = self.results[id] + f.error(error) + self.results.clear() + + for q in self._incoming.values(): + q.close(error) + notify(self.condition) + finally: + self.lock.release() + def resolve_method(self, name): cmd = self.spec.instructions.get(name) if cmd is not None and cmd.track == self.spec["track.command"].value: @@ -136,7 +157,7 @@ class Session(Invoker): self.invoke_lock.release() def do_invoke(self, type, args, kwargs): - if self.closed: + if self._closing: raise SessionClosed() if self.channel == None: @@ -311,20 +332,7 @@ class Delegate: future.set(er.value) def execution_exception(self, ex): - self.session.lock.acquire() - try: - self.session.exceptions.append(ex) - error = self.session.error() - for id in self.session.results: - f = self.session.results[id] - f.error(error) - self.session.results.clear() - - for q in self.session._incoming.values(): - q.close(error) - notify(self.session.condition) - finally: - self.session.lock.release() + self.session.exceptions.append(ex) class Client(Delegate): diff --git a/qpid/python/tests/connection.py b/qpid/python/tests/connection.py index 6925480ed3..88620bc1c6 100644 --- a/qpid/python/tests/connection.py +++ b/qpid/python/tests/connection.py @@ -51,8 +51,16 @@ class TestSession(Delegate): def queue_query(self, qq): return qq._type.result.type.new((qq.queue,), {}) - def message_transfer(self, cmd, header, body): - self.queue.put((cmd, header, body)) + def message_transfer(self, cmd, headers, body): + if cmd.destination == "echo": + m = Message(body) + m.headers = headers + self.session.message_transfer(cmd.destination, cmd.accept_mode, + cmd.acquire_mode, m) + elif cmd.destination == "abort": + self.session.channel.connection.sock.close() + else: + self.queue.put((cmd, headers, body)) class ConnectionTest(TestCase): @@ -134,3 +142,59 @@ class ConnectionTest(TestCase): qq = ssn.queue_query("asdf") assert qq.queue == "asdf" c.close(5) + + def testCloseGet(self): + c = Connection(connect("0.0.0.0", PORT), self.spec) + c.start(10) + ssn = c.session("test", timeout=10) + echos = ssn.incoming("echo") + + for i in range(10): + ssn.message_transfer("echo", message=Message("test%d" % i)) + + ssn.auto_sync=False + ssn.message_transfer("abort") + + for i in range(10): + m = echos.get(timeout=10) + assert m.body == "test%d" % i + + try: + m = echos.get(timeout=10) + assert False + except Closed, e: + pass + + def testCloseListen(self): + c = Connection(connect("0.0.0.0", PORT), self.spec) + c.start(10) + ssn = c.session("test", timeout=10) + echos = ssn.incoming("echo") + + messages = [] + exceptions = [] + condition = Condition() + def listener(m): messages.append(m) + def exc_listener(e): + exceptions.append(e) + condition.acquire() + condition.notify() + condition.release() + + echos.listen(listener, exc_listener) + + for i in range(10): + ssn.message_transfer("echo", message=Message("test%d" % i)) + + ssn.auto_sync=False + ssn.message_transfer("abort") + + condition.acquire() + condition.wait(10) + condition.release() + + for i in range(10): + m = messages.pop(0) + assert m.body == "test%d" % i + + assert len(exceptions) == 1 -- cgit v1.2.1 From 09bd97be6159c787ee49bf995a43990742780d0e Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 9 May 2008 19:26:28 +0000 Subject: QPID-1045 and QPID-1041: added a destination attribute to incoming queues, and added a start() method to incoming queues as syntactic sugar for the verbose message flow idiom git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654918 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 13 ++++++++++++- qpid/python/qpid/spec010.py | 10 ++++++++-- qpid/python/tests_0-10/message.py | 18 +++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index a1103e0428..aa0446b941 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -75,7 +75,7 @@ class Session(Invoker): try: queue = self._incoming.get(destination) if queue == None: - queue = Queue() + queue = Incoming(self, destination) self._incoming[destination] = queue return queue finally: @@ -319,6 +319,17 @@ class Sender: for range in commands.ranges: self._completed.add(range.lower, range.upper) +class Incoming(Queue): + + def __init__(self, session, destination): + Queue.__init__(self) + self.session = session + self.destination = destination + + def start(self): + for unit in self.session.credit_unit.values(): + self.session.message_flow(self.destination, unit, 0xFFFFFFFF) + class Delegate: def __init__(self, session): diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index fb625eab65..58d305aa6c 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -170,10 +170,14 @@ class Enum: def __init__(self, name): self.name = name + self._names = () + self._values = () + + def values(self): + return self._values def __repr__(self): - return "%s(%s)" % (self.name, ", ".join([k for k in self.__dict__.keys() - if k != "name"])) + return "%s(%s)" % (self.name, ", ".join(self._names)) class Choice(Named, Node): @@ -192,6 +196,8 @@ class Choice(Named, Node): enum = Enum(node.name) node.spec.enums[node.name] = enum setattr(enum, self.name, self.value) + enum._names += (self.name,) + enum._values += (self.value,) class Composite(Type, Coded): diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index e21716d855..8f3d7bdaef 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -229,6 +229,7 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-4"), "One")) session.message_subscribe(destination="my-consumer", queue="test-queue-4") + myqueue = session.incoming("my-consumer") session.message_flow(destination="my-consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) session.message_flow(destination="my-consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) @@ -237,7 +238,6 @@ class MessageTests(TestBase010): #cancel should stop messages being delivered session.message_cancel(destination="my-consumer") session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue-4"), "Two")) - myqueue = session.incoming("my-consumer") msg = myqueue.get(timeout=1) self.assertEqual("One", msg.body) try: @@ -1001,6 +1001,22 @@ class MessageTests(TestBase010): self.assertEquals("", msg.body) session.message_accept(RangedSet(msg.id)) + def test_incoming_start(self): + q = "test_incoming_start" + session = self.session + + session.queue_declare(queue=q, exclusive=True, auto_delete=True) + session.message_subscribe(queue=q, destination="msgs") + messages = session.incoming("msgs") + assert messages.destination == "msgs" + + dp = session.delivery_properties(routing_key=q) + session.message_transfer(message=Message(dp, "test")) + + messages.start() + msg = messages.get() + assert msg.body == "test" + def assertDataEquals(self, session, msg, expected): self.assertEquals(expected, msg.body) -- cgit v1.2.1 From 1f2d86d3dd25801f742831901e34317f5623e113 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 9 May 2008 21:41:24 +0000 Subject: QPID-947: made python client use execution.sync instead of session.flush when not in auto_sync mode git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@654947 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index aa0446b941..a77ef91c0f 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -93,7 +93,8 @@ class Session(Invoker): def sync(self, timeout=None): if currentThread() == self.channel.connection.thread: raise SessionException("deadlock detected") - self.channel.session_flush(completed=True) + if not self.auto_sync: + self.execution_sync() last = self.sender.next_id - 1 if not wait(self.condition, lambda: last in self.sender._completed or self.exceptions, @@ -250,6 +251,9 @@ class Session(Invoker): if result is not INCOMPLETE: for seg in segments: self.receiver.completed(seg) + # XXX: don't forget to obey sync for manual completion as well + if hdr.sync: + self.channel.session_completed(self.receiver._completed) def send(self, seg): self.sender.send(seg) -- cgit v1.2.1 From 193d13307baba2a3a2ede3682755f29860f5ed02 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 12 May 2008 15:37:10 +0000 Subject: QPID-947: added handler for known_completed and generate known_completed when timely-reply is set git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655533 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/datatypes.py | 8 ++++++++ qpid/python/qpid/delegates.py | 5 +++++ qpid/python/qpid/session.py | 10 ++++++++++ qpid/python/tests/datatypes.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index ba9eaa053f..7bea1ccc19 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -142,6 +142,14 @@ class Range: def span(self, r): return Range(min(self.lower, r.lower), max(self.upper, r.upper)) + def intersect(self, r): + lower = max(self.lower, r.lower) + upper = min(self.upper, r.upper) + if lower > upper: + return None + else: + return Range(lower, upper) + def __repr__(self): return "%s-%s" % (self.lower, self.upper) diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 6e6b55e36a..a284fb3b59 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -87,8 +87,13 @@ class Delegate: def session_completed(self, ch, cmp): ch.session.sender.completed(cmp.commands) + if cmp.timely_reply: + ch.session_known_completed(cmp.commands) notify(ch.session.condition) + def session_known_completed(self, ch, kn_cmp): + ch.session.receiver.known_completed(kn_cmp.commands) + def session_flush(self, ch, f): rcv = ch.session.receiver if f.expected: diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index a77ef91c0f..d5b4a75b62 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -289,6 +289,16 @@ class Receiver: if seg.last: self._completed.add(seg.id) + def known_completed(self, commands): + completed = RangeSet() + for c in self._completed.ranges: + for kc in commands.ranges: + if c.lower in kc and c.upper in kc: + break + else: + completed.add_range(c) + self._completed = completed + class Sender: def __init__(self, session): diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index 5c6ee82283..6d8703ed4f 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -103,6 +103,34 @@ class RangedSetTest(TestCase): assert range.lower == 0 assert range.upper == 8 +class RangeTest(TestCase): + + def testIntersect1(self): + a = Range(0, 10) + b = Range(9, 20) + i1 = a.intersect(b) + i2 = b.intersect(a) + assert i1.upper == 10 + assert i2.upper == 10 + assert i1.lower == 9 + assert i2.lower == 9 + + def testIntersect2(self): + a = Range(0, 10) + b = Range(11, 20) + assert a.intersect(b) == None + assert b.intersect(a) == None + + def testIntersect3(self): + a = Range(0, 10) + b = Range(3, 5) + i1 = a.intersect(b) + i2 = b.intersect(a) + assert i1.upper == 5 + assert i2.upper == 5 + assert i1.lower == 3 + assert i2.lower == 3 + class UUIDTest(TestCase): def test(self): -- cgit v1.2.1 From b6ae7a7eb71bdefa1dded98a221de32a956962c2 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 12 May 2008 15:39:36 +0000 Subject: QPID-947: fixed typo in prior commit git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655534 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index d5b4a75b62..6dc64b4f06 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -290,7 +290,7 @@ class Receiver: self._completed.add(seg.id) def known_completed(self, commands): - completed = RangeSet() + completed = RangedSet() for c in self._completed.ranges: for kc in commands.ranges: if c.lower in kc and c.upper in kc: -- cgit v1.2.1 From b9622a65fdfc0846e620b4ef508488d75d6e6ee5 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 12 May 2008 17:04:07 +0000 Subject: QPID-1050: Patch from Ted Ross: 1) Durability for federation links (broker-to-broker connections) 2) Improved handling of federation links: a) Links can be created even if the remote broker is not reachable b) If links are lost, re-establishment will occur using an exponential back-off algorithm 3) Durability of exchanges is now viewable through management 4) ManagementAgent API has been moved to an interface class to reduce coupling between the broker and manageable plug-ins. 5) General configuration storage capability has been added to the store/recover interface. This is used for federation links. 6) Management object-ids for durable objects are now themselves durable. (Note: some refactoring needed around ProtocolAccess needed to try and reduce dependencies) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655563 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 14 +++++++------- qpid/python/commands/qpid-route | 18 ++++++++++++------ qpid/python/qpid/management.py | 8 +++++++- qpid/python/qpid/managementdata.py | 2 +- 4 files changed, 27 insertions(+), 15 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 8f2fb7ff1b..23a054f497 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -96,9 +96,9 @@ class BrokerManager: self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, username=self.broker.username, password=self.broker.password) self.conn.start () - self.session = self.conn.session(str(uuid4())) + self.session = self.conn.session (self.sessionId) self.mclient = managementClient (self.spec) - self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) + self.mchannel = self.mclient.addChannel (self.session) except socket.error, e: print "Socket Error:", e sys.exit (1) @@ -141,11 +141,11 @@ class BrokerManager: mch = self.mchannel mc.syncWaitForStable (mch) exchanges = mc.syncGetObjects (mch, "exchange") - print "Type Bindings Exchange Name" - print "=============================================" + print "Durable Type Bindings Exchange Name" + print "=======================================================" for ex in exchanges: if self.match (ex.name, filter): - print "%-10s%5d %s" % (ex.type, ex.bindings, ex.name) + print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindings, ex.name) def ExchangeListRecurse (self, filter): self.ConnectToBroker () @@ -278,7 +278,7 @@ class BrokerManager: key = args[2] try: - self.session.exchange_bind (queue=qname, exchange=ename, routing_key=key) + self.session.exchange_bind (queue=qname, exchange=ename, binding_key=key) except Closed, e: print "Failed:", e @@ -293,7 +293,7 @@ class BrokerManager: key = args[2] try: - self.session.exchange_unbind (queue=qname, exchange=ename, routing_key=key) + self.session.exchange_unbind (queue=qname, exchange=ename, binding_key=key) except Closed, e: print "Failed:", e diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index baa45a320f..e59f89480e 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -83,7 +83,7 @@ class RouteManager: def getLink (self): links = self.mclient.syncGetObjects (self.mch, "link") for link in links: - if link.address == self.src.name (): + if "%s:%d" % (link.host, link.port) == self.src.name (): return link return None @@ -100,8 +100,13 @@ class RouteManager: print "Inter-broker link not found, creating..." connectArgs = {} - connectArgs["host"] = self.src.host - connectArgs["port"] = self.src.port + connectArgs["host"] = self.src.host + connectArgs["port"] = self.src.port + connectArgs["useSsl"] = False + connectArgs["durable"] = False + connectArgs["authMechanism"] = "ANONYMOUS" + connectArgs["username"] = "" + connectArgs["password"] = "" res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) if _verbose: print "Connect method returned:", res.status, res.statusText @@ -122,6 +127,7 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." bridgeArgs = {} + bridgeArgs["durable"] = 0 bridgeArgs["src"] = exchange bridgeArgs["dest"] = exchange bridgeArgs["key"] = routingKey @@ -180,7 +186,7 @@ class RouteManager: myLink = link break if myLink != None: - print "%s %s %s %s" % (self.dest.name(), myLink.address, bridge.dest, bridge.key) + print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, bridge.key) def LoadRoutes (self, inFile): pass @@ -198,7 +204,7 @@ class RouteManager: myLink = link break if myLink != None: - print "Deleting Bridge: %s %s %s... " % (myLink.address, bridge.dest, bridge.key), + print "Deleting Bridge: %s:%d %s %s... " % (myLink.host, myLink.port, bridge.dest, bridge.key), res = mc.syncCallMethod (self.mch, bridge.id, bridge.classKey, "close") if res.status != 0: print "Error: %d - %s" % (res.status, res.statusText) @@ -208,7 +214,7 @@ class RouteManager: links = mc.syncGetObjects (self.mch, "link") for link in links: if _verbose: - print "Deleting Link: %s... " % link.address, + print "Deleting Link: %s:%d... " % (link.host, link.port), res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") if res.status != 0: print "Error: %d - %s" % (res.status, res.statusText) diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 3595fdfc3c..d8f09d14ab 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -281,7 +281,13 @@ class managementClient: hdr = self.checkHeader (codec) if hdr == None: raise ValueError ("outer header invalid"); - self.parse (ch, codec, hdr[0], hdr[1]) + + if hdr[0] == 'p': + self.handlePackageInd (ch, codec) + elif hdr[0] == 'q': + self.handleClassInd (ch, codec) + else: + self.parse (ch, codec, hdr[0], hdr[1]) ch.accept(msg) def replyCb (self, ch, msg): diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index a0197ba7d2..1d99cc11bc 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -84,7 +84,7 @@ class ManagementData: def rawObjId (self, displayId): bank = displayId / 1000 id = displayId % 1000 - if bank < 3: + if bank < 5: objId = (bank << 24) + id else: objId = self.bootSequence + (bank << 24) + id -- cgit v1.2.1 From 48454ce071be26b4662a137e41e7421f7b3f8d69 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 12 May 2008 17:23:21 +0000 Subject: QPID-1044: Part of patch from Jonathan Robie + changes to verify scripts to keep automated testing working. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655568 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 31 +++++-- qpid/python/examples/direct/direct_consumer.py | 51 ++++++----- qpid/python/examples/direct/direct_producer.py | 32 ++++--- qpid/python/examples/direct/listener.py | 59 ++++++------ qpid/python/examples/fanout/fanout_consumer.py | 115 +++++++++++------------- qpid/python/examples/fanout/fanout_producer.py | 33 ++++--- qpid/python/examples/fanout/listener.py | 83 ++++++++++------- qpid/python/examples/fanout/verify.in | 48 +++++----- qpid/python/examples/pubsub/topic_publisher.py | 45 ++++++---- qpid/python/examples/pubsub/topic_subscriber.py | 98 +++++++++++--------- qpid/python/examples/pubsub/verify.in | 92 ++++++++++--------- qpid/python/examples/request-response/client.py | 89 +++++++++--------- qpid/python/examples/request-response/server.py | 48 ++++++---- qpid/python/examples/request-response/verify.in | 2 +- 14 files changed, 469 insertions(+), 357 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index 7041ce2f24..deea0a3ccc 100755 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -8,34 +8,47 @@ sent to the queue named "message_queue". """ +# Common includes + import qpid import sys import os -from random import randint from qpid.util import connect from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty #----- Initialization ----------------------------------- # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" + amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session = conn.session(str(randint(1,64*1024))) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Create a queue ------------------------------------- diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index 91d85cee1a..f2018bbbb8 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -12,7 +12,7 @@ import os from random import randint from qpid.util import connect from qpid.connection import Connection -from qpid.datatypes import Message, RangedSet +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty @@ -20,11 +20,22 @@ from qpid.queue import Empty # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -32,10 +43,10 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session = conn.session(str(randint(1,64*1024))) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Read from queue -------------------------------------------- @@ -44,16 +55,17 @@ session = conn.session(str(randint(1,64*1024))) # The consumer tag identifies the client-side queue. -consumer_tag = "consumer1" -queue = session.incoming(consumer_tag) +local_queue_name = "local_queue" +queue = session.incoming(local_queue_name) -# Call message_consume() to tell the broker to deliver messages +# Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. +# start delivering messages as soon as credit is allocated using +# session.message_flow(). -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? +session.message_subscribe(queue="message_queue", destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) # Initialize 'final' and 'content', variables used to identify the last message. @@ -67,15 +79,6 @@ while content != final: session.message_accept(RangedSet(message.id)) print content -# Messages are not removed from the queue until they are -# acknowledged. Using cumulative=True, all messages from the session -# up to and including the one identified by the delivery tag are -# acknowledged. This is more efficient, because there are fewer -# network round-trips. - -#message.complete(cumulative=True) -# ? Is there an equivakent to the above in the new API ? - #----- Cleanup ------------------------------------------------ # Clean up before exiting so there are no open threads. diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index 7c4e30d96e..8f6a91ba18 100755 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -9,21 +9,33 @@ import qpid import sys import os -from random import randint from qpid.util import connect from qpid.connection import Connection from qpid.datatypes import Message +from qpid.datatypes import uuid4 from qpid.queue import Empty + #----- Initialization ----------------------------------- # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -31,10 +43,10 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session = conn.session(str(randint(1,64*1024))) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Publish some messages ------------------------------ @@ -42,9 +54,9 @@ session = conn.session(str(randint(1,64*1024))) props = session.delivery_properties(routing_key="routing_key") for i in range(10): - session.message_transfer("amq.direct",None, None, Message(props,"message " + str(i))) + session.message_transfer(destination="amq.direct", message=Message(props,"message " + str(i))) -session.message_transfer("amq.direct",None,None, Message(props,"That's all, folks!")) +session.message_transfer(destination="amq.direct", message=Message(props,"That's all, folks!")) #----- Cleanup -------------------------------------------- diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index aa60b1c501..c18ef47fb7 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -7,14 +7,18 @@ as a message listener. """ +# Common includes + import qpid import sys import os -from random import randint from qpid.util import connect from qpid.connection import Connection -from qpid.datatypes import Message, RangedSet -from qpid.queue import Empty +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +# Includes specific to this example + from time import sleep @@ -33,23 +37,26 @@ class Receiver: if content == "That's all, folks!": self.finalReceived = True - # Messages are not removed from the queue until they are - # acknowledged. Using cumulative=True, all messages from the session - # up to and including the one identified by the delivery tag are - # acknowledged. This is more efficient, because there are fewer - # network round-trips. - #message.complete(cumulative=True) - - #----- Initialization -------------------------------------- # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -57,33 +64,33 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session = conn.session(str(randint(1,64*1024))) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Read from queue -------------------------------------------- # Now let's create a local client queue and tell it to read # incoming messages. -# The consumer tag identifies the client-side queue. +# The local_queue_name identifies the client-side queue. -consumer_tag = "consumer1" -queue = session.incoming(consumer_tag) +local_queue_name = "local_queue" +queue = session.incoming(local_queue_name) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will # start delivering messages as soon as message_subscribe() is called. -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? +session.message_subscribe(queue="message_queue", destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) -receiver = Receiver () +receiver = Receiver() queue.listen (receiver.Handler) -while not receiver.isFinal (): +while not receiver.isFinal() : sleep (1) diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index b82d8045ff..21fc5e8f16 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -13,61 +13,27 @@ from qpid.connection import Connection from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty -#----- Functions ------------------------------------------- - -def dump_queue(session, queue_name): - - print "Messages queue: " + queue_name - - consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = session.incoming(queue_name) - - # Call message_subscribe() to tell the broker to deliver messages - # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as message_subscribe() is called. - - session.message_subscribe(queue=queue_name, destination=consumer_tag) - session.message_flow(consumer_tag, 0, 0xFFFFFFFF) - session.message_flow(consumer_tag, 1, 0xFFFFFFFF) - - print "Subscribed to queue " + queue_name - sys.stdout.flush() - - message = 0 - - while True: - try: - message = queue.get(timeout=10) - content = message.body - session.message_accept(RangedSet(message.id)) - print "Response: " + content - except Empty: - print "No more messages!" - break - except: - print "Unexpected exception!" - break - - - # Messages are not removed from the queue until they - # are acknowledged. Using cumulative=True, all messages - # in the session up to and including the one identified - # by the delivery tag are acknowledged. This is more efficient, - # because there are fewer network round-trips. - - #if message != 0: - # message.complete(cumulative=True) - - #----- Initialization -------------------------------------- + # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -75,25 +41,48 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) + + +#----- Main Body ------------------------------------------- + +# Create a server-side queue and route messages to it. +# The server-side queue must have a unique name. Use the +# session id for that. +server_queue_name = session.name +session.queue_declare(queue=server_queue_name) +session.exchange_bind(queue=server_queue_name, exchange="amq.fanout") + +# Create a local queue to receive messages from the server-side +# queue. +local_queue_name = "local_queue" +local_queue = session.incoming(local_queue_name) + +# Call message_consume() to tell the server to deliver messages +# from the AMQP queue to this local client queue. -session_id = str(uuid4()) -session = conn.session(session_id) +session.message_subscribe(queue=server_queue_name, destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) -#----- Main Body -- ---------------------------------------- +print "Subscribed to queue " + server_queue_name +sys.stdout.flush() -# Make a unique queue name for my queue from the session ID. -my_queue = session_id -session.queue_declare(queue=my_queue) +# Initialize 'final' and 'content', variables used to identify the last message. +final = "That's all, folks!" # In a message body, signals the last message +content = "" # Content of the last message read -# Bind my queue to the fanout exchange. No routing key is required -# the fanout exchange copies messages unconditionally to every -# bound queue -session.exchange_bind(queue=my_queue, exchange="amq.fanout") +# Read the messages - acknowledge each one +message = None +while content != final: + message = local_queue.get(timeout=10) + content = message.body + session.message_accept(RangedSet(message.id)) + print content -# Dump the messages on the queue. -dump_queue(session, my_queue) #----- Cleanup ------------------------------------------------ diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 1b5ea6995e..43d6a94c3d 100755 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -8,21 +8,31 @@ import qpid import sys import os -from random import randint from qpid.util import connect from qpid.connection import Connection -from qpid.datatypes import Message +from qpid.datatypes import Message, uuid4 from qpid.queue import Empty #----- Initialization ----------------------------------- # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -30,19 +40,22 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) -session = conn.session(str(randint(1,64*1024))) #----- Publish some messages ------------------------------ # Create some messages and put them on the broker. +delivery_properties = session.delivery_properties(routing_key="routing_key") + for i in range(10): - session.message_transfer("amq.fanout", None, None ,Message("message " + str(i))) + session.message_transfer(destination="amq.fanout", message=Message(delivery_properties,"message " + str(i))) -session.message_transfer("amq.fanout", None, None, Message("That's all, folks!")) +session.message_transfer(destination="amq.fanout", message=Message(delivery_properties, "That's all, folks!")) #----- Cleanup -------------------------------------------- diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index 8997c3698f..50cd06d2a5 100755 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -8,11 +8,15 @@ import qpid import sys -from qpid.client import Client -from qpid.content import Content -from qpid.queue import Empty -from time import sleep +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +# +from time import sleep #----- Message Receive Handler ----------------------------- class Receiver: @@ -23,57 +27,76 @@ class Receiver: return self.finalReceived def Handler (self, message): - content = message.content.body + content = message.body + session.message_accept(RangedSet(message.id)) print content if content == "That's all, folks!": self.finalReceived = True - # Messages are not removed from the queue until they are - # acknowledged. Using cumulative=True, all messages from the session - # up to and including the one identified by the delivery tag are - # acknowledged. This is more efficient, because there are fewer - # network round-trips. - message.complete(cumulative=True) - #----- Initialization -------------------------------------- # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 -amqp_spec="/usr/share/amqp/amqp.0-10-preview.xml" +host="127.0.0.1" +port=5672 user="guest" password="guest" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" -# Create a client and log in to it. +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. -client = Client(host, port, qpid.spec.load(amqp_spec)) -client.start({"LOGIN": user, "PASSWORD": password}) +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) -session = client.session() -session.session_open() +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Read from queue -------------------------------------------- -# Now let's create a local client queue and tell it to read -# incoming messages. +# Create a server-side queue and route messages to it. +# The server-side queue must have a unique name. Use the +# session id for that. + +server_queue_name = session.name +session.queue_declare(queue=server_queue_name) +session.exchange_bind(queue=server_queue_name, exchange="amq.fanout") + +# Create a local queue to receive messages from the server-side +# queue. +local_queue_name = "local_queue" +local_queue = session.incoming(local_queue_name) + -# The consumer tag identifies the client-side queue. +# The local queue name identifies the client-side queue. -consumer_tag = "consumer1" -queue = client.queue(consumer_tag) +local_queue_name = "local_queue" +local_queue = session.incoming(local_queue_name) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will # start delivering messages as soon as message_subscribe() is called. -session.message_subscribe(queue="message_queue", destination=consumer_tag) -session.message_flow(consumer_tag, 0, 0xFFFFFFFF) # Kill these? -session.message_flow(consumer_tag, 1, 0xFFFFFFFF) # Kill these? +session.message_subscribe(queue=server_queue_name, destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) receiver = Receiver () -queue.listen (receiver.Handler) +local_queue.listen (receiver.Handler) while not receiver.isFinal (): sleep (1) @@ -84,4 +107,4 @@ while not receiver.isFinal (): # Clean up before exiting so there are no open threads. # -session.session_close() +session.close() diff --git a/qpid/python/examples/fanout/verify.in b/qpid/python/examples/fanout/verify.in index 30dfeb9e69..d4b8670de9 100644 --- a/qpid/python/examples/fanout/verify.in +++ b/qpid/python/examples/fanout/verify.in @@ -1,31 +1,27 @@ ==== fanout_producer.py.out ==== fanout_consumer.py.out | remove_uuid -Messages queue: Subscribed to queue -Response: message 0 -Response: message 1 -Response: message 2 -Response: message 3 -Response: message 4 -Response: message 5 -Response: message 6 -Response: message 7 -Response: message 8 -Response: message 9 -Response: That's all, folks! -No more messages! +message 0 +message 1 +message 2 +message 3 +message 4 +message 5 +message 6 +message 7 +message 8 +message 9 +That's all, folks! ==== fanout_consumer.pyX.out | remove_uuid -Messages queue: Subscribed to queue -Response: message 0 -Response: message 1 -Response: message 2 -Response: message 3 -Response: message 4 -Response: message 5 -Response: message 6 -Response: message 7 -Response: message 8 -Response: message 9 -Response: That's all, folks! -No more messages! +message 0 +message 1 +message 2 +message 3 +message 4 +message 5 +message 6 +message 7 +message 8 +message 9 +That's all, folks! diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py index b79896eaf6..64e5a99924 100755 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -10,20 +10,38 @@ import qpid import sys import os -from random import randint from qpid.util import connect from qpid.connection import Connection -from qpid.datatypes import Message +from qpid.datatypes import Message, RangedSet, uuid4 from qpid.queue import Empty +#----- Functions ---------------------------------------- + +def send_msg(routing_key): + props = session.delivery_properties(routing_key=routing_key) + for i in range(5): + session.message_transfer(destination="amq.topic", message=Message(props,routing_key + " " + str(i))) + #----- Initialization ----------------------------------- -# Set parameters for login. -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +# Set parameters for login + +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -31,10 +49,10 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session = conn.session(str(randint(1,64*1024))) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Publish some messages ------------------------------ @@ -42,11 +60,6 @@ session = conn.session(str(randint(1,64*1024))) # topic exchange. The routing keys are "usa.news", "usa.weather", # "europe.news", and "europe.weather". -def send_msg(routing_key): - props = session.delivery_properties(routing_key=routing_key) - for i in range(5): - session.message_transfer("amq.topic", None, None, Message(props,"message " + str(i))) - # usa.news send_msg("usa.news") @@ -61,7 +74,7 @@ send_msg("europe.weather") # Signal termination props = session.delivery_properties(routing_key="control") -session.message_transfer("amq.topic",None, None, Message(props,"That's all, folks!")) +session.message_transfer(destination="amq.topic", message=Message(props,"That's all, folks!")) #----- Cleanup -------------------------------------------- diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index 6908be5471..3c4a8d8d0c 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -16,20 +16,7 @@ from qpid.queue import Empty #----- Functions ------------------------------------------- -def dump_queue(queue_name): - - print "Messages queue: " + queue_name - - consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = session.incoming(consumer_tag) - - # Call message_subscribe() to tell the broker to deliver messages - # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as message_subscribe() is called. - - session.message_subscribe(queue=queue_name, destination=consumer_tag) - session.message_flow(consumer_tag, 0, 0xFFFFFFFF) - session.message_flow(consumer_tag, 1, 0xFFFFFFFF) +def dump_queue(queue): content = "" # Content of the last message read final = "That's all, folks!" # In a message body, signals the last message @@ -37,36 +24,48 @@ def dump_queue(queue_name): while content != final: try: - message = queue.get() + message = queue.get(timeout=10) content = message.body session.message_accept(RangedSet(message.id)) print content except Empty: - #if message != 0: - # message.complete(cumulative=True) print "No more messages!" return - # Messages are not removed from the queue until they - # are acknowledged. Using multiple=True, all messages - # in the channel up to and including the one identified - # by the delivery tag are acknowledged. This is more efficient, - # because there are fewer network round-trips. - #if message != 0: - # message.complete(cumulative=True) +def subscribe_queue(server_queue_name, local_queue_name): + print "Subscribing local queue '" + local_queue_name + "' to " + server_queue_name + "'" + + queue = session.incoming(local_queue_name) + + session.message_subscribe(queue=server_queue_name, destination=local_queue_name) + session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) + session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) + + return queue #----- Initialization -------------------------------------- # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -74,19 +73,19 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session_id = str(uuid4()) -session = conn.session(session_id) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Main Body -- ---------------------------------------- +# declare queues on the server -news = "news" + session_id -weather = "weather" + session_id -usa = "usa" + session_id -europe = "europe" + session_id +news = "news-" + session.name +weather = "weather-" + session.name +usa = "usa-" + session.name +europe = "europe-" + session.name session.queue_declare(queue=news, exclusive=True) session.queue_declare(queue=weather, exclusive=True) @@ -115,12 +114,31 @@ session.exchange_bind(exchange="amq.topic", queue=europe, binding_key="control") print "Queues created - please start the topic producer" sys.stdout.flush() +# Subscribe local queues to server queues + +local_news = "local_news" +local_weather = "local_weather" +local_usa = "local_usa" +local_europe = "local_europe" + +local_news_queue = subscribe_queue(news, local_news) +local_weather_queue = subscribe_queue(weather, local_weather) +local_usa_queue = subscribe_queue(usa, local_usa) +local_europe_queue = subscribe_queue(europe, local_europe) + # Call dump_queue to print messages from each queue -dump_queue(news) -dump_queue(weather) -dump_queue(usa) -dump_queue(europe) +print "Messages on 'news' queue:" +dump_queue(local_news_queue) + +print "Messages on 'weather' queue:" +dump_queue(local_weather_queue) + +print "Messages on 'usa' queue:" +dump_queue(local_usa_queue) + +print "Messages on 'europe' queue:" +dump_queue(local_europe_queue) #----- Cleanup ------------------------------------------------ diff --git a/qpid/python/examples/pubsub/verify.in b/qpid/python/examples/pubsub/verify.in index 2f6da09ec5..1b74acd832 100644 --- a/qpid/python/examples/pubsub/verify.in +++ b/qpid/python/examples/pubsub/verify.in @@ -1,51 +1,55 @@ ==== topic_publisher.py.out ==== topic_subscriber.py.out | remove_uuid | sort -message 0 -message 0 -message 0 -message 0 -message 0 -message 0 -message 0 -message 0 -message 1 -message 1 -message 1 -message 1 -message 1 -message 1 -message 1 -message 1 -message 2 -message 2 -message 2 -message 2 -message 2 -message 2 -message 2 -message 2 -message 3 -message 3 -message 3 -message 3 -message 3 -message 3 -message 3 -message 3 -message 4 -message 4 -message 4 -message 4 -message 4 -message 4 -message 4 -message 4 -Messages queue: europe -Messages queue: news -Messages queue: usa -Messages queue: weather +europe.news 0 +europe.news 0 +europe.news 1 +europe.news 1 +europe.news 2 +europe.news 2 +europe.news 3 +europe.news 3 +europe.news 4 +europe.news 4 +europe.weather 0 +europe.weather 0 +europe.weather 1 +europe.weather 1 +europe.weather 2 +europe.weather 2 +europe.weather 3 +europe.weather 3 +europe.weather 4 +europe.weather 4 +Messages on 'europe' queue: +Messages on 'news' queue: +Messages on 'usa' queue: +Messages on 'weather' queue: Queues created - please start the topic producer +Subscribing local queue 'local_europe' to europe-' +Subscribing local queue 'local_news' to news-' +Subscribing local queue 'local_usa' to usa-' +Subscribing local queue 'local_weather' to weather-' That's all, folks! That's all, folks! That's all, folks! That's all, folks! +usa.news 0 +usa.news 0 +usa.news 1 +usa.news 1 +usa.news 2 +usa.news 2 +usa.news 3 +usa.news 3 +usa.news 4 +usa.news 4 +usa.weather 0 +usa.weather 0 +usa.weather 1 +usa.weather 1 +usa.weather 2 +usa.weather 2 +usa.weather 3 +usa.weather 3 +usa.weather 4 +usa.weather 4 diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 8f7d430d1b..0fcd256d49 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -18,18 +18,7 @@ from qpid.queue import Empty def dump_queue(queue_name): - print "Messages queue: " + queue_name - - consumer_tag = queue_name # Use the queue name as the consumer tag - need a unique tag - queue = session.incoming(consumer_tag) - - # Call message_subscribe() to tell the broker to deliver messages - # from the AMQP queue to a local client queue. The broker will - # start delivering messages as soon as message_subscribe() is called. - - session.message_subscribe(queue=queue_name, destination=consumer_tag) - session.message_flow(consumer_tag, 0, 0xFFFFFFFF) - session.message_flow(consumer_tag, 1, 0xFFFFFFFF) + print "Messages on queue: " + queue_name message = 0 @@ -47,25 +36,27 @@ def dump_queue(queue_name): break - # Messages are not removed from the queue until they - # are acknowledged. Using cumulative=True, all messages - # in the session up to and including the one identified - # by the delivery tag are acknowledged. This is more efficient, - # because there are fewer network round-trips. - - #if message != 0: - # message.complete(cumulative=True) - - #----- Initialization -------------------------------------- + # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] @@ -73,11 +64,11 @@ except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" # Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) -session_id = str(uuid4()) -session = conn.session(session_id) #----- Main Body -- ---------------------------------------- @@ -85,9 +76,23 @@ session = conn.session(session_id) # same string as the name of the queue and the name of the routing # key. -replyTo = "ReplyTo:" + session_id -session.queue_declare(queue=replyTo, exclusive=True) -session.exchange_bind(exchange="amq.direct", queue=replyTo, binding_key=replyTo) +reply_to = "reply_to:" + session.name +session.queue_declare(queue=reply_to, exclusive=True) +session.exchange_bind(exchange="amq.direct", queue=reply_to, binding_key=reply_to) + +# Create a local queue and subscribe it to the response queue + +local_queue_name = "local_queue" +queue = session.incoming(local_queue_name) + +# Call message_subscribe() to tell the broker to deliver messages from +# the server's reply_to queue to our local client queue. The server +# will start delivering messages as soon as message credit is +# available. + +session.message_subscribe(queue=reply_to, destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) # Send some messages to the server's request queue @@ -96,16 +101,20 @@ lines = ["Twas brilling, and the slithy toves", "All mimsy were the borogroves,", "And the mome raths outgrabe."] -for ln in lines: - print "Request: " + ln - mp = session.message_properties() - mp.reply_to = session.reply_to("amq.direct", replyTo) - dp = session.delivery_properties(routing_key="request") - session.message_transfer("amq.direct", None, None, Message(mp,dp,ln)) +# We will use the same reply_to and routing key +# for each message + +message_properties = session.message_properties() +message_properties.reply_to = session.reply_to("amq.direct", reply_to) +delivery_properties = session.delivery_properties(routing_key="request") + +for line in lines: + print "Request: " + line + session.message_transfer(destination="amq.direct", message=Message(message_properties, delivery_properties, line)) -# Now see what messages the server sent to our replyTo queue +# Now see what messages the server sent to our reply_to queue -dump_queue(replyTo) +dump_queue(reply_to) #----- Cleanup ------------------------------------------------ diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 4377571248..7b182723b9 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -4,6 +4,7 @@ Server for a client/server example """ + import qpid import sys import os @@ -22,31 +23,42 @@ def respond(session, request): message_properties = request.get("message_properties") reply_to = message_properties.reply_to if reply_to == None: - raise Exception("reply to property needs to be there") - - props = session.delivery_properties(routing_key=reply_to["routing_key"]) - session.message_transfer(reply_to["exchange"],None, None, Message(props,request.body.upper())) + raise Exception("This message is missing the 'reply_to' property, which is required") + + props = session.delivery_properties(routing_key=reply_to["routing_key"]) + session.message_transfer(destination=reply_to["exchange"], message=Message(props,request.body.upper())) #----- Initialization -------------------------------------- + # Set parameters for login -host=len(sys.argv) > 1 and sys.argv[1] or "127.0.0.1" -port=len(sys.argv) > 2 and int(sys.argv[2]) or 5672 + +host="127.0.0.1" +port=5672 user="guest" password="guest" -amqp_spec="" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) try: amqp_spec = os.environ["AMQP_SPEC"] except KeyError: amqp_spec="/usr/share/amqp/amqp.0-10.xml" -# Create a connection. -conn = Connection (connect (host,port), qpid.spec.load(amqp_spec)) -conn.start() - -session_id = str(uuid4()) -session = conn.session(session_id) +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) #----- Main Body -- ---------------------------------------- @@ -55,11 +67,11 @@ session = conn.session(session_id) session.queue_declare(queue="request", exclusive=True) session.exchange_bind(exchange="amq.direct", queue="request", binding_key="request") -dest = "request_destination" +local_queue_name = "local_queue" -session.message_subscribe(queue="request", destination=dest) -session.message_flow(dest, 0, 0xFFFFFFFF) -session.message_flow(dest, 1, 0xFFFFFFFF) +session.message_subscribe(queue="request", destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) # Remind the user to start the client program @@ -70,7 +82,7 @@ sys.stdout.flush() # Respond to each request -queue = session.incoming(dest) +queue = session.incoming(local_queue_name) # If we get a message, send it back to the user (as indicated in the # ReplyTo property) diff --git a/qpid/python/examples/request-response/verify.in b/qpid/python/examples/request-response/verify.in index 8d7f732ec8..6c24366722 100644 --- a/qpid/python/examples/request-response/verify.in +++ b/qpid/python/examples/request-response/verify.in @@ -3,7 +3,7 @@ Request: Twas brilling, and the slithy toves Request: Did gyre and gimble in the wabe. Request: All mimsy were the borogroves, Request: And the mome raths outgrabe. -Messages queue: ReplyTo: +Messages on queue: reply_to: Response: TWAS BRILLING, AND THE SLITHY TOVES Response: DID GYRE AND GIMBLE IN THE WABE. Response: ALL MIMSY WERE THE BOROGROVES, -- cgit v1.2.1 From 42ff3ed764be27d4611dce93c1f7fe4805aba533 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 12 May 2008 19:45:22 +0000 Subject: QPID-1052: Patch from Ted Ross This patch contains the following: 1) The session-id reported by the management API now matches the session.name in the session table 2) management.py API has a new callback for closed connections 3) qpid-tool uses the closed-connection handler to notify the user of a lost connection git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655619 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-tool | 9 ++++++++- qpid/python/qpid/management.py | 18 ++++++++++++++---- qpid/python/qpid/managementdata.py | 13 ++++++++++++- 3 files changed, 34 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 1aee3a1b7f..9977db3518 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -31,16 +31,23 @@ from qpid.peer import Closed class Mcli (Cmd): """ Management Command Interpreter """ - prompt = "qpid: " def __init__ (self, dataObject, dispObject): Cmd.__init__ (self) self.dataObject = dataObject self.dispObject = dispObject + self.dataObject.setCli (self) + self.prompt = "qpid: " def emptyline (self): pass + def setPromptMessage (self, p): + if p == None: + self.prompt = "qpid: " + else: + self.prompt = "qpid[%s]: " % p + def do_help (self, data): print "Management Tool for QPID" print diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index d8f09d14ab..0e7233fad2 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -81,7 +81,7 @@ class methodResult: class managementChannel: """ This class represents a connection to an AMQP broker. """ - def __init__ (self, ssn, topicCb, replyCb, cbContext, _detlife=0): + def __init__ (self, ssn, topicCb, replyCb, exceptionCb, cbContext, _detlife=0): """ Given a channel on an established AMQP broker connection, this method opens a session and performs all of the declarations and bindings needed to participate in the management protocol. """ @@ -93,6 +93,7 @@ class managementChannel: self.qpidChannel = ssn self.tcb = topicCb self.rcb = replyCb + self.ecb = exceptionCb self.context = cbContext self.reqsOutstanding = 0 @@ -104,7 +105,7 @@ class managementChannel: ssn.message_subscribe (queue=self.topicName, destination="tdest") ssn.message_subscribe (queue=self.replyName, destination="rdest") - ssn.incoming ("tdest").listen (self.topicCb) + ssn.incoming ("tdest").listen (self.topicCb, self.exceptionCb) ssn.incoming ("rdest").listen (self.replyCb) ssn.message_set_flow_mode (destination="tdest", flow_mode=1) @@ -130,6 +131,10 @@ class managementChannel: if self.enabled: self.rcb (self, msg) + def exceptionCb (self, data): + if self.ecb != None: + self.ecb (data) + def send (self, exchange, msg): if self.enabled: self.qpidChannel.message_transfer (destination=exchange, message=msg) @@ -160,12 +165,13 @@ class managementClient: #======================================================== # User API - interacts with the class's user #======================================================== - def __init__ (self, amqpSpec, ctrlCb=None, configCb=None, instCb=None, methodCb=None): + def __init__ (self, amqpSpec, ctrlCb=None, configCb=None, instCb=None, methodCb=None, closeCb=None): self.spec = amqpSpec self.ctrlCb = ctrlCb self.configCb = configCb self.instCb = instCb self.methodCb = methodCb + self.closeCb = closeCb self.schemaCb = None self.eventCb = None self.channels = [] @@ -189,7 +195,7 @@ class managementClient: def addChannel (self, channel, cbContext=None): """ Register a new channel. """ - mch = managementChannel (channel, self.topicCb, self.replyCb, cbContext) + mch = managementChannel (channel, self.topicCb, self.replyCb, self.exceptCb, cbContext) self.channels.append (mch) self.incOutstanding (mch) @@ -312,6 +318,10 @@ class managementClient: self.parse (ch, codec, hdr[0], hdr[1]) ch.accept(msg) + def exceptCb (self, data): + if self.closeCb != None: + self.closeCb (data) + #======================================================== # Internal Functions #======================================================== diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 1d99cc11bc..bdc299767d 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -160,11 +160,20 @@ class ManagementData: finally: self.lock.release () + def closeHandler (self, reason): + print "Connection to broker lost:", reason + self.operational = False + if self.cli != None: + self.cli.setPromptMessage ("Broker Disconnected") + def schemaHandler (self, context, className, configs, insts, methods, events): """ Callback for schema updates """ if className not in self.schema: self.schema[className] = (configs, insts, methods, events) + def setCli (self, cliobj): + self.cli = cliobj + def __init__ (self, disp, host, username="guest", password="guest", specfile="../../specs/amqp.0-10.xml"): self.spec = qpid.spec.load (specfile) @@ -184,9 +193,11 @@ class ManagementData: self.conn.start () self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, - self.instHandler, self.methodReply) + self.instHandler, self.methodReply, self.closeHandler) self.mclient.schemaListener (self.schemaHandler) self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) + self.operational = True + self.cli = None def close (self): pass -- cgit v1.2.1 From 8c9f5cf7454080b4056de1aa3d9e900813128d85 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Tue, 13 May 2008 15:30:13 +0000 Subject: QPID-1052: Management: session.name matches session id provided by API, handling of lost connections -- applied patch supplied by Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655915 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 12 +++++++++--- qpid/python/tests_0-10/management.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 0e7233fad2..fe980b4a02 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -96,6 +96,7 @@ class managementChannel: self.ecb = exceptionCb self.context = cbContext self.reqsOutstanding = 0 + self.brokerInfo = None ssn.queue_declare (queue=self.topicName, exclusive=True, auto_delete=True) ssn.queue_declare (queue=self.replyName, exclusive=True, auto_delete=True) @@ -116,6 +117,9 @@ class managementChannel: ssn.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) ssn.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) + def setBrokerInfo (self, data): + self.brokerInfo = data + def shutdown (self): self.enabled = False self.ssn.message_cancel (destination="tdest") @@ -229,7 +233,7 @@ class managementClient: self.cv.acquire () if channel.reqsOutstanding == 0: self.cv.release () - return + return channel.brokerInfo self.syncInFlight = True starttime = time () @@ -239,6 +243,7 @@ class managementClient: self.cv.release () raise RuntimeError ("Timed out waiting for response on channel") self.cv.release () + return channel.brokerInfo def syncCallMethod (self, channel, objId, className, methodName, args=None): """ Synchronous (blocking) method call """ @@ -492,9 +497,10 @@ class managementClient: self.ctrlCb (ch.context, self.CTRL_USER, data) def handleBrokerResponse (self, ch, codec): + uuid = codec.read_uuid () + data = (uuid, ch.sessionId) + ch.setBrokerInfo (data) if self.ctrlCb != None: - uuid = codec.read_uuid () - data = (uuid, ch.sessionId) self.ctrlCb (ch.context, self.CTRL_BROKER_INFO, data) # Send a package request diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index b2ab617244..3ca7284d2a 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -63,6 +63,21 @@ class ManagementTest (TestBase010): self.assertEqual (len (systems), 1) mc.removeChannel (mch) + def test_self_session_id (self): + session = self.session + + mc = managementClient (session.spec) + mch = mc.addChannel (session) + + info = mc.syncWaitForStable (mch) + brokerSessions = mc.syncGetObjects (mch, "session") + found = False + for bs in brokerSessions: + if bs.name == info[1]: + found = True + self.assertEqual (found, True) + mc.removeChannel (mch) + def test_standard_exchanges (self): session = self.session -- cgit v1.2.1 From d6332236d8d3a7e45dba869ac8757c4029cd667e Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 13 May 2008 17:23:35 +0000 Subject: QPID-1055: use int64 for encoding python both python int and longs; this ensures consistent behavior on both 64 bit and non 64 bit systems git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655951 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec010.py | 2 +- qpid/python/tests/codec010.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 58d305aa6c..9db73ae7cf 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -447,7 +447,7 @@ class Spec(Node): ENCODINGS = { basestring: "vbin16", - int: "int32", + int: "int64", long: "int64", None.__class__: "void", list: "list", diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index 65d4525758..bd3f875ac4 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -32,7 +32,7 @@ class CodecTest(TestCase): sc = StringCodec(self.spec) t.encode(sc, value) decoded = t.decode(sc) - assert decoded == value + assert decoded == value, "%s, %s" % (decoded, value) def testMapString(self): self.check("map", {"string": "this is a test"}) -- cgit v1.2.1 From 1b092b72dd110848a9dce856fede4f4a0af83331 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Tue, 13 May 2008 18:39:11 +0000 Subject: Fix typo in examples. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655965 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/request-response/client.py | 2 +- qpid/python/examples/request-response/verify.in | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 0fcd256d49..8ab1149901 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -96,7 +96,7 @@ session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) # Send some messages to the server's request queue -lines = ["Twas brilling, and the slithy toves", +lines = ["Twas brillig, and the slithy toves", "Did gyre and gimble in the wabe.", "All mimsy were the borogroves,", "And the mome raths outgrabe."] diff --git a/qpid/python/examples/request-response/verify.in b/qpid/python/examples/request-response/verify.in index 6c24366722..4c31128975 100644 --- a/qpid/python/examples/request-response/verify.in +++ b/qpid/python/examples/request-response/verify.in @@ -1,10 +1,10 @@ ==== client.py.out | remove_uuid -Request: Twas brilling, and the slithy toves +Request: Twas brillig, and the slithy toves Request: Did gyre and gimble in the wabe. Request: All mimsy were the borogroves, Request: And the mome raths outgrabe. Messages on queue: reply_to: -Response: TWAS BRILLING, AND THE SLITHY TOVES +Response: TWAS BRILLIG, AND THE SLITHY TOVES Response: DID GYRE AND GIMBLE IN THE WABE. Response: ALL MIMSY WERE THE BOROGROVES, Response: AND THE MOME RATHS OUTGRABE. -- cgit v1.2.1 From e0992fcd5dfceb2504fc8f3f6b9fce28035f37d4 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 13 May 2008 19:16:15 +0000 Subject: QPID-954: added fallbacks and fixes for running the python client on python 2.3 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@655976 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/__init__.py | 6 ++++-- qpid/python/qpid/compat.py | 28 ++++++++++++++++++++++++++++ qpid/python/qpid/connection08.py | 10 +++++++--- qpid/python/tests_0-10/dtx.py | 1 + 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 qpid/python/qpid/compat.py (limited to 'qpid/python') diff --git a/qpid/python/mllib/__init__.py b/qpid/python/mllib/__init__.py index 44b78126fb..81165f6999 100644 --- a/qpid/python/mllib/__init__.py +++ b/qpid/python/mllib/__init__.py @@ -22,7 +22,7 @@ This module provides document parsing and transformation utilities for both SGML and XML. """ -import dom, transforms, parsers +import os, dom, transforms, parsers import xml.sax, types from cStringIO import StringIO @@ -49,7 +49,9 @@ def sgml_parse(source): p.close() return p.parser.tree -def xml_parse(source): +def xml_parse(filename): + # XXX: this is for older versions of python + source = "file://%s" % os.path.abspath(filename) p = parsers.XMLParser() xml.sax.parse(source, p) return p.parser.tree diff --git a/qpid/python/qpid/compat.py b/qpid/python/qpid/compat.py new file mode 100644 index 0000000000..26f60fb8aa --- /dev/null +++ b/qpid/python/qpid/compat.py @@ -0,0 +1,28 @@ +# +# 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. +# + +try: + set = set +except NameError: + from sets import Set as set + +try: + from socket import SHUT_RDWR +except ImportError: + SHUT_RDWR = 2 diff --git a/qpid/python/qpid/connection08.py b/qpid/python/qpid/connection08.py index eafad7067a..8f2eef4770 100644 --- a/qpid/python/qpid/connection08.py +++ b/qpid/python/qpid/connection08.py @@ -27,6 +27,7 @@ import socket, codec, logging, qpid from cStringIO import StringIO from spec import load from codec import EOF +from compat import SHUT_RDWR class SockIO: @@ -54,7 +55,8 @@ class SockIO: pass def close(self): - self.sock.shutdown(socket.SHUT_RDWR) + self.sock.shutdown(SHUT_RDWR) + self.sock.close() def connect(host, port): sock = socket.socket() @@ -407,7 +409,6 @@ class Header(Frame): else: return Header.decode_legacy(spec, c, size) - @staticmethod def decode_structs(spec, c, size): structs = [] start = c.nread @@ -425,7 +426,8 @@ class Header(Frame): length = s.get(f.name) return Header(None, 0, length, props) - @staticmethod + decode_structs = staticmethod(decode_structs) + def decode_legacy(spec, c, size): klass = spec.classes.byid[c.decode_short()] weight = c.decode_short() @@ -453,6 +455,8 @@ class Header(Frame): properties[str(f.name)] = c.decode(f.type) return Header(klass, weight, size, properties) + decode_legacy = staticmethod(decode_legacy) + def __str__(self): return "%s %s %s %s" % (self.klass, self.weight, self.size, self.properties) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 531f8a8f66..d676b5806c 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -21,6 +21,7 @@ from qpid.queue import Empty from qpid.datatypes import Message, RangedSet from qpid.session import SessionException from qpid.testlib import TestBase010 +from qpid.compat import set from struct import pack, unpack from time import sleep -- cgit v1.2.1 From f3f2a5d43a0b23837d87d33e5415c9b131b5457d Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Tue, 13 May 2008 20:36:23 +0000 Subject: continuation of QPID-1052 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656004 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 18 ++++++++++++------ qpid/python/qpid/managementdata.py | 2 +- qpid/python/tests_0-10/management.py | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index fe980b4a02..f831ee472e 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -78,6 +78,13 @@ class methodResult: for arg in args: setattr (self, arg, args[arg]) +class brokerInfo: + """ Object that contains information about a broker and the session to it """ + + def __init__ (self, brokerId, sessionId): + self.brokerId = brokerId + self.sessionId = sessionId + class managementChannel: """ This class represents a connection to an AMQP broker. """ @@ -137,7 +144,7 @@ class managementChannel: def exceptionCb (self, data): if self.ecb != None: - self.ecb (data) + self.ecb (self, data) def send (self, exchange, msg): if self.enabled: @@ -323,9 +330,9 @@ class managementClient: self.parse (ch, codec, hdr[0], hdr[1]) ch.accept(msg) - def exceptCb (self, data): + def exceptCb (self, ch, data): if self.closeCb != None: - self.closeCb (data) + self.closeCb (ch.context, data) #======================================================== # Internal Functions @@ -498,10 +505,9 @@ class managementClient: def handleBrokerResponse (self, ch, codec): uuid = codec.read_uuid () - data = (uuid, ch.sessionId) - ch.setBrokerInfo (data) + ch.brokerInfo = brokerInfo (uuid, ch.sessionId) if self.ctrlCb != None: - self.ctrlCb (ch.context, self.CTRL_BROKER_INFO, data) + self.ctrlCb (ch.context, self.CTRL_BROKER_INFO, ch.brokerInfo) # Send a package request sendCodec = Codec (self.spec) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index bdc299767d..36eb442e0d 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -160,7 +160,7 @@ class ManagementData: finally: self.lock.release () - def closeHandler (self, reason): + def closeHandler (self, context, reason): print "Connection to broker lost:", reason self.operational = False if self.cli != None: diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 3ca7284d2a..f1360a1902 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -73,7 +73,7 @@ class ManagementTest (TestBase010): brokerSessions = mc.syncGetObjects (mch, "session") found = False for bs in brokerSessions: - if bs.name == info[1]: + if bs.name == info.sessionId: found = True self.assertEqual (found, True) mc.removeChannel (mch) -- cgit v1.2.1 From 4bed8993507c1a9e89a7cc04547faf409356e347 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Tue, 13 May 2008 20:38:21 +0000 Subject: From Jonathan Robie: https://issues.apache.org/jira/browse/QPID-1056: Python examples for the xml exchange. https://issues.apache.org/jira/browse/QPID-1057 Fixes to the XmlExchange.cpp that prevent it from crashing the broker when used with python clients that don't send application header properties git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656005 13f79535-47bb-0310-9956-ffa450edef68 --- .../python/examples/xml-exchange/declare_queues.py | 82 ++++++++++++++++++ qpid/python/examples/xml-exchange/listener.py | 98 ++++++++++++++++++++++ qpid/python/examples/xml-exchange/verify | 3 + qpid/python/examples/xml-exchange/verify.in | 14 ++++ qpid/python/examples/xml-exchange/xml_consumer.py | 89 ++++++++++++++++++++ qpid/python/examples/xml-exchange/xml_producer.py | 84 +++++++++++++++++++ 6 files changed, 370 insertions(+) create mode 100644 qpid/python/examples/xml-exchange/declare_queues.py create mode 100644 qpid/python/examples/xml-exchange/listener.py create mode 100644 qpid/python/examples/xml-exchange/verify create mode 100644 qpid/python/examples/xml-exchange/verify.in create mode 100644 qpid/python/examples/xml-exchange/xml_consumer.py create mode 100644 qpid/python/examples/xml-exchange/xml_producer.py (limited to 'qpid/python') diff --git a/qpid/python/examples/xml-exchange/declare_queues.py b/qpid/python/examples/xml-exchange/declare_queues.py new file mode 100644 index 0000000000..d3bf4d359e --- /dev/null +++ b/qpid/python/examples/xml-exchange/declare_queues.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +""" + declare_queues.py + + Creates and binds a queue on an AMQP direct exchange. + + All messages using the routing key "routing_key" are + sent to the queue named "message_queue". +""" + +import qpid +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +#----- Initialization ----------------------------------- + + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) + +#----- Create a queue ------------------------------------- + +# queue_declare() creates an AMQP queue, which is held +# on the broker. Published messages are sent to the AMQP queue, +# from which messages are delivered to consumers. +# +# queue_bind() determines which messages are routed to a queue. +# Route all messages with the routing key "routing_key" to +# the AMQP queue named "message_queue". + +session.exchange_declare(exchange="xml", type="xml") +session.queue_declare(queue="message_queue") + +binding = {} +binding["xquery"] = """ + let $w := ./weather + return $w/station = 'Raleigh-Durham International Airport (KRDU)' + and $w/temperature_f > 50 + and $w/temperature_f - $w/dewpoint > 5 + and $w/wind_speed_mph > 7 + and $w/wind_speed_mph < 20 """ + + +session.exchange_bind(exchange="xml", queue="message_queue", binding_key="weather", arguments=binding) + + +#----- Cleanup --------------------------------------------- + +session.close() + + diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py new file mode 100644 index 0000000000..cdc00a7015 --- /dev/null +++ b/qpid/python/examples/xml-exchange/listener.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +""" + listener.py + + This AMQP client reads messages from a message + queue named "message_queue". It is implemented + as a message listener. +""" + + +import qpid +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +# + +from time import sleep + + +#----- Message Receive Handler ----------------------------- +class Receiver: + def __init__ (self): + self.finalReceived = False + + def isFinal (self): + return self.finalReceived + + def Handler (self, message): + content = message.body + session.message_accept(RangedSet(message.id)) + print content + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +local_queue_name = "local_queue" +local_queue = session.incoming(local_queue_name) + +# Call message_subscribe() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_subscribe() is called. + +session.message_subscribe(queue="message_queue", destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) + +receiver = Receiver () +local_queue.listen (receiver.Handler) + +sleep (10) + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.close() diff --git a/qpid/python/examples/xml-exchange/verify b/qpid/python/examples/xml-exchange/verify new file mode 100644 index 0000000000..01d81a18a1 --- /dev/null +++ b/qpid/python/examples/xml-exchange/verify @@ -0,0 +1,3 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +clients ./declare_queues.py ./direct_producer.py ./direct_consumer.py +outputs ./declare_queues.py.out ./direct_producer.py.out ./direct_consumer.py.out diff --git a/qpid/python/examples/xml-exchange/verify.in b/qpid/python/examples/xml-exchange/verify.in new file mode 100644 index 0000000000..5e691619d9 --- /dev/null +++ b/qpid/python/examples/xml-exchange/verify.in @@ -0,0 +1,14 @@ +==== declare_queues.py.out +==== direct_producer.py.out +==== direct_consumer.py.out +message 0 +message 1 +message 2 +message 3 +message 4 +message 5 +message 6 +message 7 +message 8 +message 9 +That's all, folks! diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py new file mode 100644 index 0000000000..c210a4342c --- /dev/null +++ b/qpid/python/examples/xml-exchange/xml_consumer.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" + direct_consumer.py + + This AMQP client reads messages from a message + queue named "message_queue". +""" + +import qpid +import sys +import os +from random import randint +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + + +#----- Initialization -------------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) + + +#----- Read from queue -------------------------------------------- + +# Now let's create a local client queue and tell it to read +# incoming messages. + +# The consumer tag identifies the client-side queue. + +local_queue_name = "local_queue" +local_queue = session.incoming(local_queue_name) + +# Call message_consume() to tell the broker to deliver messages +# from the AMQP queue to this local client queue. The broker will +# start delivering messages as soon as message_consume() is called. + +session.message_subscribe(queue="message_queue", destination=local_queue_name) +session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) +session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) + +# Initialize 'final' and 'content', variables used to identify the last message. + +message = None +while True: + try: + message = local_queue.get(timeout=10) + session.message_accept(RangedSet(message.id)) + content = message.body + print content + except Empty: + print "No more messages!" + break + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. +# + +session.close() diff --git a/qpid/python/examples/xml-exchange/xml_producer.py b/qpid/python/examples/xml-exchange/xml_producer.py new file mode 100644 index 0000000000..9e609ed132 --- /dev/null +++ b/qpid/python/examples/xml-exchange/xml_producer.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +""" + xml_producer.py + + Publishes messages to an XML exchange, using + the routing key "weather" +""" + + +import qpid +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +#----- Functions ---------------------------------------- + +# Data for weather reports + +station = ("Raleigh-Durham International Airport (KRDU)", + "New Bern, Craven County Regional Airport (KEWN)", + "Boone, Watauga County Hospital Heliport (KTNB)", + "Hatteras, Mitchell Field (KHSE)") +wind_speed_mph = ( 0, 2, 5, 10, 16, 22, 28, 35, 42, 51, 61, 70, 80 ) +temperature_f = ( 30, 40, 50, 60, 70, 80, 90, 100 ) +dewpoint = ( 35, 40, 45, 50 ) + +def pick_one(list, i): + return str( list [ i % len(list)] ) + +def report(i): + return "" + "" + pick_one(station,i)+ "" + "" + pick_one(wind_speed_mph,i) + "" + "" + pick_one(temperature_f,i) + "" + "" + pick_one(dewpoint,i) + "" + "" + + +#----- Initialization ----------------------------------- + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" +amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +# +# If AMQP_SPEC is defined, use it to locate the spec file instead of +# looking for it in the default location. + +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +try: + amqp_spec = os.environ["AMQP_SPEC"] +except KeyError: + amqp_spec="/usr/share/amqp/amqp.0-10.xml" + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection.start() +session = connection.session(str(uuid4())) + +#----- Publish some messages ------------------------------ + +# Create some messages and put them on the broker. + +props = session.delivery_properties(routing_key="weather") + +for i in range(10): + print report(i) + session.message_transfer(destination="xml", message=Message(props, report(i))) + + +#----- Cleanup -------------------------------------------- + +# Clean up before exiting so there are no open threads. + +session.close() -- cgit v1.2.1 From 55bca9389c6a0b5fa899238e26a2a913be459a78 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 13 May 2008 21:38:28 +0000 Subject: QPID-990: Patch from Ted Ross to enable persisting of inter-broker routing entities git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656023 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 204 +++++++++++++++++++++++++++------------- 1 file changed, 140 insertions(+), 64 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index e59f89480e..e839e36821 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -31,30 +31,32 @@ from qpid.connection import Connection from qpid.util import connect def Usage (): - print "Usage: qpid-route [OPTIONS] add [id] [exclude-list]" - print " qpid-route [OPTIONS] del " - print " qpid-route [OPTIONS] list " - #print " qpid-route [OPTIONS] load " - print " qpid-route [OPTIONS] flush " + print "Usage: qpid-route [OPTIONS] link add " + print " qpid-route [OPTIONS] link del " + print " qpid-route [OPTIONS] link list " + print + print " qpid-route [OPTIONS] route add [id] [exclude-list]" + print " qpid-route [OPTIONS] route del " + print " qpid-route [OPTIONS] route list " + print " qpid-route [OPTIONS] route flush " print print "Options:" print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10.xml)" - print " -v [ --verbose ] Verbose output" - print " -q [ --quiet ] Quiet output, don't print duplicate warnings" + print " -v [ --verbose ] Verbose output" + print " -q [ --quiet ] Quiet output, don't print duplicate warnings" + print " -d [ --durable ] Added configuration shall be durable" + print " -e [ --del-empty-link ] Delete link after deleting last route on the link" print print " dest-broker and src-broker are in the form: [username/password@] hostname | ip-address [:]" print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print - #print " If loading the route configuration from a file, the input file has one line per route" - #print " in the form:" - #print - #print " " - #print sys.exit (1) _specpath = "/usr/share/amqp/amqp.0-10.xml" _verbose = False _quiet = False +_durable = False +_dellink = False class RouteManager: def __init__ (self, destBroker): @@ -87,6 +89,57 @@ class RouteManager: return link return None + def AddLink (self, srcBroker): + self.src = Broker (srcBroker) + mc = self.mclient + + brokers = mc.syncGetObjects (self.mch, "broker") + broker = brokers[0] + link = self.getLink() + if link != None: + print "Link already exists" + sys.exit(1) + + connectArgs = {} + connectArgs["host"] = self.src.host + connectArgs["port"] = self.src.port + connectArgs["useSsl"] = False + connectArgs["durable"] = _durable + connectArgs["authMechanism"] = "PLAIN" + connectArgs["username"] = self.src.username + connectArgs["password"] = self.src.password + res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) + if _verbose: + print "Connect method returned:", res.status, res.statusText + link = self.getLink () + + def DelLink (self, srcBroker): + self.src = Broker (srcBroker) + mc = self.mclient + + brokers = mc.syncGetObjects (self.mch, "broker") + broker = brokers[0] + link = self.getLink() + if link == None: + print "Link not found" + sys.exit(1) + + res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + if _verbose: + print "Close method returned:", res.status, res.statusText + + def ListLinks (self): + mc = self.mclient + links = mc.syncGetObjects (self.mch, "link") + if len(links) == 0: + print "No Links Found" + else: + print + print "Host Port Durable State Last Error" + print "===================================================================" + for link in links: + print "%-16s%-8d %c %-18s%s" % (link.host, link.port, YN(link.durable), link.state, link.lastError) + def AddRoute (self, srcBroker, exchange, routingKey, id, excludes): self.src = Broker (srcBroker) mc = self.mclient @@ -103,10 +156,10 @@ class RouteManager: connectArgs["host"] = self.src.host connectArgs["port"] = self.src.port connectArgs["useSsl"] = False - connectArgs["durable"] = False - connectArgs["authMechanism"] = "ANONYMOUS" - connectArgs["username"] = "" - connectArgs["password"] = "" + connectArgs["durable"] = _durable + connectArgs["authMechanism"] = "PLAIN" + connectArgs["username"] = self.src.username + connectArgs["password"] = self.src.password res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) if _verbose: print "Connect method returned:", res.status, res.statusText @@ -127,15 +180,18 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." bridgeArgs = {} - bridgeArgs["durable"] = 0 + bridgeArgs["durable"] = _durable bridgeArgs["src"] = exchange bridgeArgs["dest"] = exchange bridgeArgs["key"] = routingKey - bridgeArgs["id"] = id + bridgeArgs["tag"] = id bridgeArgs["excludes"] = excludes bridgeArgs["src_is_queue"] = 0 bridgeArgs["src_is_local"] = 0 res = mc.syncCallMethod (self.mch, link.id, link.classKey, "bridge", bridgeArgs) + if res.status == 4: + print "Can't create a durable route on a non-durable link" + sys.exit(1) if _verbose: print "Bridge method returned:", res.status, res.statusText @@ -159,7 +215,7 @@ class RouteManager: if res.status != 0: print "Error closing bridge: %d - %s" % (res.status, res.statusText) sys.exit (1) - if len (bridges) == 1: + if len (bridges) == 1 and _dellink: link = self.getLink () if link == None: sys.exit (0) @@ -188,9 +244,6 @@ class RouteManager: if myLink != None: print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, bridge.key) - def LoadRoutes (self, inFile): - pass - def ClearAllRoutes (self): mc = self.mclient links = mc.syncGetObjects (self.mch, "link") @@ -211,23 +264,29 @@ class RouteManager: elif _verbose: print "Ok" - links = mc.syncGetObjects (self.mch, "link") - for link in links: - if _verbose: - print "Deleting Link: %s:%d... " % (link.host, link.port), - res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") - if res.status != 0: - print "Error: %d - %s" % (res.status, res.statusText) - elif _verbose: - print "Ok" + if _dellink: + links = mc.syncGetObjects (self.mch, "link") + for link in links: + if _verbose: + print "Deleting Link: %s:%d... " % (link.host, link.port), + res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + if res.status != 0: + print "Error: %d - %s" % (res.status, res.statusText) + elif _verbose: + print "Ok" + +def YN(val): + if val == 1: + return 'Y' + return 'N' ## ## Main Program ## try: - longOpts = ("verbose", "quiet", "spec-file=") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:vq", longOpts) + longOpts = ("verbose", "quiet", "spec-file=", "durable", "del-empty-link") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:vqde", longOpts) except: Usage () @@ -238,40 +297,57 @@ for opt in optlist: _verbose = True if opt[0] == "-q" or opt[0] == "--quiet": _quiet = True + if opt[0] == "-d" or opt[0] == "--durable": + _durable = True + if opt[0] == "-e" or opt[0] == "--del-empty-link": + _dellink = True nargs = len (cargs) -if nargs < 2: +if nargs < 3: Usage () -cmd = cargs[0] -if cmd != "load": - rm = RouteManager (cargs[1]) - rm.ConnectToBroker () - -if cmd == "add": - if nargs < 5 or nargs > 7: - Usage () - - id = "" - excludes = "" - if nargs > 5: id = cargs[5] - if nargs > 6: excludes = cargs[6] - rm.AddRoute (cargs[2], cargs[3], cargs[4], id, excludes) -elif cmd == "del": - if nargs != 5: - Usage () - else: - rm.DelRoute (cargs[2], cargs[3], cargs[4]) -else: - if nargs != 2: - Usage () - - if cmd == "list": - rm.ListRoutes () - #elif cmd == "load": - # rm.LoadRoutes (cargs[1]) - elif cmd == "flush": - rm.ClearAllRoutes () +group = cargs[0] +cmd = cargs[1] +rm = RouteManager (cargs[2]) +rm.ConnectToBroker () + +if group == "link": + if cmd == "add": + if nargs != 4: + Usage() + rm.AddLink (cargs[3]) + elif cmd == "del": + if nargs != 4: + Usage() + rm.DelLink (cargs[3]) + elif cmd == "list": + if nargs != 3: + Usage() + rm.ListLinks () + +elif group == "route": + if cmd == "add": + if nargs < 6 or nargs > 8: + Usage () + + id = "" + excludes = "" + if nargs > 6: id = cargs[6] + if nargs > 7: excludes = cargs[7] + rm.AddRoute (cargs[3], cargs[4], cargs[5], id, excludes) + elif cmd == "del": + if nargs != 6: + Usage () + else: + rm.DelRoute (cargs[3], cargs[4], cargs[5]) else: - Usage () + if nargs != 3: + Usage () + + if cmd == "list": + rm.ListRoutes () + elif cmd == "flush": + rm.ClearAllRoutes () + else: + Usage () rm.Disconnect () -- cgit v1.2.1 From 0862b2ed103b79bfb966c23bd1098ef411c3c5bf Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 14 May 2008 12:56:09 +0000 Subject: Fixed python/examples/xml-exchange verify script. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656255 13f79535-47bb-0310-9956-ffa450edef68 --- .../python/examples/xml-exchange/declare_queues.py | 0 qpid/python/examples/xml-exchange/listener.py | 0 qpid/python/examples/xml-exchange/verify | 4 ++-- qpid/python/examples/xml-exchange/verify.in | 27 +++++++++++----------- qpid/python/examples/xml-exchange/xml_consumer.py | 0 qpid/python/examples/xml-exchange/xml_producer.py | 0 6 files changed, 16 insertions(+), 15 deletions(-) mode change 100644 => 100755 qpid/python/examples/xml-exchange/declare_queues.py mode change 100644 => 100755 qpid/python/examples/xml-exchange/listener.py mode change 100644 => 100755 qpid/python/examples/xml-exchange/xml_consumer.py mode change 100644 => 100755 qpid/python/examples/xml-exchange/xml_producer.py (limited to 'qpid/python') diff --git a/qpid/python/examples/xml-exchange/declare_queues.py b/qpid/python/examples/xml-exchange/declare_queues.py old mode 100644 new mode 100755 diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py old mode 100644 new mode 100755 diff --git a/qpid/python/examples/xml-exchange/verify b/qpid/python/examples/xml-exchange/verify index 01d81a18a1..bf05463f1d 100644 --- a/qpid/python/examples/xml-exchange/verify +++ b/qpid/python/examples/xml-exchange/verify @@ -1,3 +1,3 @@ # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify -clients ./declare_queues.py ./direct_producer.py ./direct_consumer.py -outputs ./declare_queues.py.out ./direct_producer.py.out ./direct_consumer.py.out +clients ./declare_queues.py ./xml_producer.py ./xml_consumer.py +outputs ./declare_queues.py.out ./xml_producer.py.out ./xml_consumer.py.out diff --git a/qpid/python/examples/xml-exchange/verify.in b/qpid/python/examples/xml-exchange/verify.in index 5e691619d9..e5b9909408 100644 --- a/qpid/python/examples/xml-exchange/verify.in +++ b/qpid/python/examples/xml-exchange/verify.in @@ -1,14 +1,15 @@ ==== declare_queues.py.out -==== direct_producer.py.out -==== direct_consumer.py.out -message 0 -message 1 -message 2 -message 3 -message 4 -message 5 -message 6 -message 7 -message 8 -message 9 -That's all, folks! +==== xml_producer.py.out +Raleigh-Durham International Airport (KRDU)03035 +New Bern, Craven County Regional Airport (KEWN)24040 +Boone, Watauga County Hospital Heliport (KTNB)55045 +Hatteras, Mitchell Field (KHSE)106050 +Raleigh-Durham International Airport (KRDU)167035 +New Bern, Craven County Regional Airport (KEWN)228040 +Boone, Watauga County Hospital Heliport (KTNB)289045 +Hatteras, Mitchell Field (KHSE)3510050 +Raleigh-Durham International Airport (KRDU)423035 +New Bern, Craven County Regional Airport (KEWN)514040 +==== xml_consumer.py.out +Raleigh-Durham International Airport (KRDU)167035 +No more messages! diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py old mode 100644 new mode 100755 diff --git a/qpid/python/examples/xml-exchange/xml_producer.py b/qpid/python/examples/xml-exchange/xml_producer.py old mode 100644 new mode 100755 -- cgit v1.2.1 From 9cc70bbe0ed008653b050fe2e2fec0a90aa8ad1f Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Wed, 14 May 2008 14:47:31 +0000 Subject: Merge branch 'python-mllib' git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656301 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/mllib/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/mllib/__init__.py b/qpid/python/mllib/__init__.py index 81165f6999..39e9363614 100644 --- a/qpid/python/mllib/__init__.py +++ b/qpid/python/mllib/__init__.py @@ -22,7 +22,7 @@ This module provides document parsing and transformation utilities for both SGML and XML. """ -import os, dom, transforms, parsers +import os, dom, transforms, parsers, sys import xml.sax, types from cStringIO import StringIO @@ -50,8 +50,11 @@ def sgml_parse(source): return p.parser.tree def xml_parse(filename): - # XXX: this is for older versions of python - source = "file://%s" % os.path.abspath(filename) + if sys.version_info[0:2] == (2,3): + # XXX: this is for older versions of python + source = "file://%s" % os.path.abspath(filename) + else: + source = filename p = parsers.XMLParser() xml.sax.parse(source, p) return p.parser.tree -- cgit v1.2.1 From ca3bc30a4ff5fbd2504b2868eea60e647b16b2d8 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Thu, 15 May 2008 21:34:10 +0000 Subject: - Enable python tets and examples in make rpmbuild. - Remove hard-coded amqp.xml paths from python examples. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656853 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 8 +------- qpid/python/examples/direct/direct_consumer.py | 8 +------- qpid/python/examples/direct/direct_producer.py | 8 +------- qpid/python/examples/direct/listener.py | 8 +------- qpid/python/examples/fanout/fanout_consumer.py | 8 +------- qpid/python/examples/fanout/fanout_producer.py | 8 +------- qpid/python/examples/fanout/listener.py | 8 +------- qpid/python/examples/pubsub/topic_publisher.py | 8 +------- qpid/python/examples/pubsub/topic_subscriber.py | 8 +------- qpid/python/examples/request-response/client.py | 8 +------- qpid/python/examples/request-response/server.py | 8 +------- qpid/python/examples/xml-exchange/declare_queues.py | 8 +------- qpid/python/examples/xml-exchange/listener.py | 8 +------- qpid/python/examples/xml-exchange/xml_consumer.py | 8 +------- qpid/python/examples/xml-exchange/xml_producer.py | 8 +------- 15 files changed, 15 insertions(+), 105 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index deea0a3ccc..ce19a91a60 100755 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -26,7 +26,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -39,14 +38,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index f2018bbbb8..0e629b476a 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -24,7 +24,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -37,14 +36,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index 8f6a91ba18..bdc668b3c4 100755 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -24,7 +24,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -37,14 +36,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index c18ef47fb7..ee13aae75b 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -45,7 +45,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -58,14 +57,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index 21fc5e8f16..15452390a0 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -22,7 +22,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -35,14 +34,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 43d6a94c3d..f5b589c861 100755 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -21,7 +21,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -34,14 +33,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index 50cd06d2a5..56acd50e10 100755 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -42,7 +42,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -55,14 +54,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py index 64e5a99924..fb817c15f4 100755 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -30,7 +30,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -43,14 +42,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index 3c4a8d8d0c..a4210c83ef 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -54,7 +54,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -67,14 +66,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 8ab1149901..7bb4ee3462 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -45,7 +45,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -58,14 +57,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 7b182723b9..dbe4b88208 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -37,7 +37,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -50,13 +49,8 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/declare_queues.py b/qpid/python/examples/xml-exchange/declare_queues.py index d3bf4d359e..8985c20cab 100755 --- a/qpid/python/examples/xml-exchange/declare_queues.py +++ b/qpid/python/examples/xml-exchange/declare_queues.py @@ -25,7 +25,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -38,14 +37,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py index cdc00a7015..f2f9105091 100755 --- a/qpid/python/examples/xml-exchange/listener.py +++ b/qpid/python/examples/xml-exchange/listener.py @@ -42,7 +42,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -55,14 +54,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py index c210a4342c..047faec668 100755 --- a/qpid/python/examples/xml-exchange/xml_consumer.py +++ b/qpid/python/examples/xml-exchange/xml_consumer.py @@ -24,7 +24,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -37,14 +36,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/xml_producer.py b/qpid/python/examples/xml-exchange/xml_producer.py index 9e609ed132..435045b0fa 100755 --- a/qpid/python/examples/xml-exchange/xml_producer.py +++ b/qpid/python/examples/xml-exchange/xml_producer.py @@ -42,7 +42,6 @@ host="127.0.0.1" port=5672 user="guest" password="guest" -amqp_spec="/usr/share/amqp/amqp.0-10.xml" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) @@ -55,14 +54,9 @@ if len(sys.argv) > 1 : if len(sys.argv) > 2 : port=int(sys.argv[2]) -try: - amqp_spec = os.environ["AMQP_SPEC"] -except KeyError: - amqp_spec="/usr/share/amqp/amqp.0-10.xml" - # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket, spec=qpid.spec.load(amqp_spec)) +connection = Connection (sock=socket) connection.start() session = connection.session(str(uuid4())) -- cgit v1.2.1 From 6759e54c226d6dc779afd034078c73bc3bf23093 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Thu, 15 May 2008 21:46:14 +0000 Subject: - Remove redundant comments about AMQP_SPEC git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656859 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 4 ---- qpid/python/examples/direct/direct_consumer.py | 4 ---- qpid/python/examples/direct/direct_producer.py | 4 ---- qpid/python/examples/direct/listener.py | 4 ---- qpid/python/examples/fanout/fanout_consumer.py | 4 ---- qpid/python/examples/fanout/fanout_producer.py | 4 ---- qpid/python/examples/fanout/listener.py | 4 ---- qpid/python/examples/pubsub/topic_publisher.py | 4 ---- qpid/python/examples/pubsub/topic_subscriber.py | 4 ---- qpid/python/examples/request-response/client.py | 4 ---- qpid/python/examples/request-response/server.py | 4 ---- qpid/python/examples/xml-exchange/declare_queues.py | 4 ---- qpid/python/examples/xml-exchange/listener.py | 4 ---- qpid/python/examples/xml-exchange/xml_consumer.py | 4 ---- qpid/python/examples/xml-exchange/xml_producer.py | 4 ---- 15 files changed, 60 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index ce19a91a60..f0c34fa8c9 100755 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -29,10 +29,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index 0e629b476a..e421a417c1 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -27,10 +27,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index bdc668b3c4..870ce66e78 100755 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -27,10 +27,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index ee13aae75b..0716adecef 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -48,10 +48,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index 15452390a0..4a0e8ef488 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -25,10 +25,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index f5b589c861..3950ca6d2e 100755 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -24,10 +24,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index 56acd50e10..b2ed85045b 100755 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -45,10 +45,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py index fb817c15f4..8cf1b08644 100755 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -33,10 +33,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index a4210c83ef..4ba8e6a680 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -57,10 +57,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 7bb4ee3462..8d40aca5bb 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -48,10 +48,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index dbe4b88208..99b1431509 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -40,10 +40,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/xml-exchange/declare_queues.py b/qpid/python/examples/xml-exchange/declare_queues.py index 8985c20cab..bd17da5013 100755 --- a/qpid/python/examples/xml-exchange/declare_queues.py +++ b/qpid/python/examples/xml-exchange/declare_queues.py @@ -28,10 +28,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py index f2f9105091..db12d57e3e 100755 --- a/qpid/python/examples/xml-exchange/listener.py +++ b/qpid/python/examples/xml-exchange/listener.py @@ -45,10 +45,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py index 047faec668..22973b2bd9 100755 --- a/qpid/python/examples/xml-exchange/xml_consumer.py +++ b/qpid/python/examples/xml-exchange/xml_consumer.py @@ -27,10 +27,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : diff --git a/qpid/python/examples/xml-exchange/xml_producer.py b/qpid/python/examples/xml-exchange/xml_producer.py index 435045b0fa..72c5bdb53a 100755 --- a/qpid/python/examples/xml-exchange/xml_producer.py +++ b/qpid/python/examples/xml-exchange/xml_producer.py @@ -45,10 +45,6 @@ password="guest" # If an alternate host or port has been specified, use that instead # (this is used in our unit tests) -# -# If AMQP_SPEC is defined, use it to locate the spec file instead of -# looking for it in the default location. - if len(sys.argv) > 1 : host=sys.argv[1] if len(sys.argv) > 2 : -- cgit v1.2.1 From 55969a7fa19c4ad182518066afec1b013ec4abd0 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 15 May 2008 22:15:33 +0000 Subject: QPID-1064: made qpid-config close the session/connection; added incoming.stop() to cancel incoming messages and join on the listener thread; made managementBroker.removeChannel use incoming.stop(); modified session.close to wait on _closed rather than on channel == None git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656871 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 2 ++ qpid/python/qpid/connection.py | 1 - qpid/python/qpid/management.py | 4 ++-- qpid/python/qpid/queue.py | 3 +++ qpid/python/qpid/session.py | 9 +++++++-- 5 files changed, 14 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 23a054f497..054ea39e94 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -108,6 +108,8 @@ class BrokerManager: def Disconnect (self): self.mclient.removeChannel (self.mchannel) + self.session.close(timeout=10) + self.conn.close(timeout=10) def Overview (self): self.ConnectToBroker () diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 8d0e115458..ce27a74489 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -102,7 +102,6 @@ class Connection(Assembler): if ssn is not None: ssn.channel = None ssn.closed() - notify(ssn.condition) return ssn finally: self.lock.release() diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index f831ee472e..6f68981e4c 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -129,8 +129,8 @@ class managementChannel: def shutdown (self): self.enabled = False - self.ssn.message_cancel (destination="tdest") - self.ssn.message_cancel (destination="rdest") + self.ssn.incoming("tdest").stop() + self.ssn.incoming("rdest").stop() def topicCb (self, msg): """ Receive messages via the topic queue on this channel. """ diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index a8a5c0d9ad..830ea6060d 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -42,6 +42,9 @@ class Queue(BaseQueue): def close(self, error = None): self.error = error self.put(Queue.END) + if self.thread is not None: + self.thread.join() + self.thread = None def get(self, block = True, timeout = None): result = BaseQueue.get(self, block, timeout) diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 6dc64b4f06..1b33a4a795 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -110,14 +110,13 @@ class Session(Invoker): self.channel.session_detach(self.name) finally: self.invoke_lock.release() - if not wait(self.condition, lambda: self.channel is None, timeout): + if not wait(self.condition, lambda: self._closed, timeout): raise Timeout() def closed(self): self.lock.acquire() try: if self._closed: return - self._closed = True error = self.error() for id in self.results: @@ -127,6 +126,8 @@ class Session(Invoker): for q in self._incoming.values(): q.close(error) + + self._closed = True notify(self.condition) finally: self.lock.release() @@ -344,6 +345,10 @@ class Incoming(Queue): for unit in self.session.credit_unit.values(): self.session.message_flow(self.destination, unit, 0xFFFFFFFF) + def stop(self): + self.session.message_cancel(self.destination) + self.listen(None) + class Delegate: def __init__(self, session): -- cgit v1.2.1 From 6ee707476869bc6c09866b8dd3076c5120b02df7 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Fri, 16 May 2008 01:33:21 +0000 Subject: QPID-1061: Management heartbeat message from broker -- applied patch supplied by Ted Ross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656918 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 6f68981e4c..2ca433e6d2 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -170,6 +170,7 @@ class managementClient: CTRL_BROKER_INFO = 1 CTRL_SCHEMA_LOADED = 2 CTRL_USER = 3 + CTRL_HEARTBEAT = 4 SYNC_TIME = 10.0 @@ -304,6 +305,8 @@ class managementClient: self.handlePackageInd (ch, codec) elif hdr[0] == 'q': self.handleClassInd (ch, codec) + elif hdr[0] == 'h': + self.handleHeartbeat (ch, codec) else: self.parse (ch, codec, hdr[0], hdr[1]) ch.accept(msg) @@ -549,6 +552,11 @@ class managementClient: smsg = ch.message(sendCodec.encoded) ch.send ("qpid.management", smsg) + def handleHeartbeat (self, ch, codec): + timestamp = codec.read_uint64() + if self.ctrlCb != None: + self.ctrlCb (ch.context, self.CTRL_HEARTBEAT, timestamp) + def parseSchema (self, ch, codec): """ Parse a received schema-description message. """ self.decOutstanding (ch) -- cgit v1.2.1 From ac097e2ce2f2f869996dae113ab6b6da3db6548b Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Fri, 16 May 2008 02:12:28 +0000 Subject: QPID-1017 by Senaka Fernando -- with a few small edits git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@656926 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/README.txt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/README.txt b/qpid/python/README.txt index 0a64f0e2f2..e7bb5af408 100644 --- a/qpid/python/README.txt +++ b/qpid/python/README.txt @@ -7,9 +7,9 @@ Simplest way to run the tests: * Run a broker on the default port - * ./run_tests + * ./run-tests -For additional options: ./run_tests --help +For additional options: ./run-tests --help == Expected failures == @@ -18,7 +18,15 @@ Until we complete functionality, tests may fail because the tested functionality is missing in the broker. To skip expected failures in the C++ or Java brokers: - ./run_tests -I cpp_failing.txt - ./run_tests -I java_failing.txt + ./run-tests -I + +=== File List === + +1. cpp_failing_0-10.txt +2. cpp_failing_0-9.txt +3. cpp_failing_0-8.txt +4. java_failing_0-9.txt +5. java_failing_0-8.txt +6. cpp_failing_0-10_preview.txt -- will be depricated soon. If you fix a failure, please remove it from the corresponding list. -- cgit v1.2.1 From c98a518ab1e5428894f1e1063ad76da76c463ace Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Fri, 16 May 2008 14:02:57 +0000 Subject: QPID-1067 by tross git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@657069 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 11 +++++++---- qpid/python/commands/qpid-queue-stats | 14 ++++++++++---- qpid/python/commands/qpid-route | 13 ++++++++++--- qpid/python/commands/qpid-tool | 4 ++++ 4 files changed, 31 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 054ea39e94..a27ff84cdf 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -28,7 +28,7 @@ from threading import Condition from qpid.management import managementClient from qpid.managementdata import Broker from qpid.peer import Closed -from qpid.connection import Connection +from qpid.connection import Connection, ConnectionFailed from qpid.datatypes import uuid4 from qpid.util import connect from time import sleep @@ -72,7 +72,7 @@ def Usage (): print " --durable Queue is durable" print " --file-count N (8) Number of files in queue's persistence journal" print " --file-size N (24) File size in pages (64Kib/page)" - print " --max-queue-size N Maximum in-memory queue size as bytes" + print " --max-queue-size N Maximum in-memory queue size as bytes" print " --max-queue-count N Maximum in-memory queue size as a number of messages" print print "Add Exchange Options:" @@ -100,11 +100,14 @@ class BrokerManager: self.mclient = managementClient (self.spec) self.mchannel = self.mclient.addChannel (self.session) except socket.error, e: - print "Socket Error:", e + print "Socket Error %s - %s" % (e[0], e[1]) sys.exit (1) except Closed, e: - print "Connect Failed:", e + print "Connect Failed %d - %s" % (e[0], e[1]) sys.exit (1) + except ConnectionFailed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit(1) def Disconnect (self): self.mclient.removeChannel (self.mchannel) diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 8771c7dc5d..1a663797b1 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -29,7 +29,7 @@ from threading import Condition from qpid.management import managementClient from qpid.managementdata import Broker from qpid.peer import Closed -from qpid.connection import Connection +from qpid.connection import Connection, ConnectionFailed from qpid.util import connect from time import sleep @@ -67,8 +67,14 @@ class BrokerManager: self.mclient = managementClient (self.spec, None, self.configCb, self.instCb) self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) except socket.error, e: - print "Connect Error:", e - exit (1) + print "Socket Error %s - %s" % (e[0], e[1]) + sys.exit (1) + except Closed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit (1) + except ConnectionFailed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit(1) def setFilter(self,filter): self.filter = filter @@ -143,7 +149,7 @@ class BrokerManager: def main(): p = optparse.OptionParser() p.add_option('--broker-address','-a', default='localhost' , help='broker-addr is in the form: [username/password@] hostname | ip-address [:] \n ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost') - p.add_option('--amqp-spec-file','-s', default='"/usr/share/amqp/amqp.0-10.xml', help='the path to the amqp spec file') + p.add_option('--amqp-spec-file','-s', default='/usr/share/amqp/amqp.0-10.xml', help='the path to the amqp spec file') p.add_option('--filter','-f' ,default=None ,help='a list of comma separated queue names (regex are accepted) to show') options, arguments = p.parse_args() diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index e839e36821..d87b5bd191 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -27,7 +27,7 @@ import os from qpid.management import managementClient from qpid.managementdata import Broker from qpid.peer import Closed -from qpid.connection import Connection +from qpid.connection import Connection, ConnectionFailed from qpid.util import connect def Usage (): @@ -70,14 +70,21 @@ class RouteManager: try: self.spec = qpid.spec.load (_specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (broker.host, broker.port), self.spec, username=broker.username, password=broker.password) + self.conn = Connection (connect (broker.host, broker.port), self.spec, \ + username=broker.username, password=broker.password) self.conn.start () self.mclient = managementClient (self.spec) self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) self.mclient.syncWaitForStable (self.mch) except socket.error, e: - print "Connect Error:", e + print "Socket Error %s - %s" % (e[0], e[1]) sys.exit (1) + except Closed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit (1) + except ConnectionFailed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit(1) def Disconnect (self): self.mclient.removeChannel (self.mch) diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 9977db3518..32fdc9288b 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -24,6 +24,7 @@ import getopt import sys import socket from cmd import Cmd +from qpid.connection import ConnectionFailed from qpid.managementdata import ManagementData from shlex import split from qpid.disp import Display @@ -183,6 +184,9 @@ except Closed, e: except IOError, e: print "IOError: %d - %s: %s" % (e.errno, e.strerror, e.filename) sys.exit (1) +except ConnectionFailed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit(1) # Instantiate the CLI interpreter and launch it. cli = Mcli (data, disp) -- cgit v1.2.1 From 030e25a9b97f62f80ca693f90794dc323c1e3e98 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Fri, 16 May 2008 14:51:07 +0000 Subject: QPID-1067 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@657088 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 15 ++++----------- qpid/python/commands/qpid-queue-stats | 17 ++++++----------- qpid/python/commands/qpid-route | 18 ++++++++---------- qpid/python/commands/qpid-tool | 17 ++--------------- qpid/python/qpid/managementdata.py | 17 +++++++++-------- 5 files changed, 29 insertions(+), 55 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index a27ff84cdf..58a2272ed2 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -33,8 +33,6 @@ from qpid.datatypes import uuid4 from qpid.util import connect from time import sleep -_defspecpath = "/usr/share/amqp/amqp.0-10.xml" -_specpath = _defspecpath _recursive = False _host = "localhost" _durable = False @@ -65,8 +63,6 @@ def Usage (): print " -a [ --broker-addr ] Address (localhost) Address of qpidd broker" print " broker-addr is in the form: [username/password@] hostname | ip-address [:]" print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" - print " -s [ --spec-file] Path (" + _defspecpath + ")" - print " AMQP specification file" print print "Add Queue Options:" print " --durable Queue is durable" @@ -91,13 +87,12 @@ class BrokerManager: def ConnectToBroker (self): try: - self.spec = qpid.spec.load (_specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, + self.conn = Connection (connect (self.broker.host, self.broker.port), username=self.broker.username, password=self.broker.password) self.conn.start () self.session = self.conn.session (self.sessionId) - self.mclient = managementClient (self.spec) + self.mclient = managementClient (self.conn.spec) self.mchannel = self.mclient.addChannel (self.session) except socket.error, e: print "Socket Error %s - %s" % (e[0], e[1]) @@ -325,14 +320,12 @@ def YN (bool): ## try: - longOpts = ("durable", "spec-file=", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:a:b", longOpts) + longOpts = ("durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () for opt in optlist: - if opt[0] == "-s" or opt[0] == "--spec-file": - _specpath = opt[1] if opt[0] == "-b" or opt[0] == "--bindings": _recursive = True if opt[0] == "-a" or opt[0] == "--broker-addr": diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 1a663797b1..437714fec4 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -52,20 +52,19 @@ class BrokerManager: self.broker = None self.objects = {} self.filter = None - self.specpath="/usr/share/amqp/amqp.0-10.xml" def SetBroker (self, broker): self.broker = broker def ConnectToBroker (self): try: - self.spec = qpid.spec.load (self.specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, + self.conn = Connection (connect (self.broker.host, self.broker.port), username=self.broker.username, password=self.broker.password) self.conn.start () - self.mclient = managementClient (self.spec, None, self.configCb, self.instCb) - self.mchannel = self.mclient.addChannel (self.conn.session(self.sessionId)) + self.session = self.conn.session(self.sessionId) + self.mclient = managementClient (self.conn.spec, None, self.configCb, self.instCb) + self.mchannel = self.mclient.addChannel (self.session) except socket.error, e: print "Socket Error %s - %s" % (e[0], e[1]) sys.exit (1) @@ -79,11 +78,10 @@ class BrokerManager: def setFilter(self,filter): self.filter = filter - def setSpecpath(self,spec): - self.specpath = spec - def Disconnect (self): self.mclient.removeChannel (self.mchannel) + self.session.close(timeout=10) + self.conn.close(timeout=10) def configCb (self, context, classKey, row, timestamps): className = classKey[1] @@ -149,13 +147,11 @@ class BrokerManager: def main(): p = optparse.OptionParser() p.add_option('--broker-address','-a', default='localhost' , help='broker-addr is in the form: [username/password@] hostname | ip-address [:] \n ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost') - p.add_option('--amqp-spec-file','-s', default='/usr/share/amqp/amqp.0-10.xml', help='the path to the amqp spec file') p.add_option('--filter','-f' ,default=None ,help='a list of comma separated queue names (regex are accepted) to show') options, arguments = p.parse_args() host = options.broker_address - specpath = options.amqp_spec_file filter = [] if options.filter != None: for s in options.filter.split(","): @@ -163,7 +159,6 @@ def main(): bm = BrokerManager () bm.SetBroker (Broker (host)) - bm.setSpecpath(specpath) bm.setFilter(filter) bm.Display() diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index d87b5bd191..0dc9a89f21 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -41,7 +41,6 @@ def Usage (): print " qpid-route [OPTIONS] route flush " print print "Options:" - print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10.xml)" print " -v [ --verbose ] Verbose output" print " -q [ --quiet ] Quiet output, don't print duplicate warnings" print " -d [ --durable ] Added configuration shall be durable" @@ -52,7 +51,6 @@ def Usage (): print sys.exit (1) -_specpath = "/usr/share/amqp/amqp.0-10.xml" _verbose = False _quiet = False _durable = False @@ -68,13 +66,13 @@ class RouteManager: if _verbose: print "Connecting to broker: %s:%d" % (broker.host, broker.port) try: - self.spec = qpid.spec.load (_specpath) self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (broker.host, broker.port), self.spec, \ + self.conn = Connection (connect (broker.host, broker.port), \ username=broker.username, password=broker.password) self.conn.start () - self.mclient = managementClient (self.spec) - self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) + self.session = self.conn.session(self.sessionId) + self.mclient = managementClient (self.conn.spec) + self.mch = self.mclient.addChannel (self.session) self.mclient.syncWaitForStable (self.mch) except socket.error, e: print "Socket Error %s - %s" % (e[0], e[1]) @@ -88,6 +86,8 @@ class RouteManager: def Disconnect (self): self.mclient.removeChannel (self.mch) + self.session.close(timeout=10) + self.conn.close(timeout=10) def getLink (self): links = self.mclient.syncGetObjects (self.mch, "link") @@ -292,14 +292,12 @@ def YN(val): ## try: - longOpts = ("verbose", "quiet", "spec-file=", "durable", "del-empty-link") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "s:vqde", longOpts) + longOpts = ("verbose", "quiet", "durable", "del-empty-link") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "vqde", longOpts) except: Usage () for opt in optlist: - if opt[0] == "-s" or opt[0] == "--spec-file": - _specpath = opt[1] if opt[0] == "-v" or opt[0] == "--verbose": _verbose = True if opt[0] == "-q" or opt[0] == "--quiet": diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 32fdc9288b..478e362aaf 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -142,9 +142,6 @@ class Mcli (Cmd): def Usage (): print "Usage:", sys.argv[0], "[OPTIONS] []]" print - print "Options:" - print " -s [ --spec-file ] PATH (/usr/share/amqp/amqp.0-10.xml)" - print sys.exit (1) #========================================================= @@ -152,20 +149,10 @@ def Usage (): #========================================================= # Get host name and port if specified on the command line -try: - longOpts = ("spec-file=") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], 's:', longOpts) -except: - Usage () - sys.exit (1) +cargs = sys.argv[1:] -_specpath = "/usr/share/amqp/amqp.0-10.xml" _host = "localhost" -for opt in optlist: - if opt[0] == "-s" or opt[0] == "--spec-file": - _specpath = opt[1] - if len (cargs) > 0: _host = cargs[0] @@ -173,7 +160,7 @@ disp = Display () # Attempt to make a connection to the target broker try: - data = ManagementData (disp, _host, specfile=_specpath) + data = ManagementData (disp, _host) except socket.error, e: print "Socket Error (%s):" % _host, e[1] sys.exit (1) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 36eb442e0d..b04a58ddac 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -79,11 +79,11 @@ class ManagementData: def displayObjId (self, objId): bank = (objId & 0x0000FFFFFF000000L) >> 24 id = objId & 0x0000000000FFFFFFL - return bank * 1000 + id + return bank * 10000 + id def rawObjId (self, displayId): - bank = displayId / 1000 - id = displayId % 1000 + bank = displayId / 10000 + id = displayId % 10000 if bank < 5: objId = (bank << 24) + id else: @@ -135,13 +135,15 @@ class ManagementData: value = oldInst[idx][1] newInst.append ((key, value)) self.tables[className][id] = (timestamps, oldConf, newInst) - + finally: self.lock.release () def ctrlHandler (self, context, op, data): if op == self.mclient.CTRL_BROKER_INFO: pass + elif op == self.mclient.CTRL_HEARTBEAT: + pass def configHandler (self, context, className, list, timestamps): self.dataHandler (0, className, list, timestamps); @@ -174,9 +176,7 @@ class ManagementData: def setCli (self, cliobj): self.cli = cliobj - def __init__ (self, disp, host, username="guest", password="guest", - specfile="../../specs/amqp.0-10.xml"): - self.spec = qpid.spec.load (specfile) + def __init__ (self, disp, host, username="guest", password="guest"): self.lock = Lock () self.tables = {} self.schema = {} @@ -188,8 +188,9 @@ class ManagementData: self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) self.broker = Broker (host) - self.conn = Connection (connect (self.broker.host, self.broker.port), self.spec, + self.conn = Connection (connect (self.broker.host, self.broker.port), username=self.broker.username, password=self.broker.password) + self.spec = self.conn.spec self.conn.start () self.mclient = managementClient (self.spec, self.ctrlHandler, self.configHandler, -- cgit v1.2.1 From 0d9108f2899539661000e2d6c3cd96f3a0cd27fb Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 16 May 2008 15:58:12 +0000 Subject: QPID-947: initialize docstrings for protocol methods from the spec git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@657112 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/invoker.py | 3 +++ qpid/python/qpid/spec010.py | 52 +++++++++++++++++++++++++++++++++------------ qpid/python/qpid/spec08.py | 12 ++--------- qpid/python/qpid/util.py | 13 +++++++++++- 4 files changed, 55 insertions(+), 25 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/invoker.py b/qpid/python/qpid/invoker.py index 2d9e45179e..190a4ca288 100644 --- a/qpid/python/qpid/invoker.py +++ b/qpid/python/qpid/invoker.py @@ -23,6 +23,9 @@ class Invoker: def METHOD(self, name, resolved): method = lambda *args, **kwargs: self.invoke(resolved, args, kwargs) + method.__name__ = resolved.pyname + method.__doc__ = resolved.pydoc + method.__module__ = self.__class__.__module__ self.__dict__[name] = method return method diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 9db73ae7cf..8c9b9068ac 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -19,7 +19,7 @@ import os, cPickle, datatypes from codec010 import StringCodec -from util import mtime +from util import mtime, fill class Node: @@ -201,9 +201,10 @@ class Choice(Named, Node): class Composite(Type, Coded): - def __init__(self, name, code, size, pack, children): + def __init__(self, name, label, code, size, pack, children): Coded.__init__(self, code) Type.__init__(self, name, children) + self.label = label self.fields = [] self.size = size self.pack = pack @@ -254,11 +255,30 @@ class Composite(Type, Coded): if flags & (0x1 << i): f.type.encode(codec, values[f.name]) + def docstring(self): + docs = [] + if self.label: + docs.append(self.label) + docs += [d.text for d in self.docs] + s = "\n\n".join([fill(t, 2) for t in docs]) + for f in self.fields: + fdocs = [] + if f.label: + fdocs.append(f.label) + else: + fdocs.append("") + fdocs += [d.text for d in f.docs] + s += "\n\n" + "\n\n".join([fill(fdocs[0], 4, f.name)] + + [fill(t, 4) for t in fdocs[1:]]) + return s + + class Field(Named, Node, Lookup): - def __init__(self, name, type, children): + def __init__(self, name, label, type, children): Named.__init__(self, name) Node.__init__(self, children) + self.label = label self.type = type self.exceptions = [] @@ -284,6 +304,8 @@ class Struct(Composite): if self.code is not None: self.spec.structs[self.code] = self self.spec.structs_by_name[self.name] = self + self.pyname = self.name + self.pydoc = self.docstring() def __str__(self): fields = ",\n ".join(["%s: %s" % (f.name, f.type.qname) @@ -303,8 +325,8 @@ class Segment: class Instruction(Composite, Segment): - def __init__(self, name, code, children): - Composite.__init__(self, name, code, 0, 2, children) + def __init__(self, name, label, code, children): + Composite.__init__(self, name, label, code, 0, 2, children) Segment.__init__(self) self.track = None self.handlers = [] @@ -315,12 +337,14 @@ class Instruction(Composite, Segment): def register(self, node): Composite.register(self, node) - self.spec.instructions[self.qname.replace(".", "_")] = self + self.pyname = self.qname.replace(".", "_") + self.pydoc = self.docstring() + self.spec.instructions[self.pyname] = self class Control(Instruction): - def __init__(self, name, code, children): - Instruction.__init__(self, name, code, children) + def __init__(self, name, code, label, children): + Instruction.__init__(self, name, code, label, children) self.response = None def register(self, node): @@ -332,8 +356,8 @@ class Control(Instruction): class Command(Instruction): - def __init__(self, name, code, children): - Instruction.__init__(self, name, code, children) + def __init__(self, name, label, code, children): + Instruction.__init__(self, name, label, code, children) self.result = None self.exceptions = [] self.segments = [] @@ -599,7 +623,7 @@ class Loader: return Role(id(r["@name"]), self.children(r)) def do_control(self, c): - return Control(id(c["@name"]), self.code(c), self.children(c)) + return Control(id(c["@name"]), c["@label"], self.code(c), self.children(c)) def do_rule(self, r): return Rule(id(r["@name"]), self.children(r)) @@ -611,14 +635,14 @@ class Loader: return Response(id(r["@name"]), self.children(r)) def do_field(self, f): - return Field(id(f["@name"]), id(f["@type"]), self.children(f)) + return Field(id(f["@name"]), f["@label"], id(f["@type"]), self.children(f)) def do_struct(self, s): - return Struct(id(s["@name"]), self.code(s), num(s["@size"]), + return Struct(id(s["@name"]), s["@label"], self.code(s), num(s["@size"]), num(s["@pack"]), self.children(s)) def do_command(self, c): - return Command(id(c["@name"]), self.code(c), self.children(c)) + return Command(id(c["@name"]), c["@label"], self.code(c), self.children(c)) def do_segments(self, s): return Anonymous(self.children(s)) diff --git a/qpid/python/qpid/spec08.py b/qpid/python/qpid/spec08.py index 623b2e9e9f..a0047e7107 100644 --- a/qpid/python/qpid/spec08.py +++ b/qpid/python/qpid/spec08.py @@ -29,7 +29,8 @@ class so that the generated code can be reused in a variety of situations. """ -import re, textwrap, new, mllib, qpid +import re, new, mllib, qpid +from util import fill class SpecContainer: @@ -449,15 +450,6 @@ def pythonize(name): pass return name -def fill(text, indent, heading = None): - sub = indent * " " - if heading: - init = (indent - 2) * " " + heading + " -- " - else: - init = sub - w = textwrap.TextWrapper(initial_indent = init, subsequent_indent = sub) - return w.fill(" ".join(text.split())) - class Rule(Metadata): PRINT = ["text", "implement", "tests"] diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index d03a9bd7e9..1140cbe5ef 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -17,7 +17,7 @@ # under the License. # -import os, socket, time +import os, socket, time, textwrap def connect(host, port): sock = socket.socket() @@ -65,3 +65,14 @@ def notify(condition, action=lambda: None): condition.notifyAll() finally: condition.release() + +def fill(text, indent, heading = None): + sub = indent * " " + if heading: + if not text: + return (indent - 2) * " " + heading + init = (indent - 2) * " " + heading + " -- " + else: + init = sub + w = textwrap.TextWrapper(initial_indent = init, subsequent_indent = sub) + return w.fill(" ".join(text.split())) -- cgit v1.2.1 From 534f82a6777e7cb8a52a8280ce0a4f8aa0d0c3f9 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 16 May 2008 20:01:20 +0000 Subject: QPID-947: restrict docstring initialization to recent python versions git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@657191 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/invoker.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/invoker.py b/qpid/python/qpid/invoker.py index 190a4ca288..635f3ee769 100644 --- a/qpid/python/qpid/invoker.py +++ b/qpid/python/qpid/invoker.py @@ -17,15 +17,18 @@ # under the License. # +import sys + # TODO: need a better naming for this class now that it does the value # stuff class Invoker: def METHOD(self, name, resolved): method = lambda *args, **kwargs: self.invoke(resolved, args, kwargs) - method.__name__ = resolved.pyname - method.__doc__ = resolved.pydoc - method.__module__ = self.__class__.__module__ + if sys.version_info[:2] > (2, 3): + method.__name__ = resolved.pyname + method.__doc__ = resolved.pydoc + method.__module__ = self.__class__.__module__ self.__dict__[name] = method return method -- cgit v1.2.1 From fa85fb8871a8b1e620479d0717f1655553a93223 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 21 May 2008 21:40:49 +0000 Subject: QPID-1087 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@658886 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 0dc9a89f21..5206f5c3cb 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -112,9 +112,12 @@ class RouteManager: connectArgs["port"] = self.src.port connectArgs["useSsl"] = False connectArgs["durable"] = _durable - connectArgs["authMechanism"] = "PLAIN" - connectArgs["username"] = self.src.username - connectArgs["password"] = self.src.password + if self.src.username == "anonymous": + connectArgs["authMechanism"] = "ANONYMOUS" + else: + connectArgs["authMechanism"] = "PLAIN" + connectArgs["username"] = self.src.username + connectArgs["password"] = self.src.password res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) if _verbose: print "Connect method returned:", res.status, res.statusText @@ -164,9 +167,12 @@ class RouteManager: connectArgs["port"] = self.src.port connectArgs["useSsl"] = False connectArgs["durable"] = _durable - connectArgs["authMechanism"] = "PLAIN" - connectArgs["username"] = self.src.username - connectArgs["password"] = self.src.password + if self.src.username == "anonymous": + connectArgs["authMechanism"] = "ANONYMOUS" + else: + connectArgs["authMechanism"] = "PLAIN" + connectArgs["username"] = self.src.username + connectArgs["password"] = self.src.password res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) if _verbose: print "Connect method returned:", res.status, res.statusText -- cgit v1.2.1 From 7c1ba1ecf1d48993964cd86d563bf0a07e80a2f4 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 22 May 2008 18:09:31 +0000 Subject: Forbid broker to route to self, default to localhost when not specified git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@659186 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 5206f5c3cb..4e8dbc3a77 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -33,12 +33,12 @@ from qpid.util import connect def Usage (): print "Usage: qpid-route [OPTIONS] link add " print " qpid-route [OPTIONS] link del " - print " qpid-route [OPTIONS] link list " + print " qpid-route [OPTIONS] link list []" print print " qpid-route [OPTIONS] route add [id] [exclude-list]" print " qpid-route [OPTIONS] route del " - print " qpid-route [OPTIONS] route list " - print " qpid-route [OPTIONS] route flush " + print " qpid-route [OPTIONS] route list []" + print " qpid-route [OPTIONS] route flush []" print print "Options:" print " -v [ --verbose ] Verbose output" @@ -100,6 +100,10 @@ class RouteManager: self.src = Broker (srcBroker) mc = self.mclient + if self.dest.name() == self.src.name(): + print "Linking broker to itself is not permitted" + sys.exit(1) + brokers = mc.syncGetObjects (self.mch, "broker") broker = brokers[0] link = self.getLink() @@ -154,6 +158,10 @@ class RouteManager: self.src = Broker (srcBroker) mc = self.mclient + if self.dest.name() == self.src.name(): + print "Linking broker to itself is not permitted" + sys.exit(1) + brokers = mc.syncGetObjects (self.mch, "broker") broker = brokers[0] @@ -314,12 +322,16 @@ for opt in optlist: _dellink = True nargs = len (cargs) -if nargs < 3: +if nargs < 2: Usage () +if nargs == 2: + destBroker = "localhost" +else: + destBroker = cargs[2] group = cargs[0] cmd = cargs[1] -rm = RouteManager (cargs[2]) +rm = RouteManager (destBroker) rm.ConnectToBroker () if group == "link": @@ -332,8 +344,6 @@ if group == "link": Usage() rm.DelLink (cargs[3]) elif cmd == "list": - if nargs != 3: - Usage() rm.ListLinks () elif group == "route": @@ -352,9 +362,6 @@ elif group == "route": else: rm.DelRoute (cargs[3], cargs[4], cargs[5]) else: - if nargs != 3: - Usage () - if cmd == "list": rm.ListRoutes () elif cmd == "flush": -- cgit v1.2.1 From 2be65f56d7b82430679e068756cc3d10b0855d47 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 23 May 2008 13:29:42 +0000 Subject: qpid-tool fixed to cleanly handle brokers with management disabled git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@659535 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-tool | 8 ++++---- qpid/python/qpid/managementdata.py | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 478e362aaf..bf16faac42 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -164,16 +164,16 @@ try: except socket.error, e: print "Socket Error (%s):" % _host, e[1] sys.exit (1) -except Closed, e: - if str(e).find ("Exchange not found") != -1: - print "Management not enabled on broker: Use '-m yes' option on broker startup." - sys.exit (1) except IOError, e: print "IOError: %d - %s: %s" % (e.errno, e.strerror, e.filename) sys.exit (1) except ConnectionFailed, e: print "Connect Failed %d - %s" % (e[0], e[1]) sys.exit(1) +except Exception, e: + if str(e).find ("Exchange not found") != -1: + print "Management not enabled on broker: Use '-m yes' option on broker startup." + sys.exit(1) # Instantiate the CLI interpreter and launch it. cli = Mcli (data, disp) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index b04a58ddac..a0fa1ece64 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -163,7 +163,8 @@ class ManagementData: self.lock.release () def closeHandler (self, context, reason): - print "Connection to broker lost:", reason + if self.operational: + print "Connection to broker lost:", reason self.operational = False if self.cli != None: self.cli.setPromptMessage ("Broker Disconnected") @@ -181,7 +182,9 @@ class ManagementData: self.tables = {} self.schema = {} self.bootSequence = 0 + self.operational = False self.disp = disp + self.cli = None self.lastUnit = None self.methodSeq = 1 self.methodsPending = {} @@ -198,7 +201,6 @@ class ManagementData: self.mclient.schemaListener (self.schemaHandler) self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) self.operational = True - self.cli = None def close (self): pass -- cgit v1.2.1 From 6098815e59e2da90883ce6fb4d9f85354d6425f5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 23 May 2008 20:22:13 +0000 Subject: QPID-947: Switched over to using proper RFC 1982 serial numbers. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@659647 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 6 +++--- qpid/python/qpid/datatypes.py | 47 +++++++++++++++++++++++++++++++++++++++--- qpid/python/qpid/session.py | 4 ++-- qpid/python/tests/datatypes.py | 33 +++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 68573921d7..3d67d42d43 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -18,7 +18,7 @@ # from packer import Packer -from datatypes import RangedSet, Struct +from datatypes import serial, RangedSet, Struct class CodecException(Exception): pass @@ -87,9 +87,9 @@ class Codec(Packer): self.pack("!f", f) def read_sequence_no(self): - return self.read_uint32() + return serial(self.read_uint32()) def write_sequence_no(self, n): - self.write_uint32(n) + self.write_uint32(n.value) def read_uint64(self): diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 7bea1ccc19..7249d5621b 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -116,14 +116,55 @@ class Message: args.append("id=%s" % self.id) return "Message(%s)" % ", ".join(args) +def serial(o): + if isinstance(o, Serial): + return o + else: + return Serial(o) + +class Serial: + + def __init__(self, value): + self.value = value & 0xFFFFFFFF + + def __hash__(self): + return hash(self.value) + + def __cmp__(self, other): + if other is None: + return 1 + + other = serial(other) + + delta = (self.value - other.value) & 0xFFFFFFFF + neg = delta & 0x80000000 + mag = delta & 0x7FFFFFFF + + if neg: + return -mag + else: + return mag + + def __add__(self, other): + return Serial(self.value + other) + + def __sub__(self, other): + return Serial(self.value - other) + + def __repr__(self): + return "serial(%s)" % self.value + + def __str__(self): + return str(self.value) + class Range: def __init__(self, lower, upper = None): - self.lower = lower + self.lower = serial(lower) if upper is None: - self.upper = lower + self.upper = self.lower else: - self.upper = upper + self.upper = serial(upper) def __contains__(self, n): return self.lower <= n and n <= self.upper diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 1b33a4a795..3d8c0aa7fd 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -23,7 +23,7 @@ from datatypes import RangedSet, Struct, Future from codec010 import StringCodec from assembler import Segment from queue import Queue -from datatypes import Message +from datatypes import Message, serial from util import wait, notify from exceptions import * from logging import getLogger @@ -304,7 +304,7 @@ class Sender: def __init__(self, session): self.session = session - self.next_id = 0 + self.next_id = serial(0) self.next_offset = 0 self.segments = [] self._completed = RangedSet() diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index 6d8703ed4f..ef98e81da0 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -22,6 +22,39 @@ from qpid.testlib import testrunner from qpid.spec010 import load from qpid.datatypes import * +class SerialTest(TestCase): + + def test(self): + for s in (serial(0), serial(0x8FFFFFFF), serial(0xFFFFFFFF)): + assert s + 1 > s + assert s - 1 < s + assert s < s + 1 + assert s > s - 1 + + assert serial(0xFFFFFFFF) + 1 == serial(0) + + assert min(serial(0xFFFFFFFF), serial(0x0)) == serial(0xFFFFFFFF) + assert max(serial(0xFFFFFFFF), serial(0x0)) == serial(0x0) + + def testIncr(self): + s = serial(0) + s += 1 + assert s == serial(1) + + def testIn(self): + l = [serial(1), serial(2), serial(3), serial(4)] + assert serial(1) in l + assert serial(0xFFFFFFFF + 2) in l + assert 4 in l + + def testNone(self): + assert serial(0) != None + + def testHash(self): + d = {} + d[serial(0)] = "zero" + assert d[0] == "zero" + class RangedSetTest(TestCase): def check(self, ranges): -- cgit v1.2.1 From fdc891389a4b75b73249c88b195cebead020af74 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 23 May 2008 20:36:10 +0000 Subject: QPID-1064: only set the listeners to None *after* the thread has stopped git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@659650 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/queue.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/queue.py b/qpid/python/qpid/queue.py index 830ea6060d..c9f4d1d1d0 100644 --- a/qpid/python/qpid/queue.py +++ b/qpid/python/qpid/queue.py @@ -60,19 +60,19 @@ class Queue(BaseQueue): if listener is None and exc_listener is not None: raise ValueError("cannot set exception listener without setting listener") - self.listener = listener - self.exc_listener = exc_listener - if listener is None: if self.thread is not None: self.put(Queue.STOP) self.thread.join() self.thread = None - else: - if self.thread is None: - self.thread = Thread(target = self.run) - self.thread.setDaemon(True) - self.thread.start() + + self.listener = listener + self.exc_listener = exc_listener + + if listener is not None and self.thread is None: + self.thread = Thread(target = self.run) + self.thread.setDaemon(True) + self.thread.start() def run(self): while True: -- cgit v1.2.1 From 99daabac209e7e0d7e624f37e76a33b9e15f71f9 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 23 May 2008 21:40:03 +0000 Subject: QPID-947: added codec and tests for array and list types git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@659671 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 58 +++++++++++++++++++++++++++++++++++-------- qpid/python/qpid/spec010.py | 1 + qpid/python/tests/codec010.py | 27 ++++++++++++++++++++ 3 files changed, 75 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 3d67d42d43..dac023e2bd 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -160,17 +160,20 @@ class Codec(Packer): def write_map(self, m): sc = StringCodec(self.spec) - sc.write_uint32(len(m)) - for k, v in m.items(): - type = self.spec.encoding(v.__class__) - if type == None: - raise CodecException("no encoding for %s" % v.__class__) - sc.write_str8(k) - sc.write_uint8(type.code) - type.encode(sc, v) + if m is not None: + sc.write_uint32(len(m)) + for k, v in m.items(): + type = self.spec.encoding(v.__class__) + if type == None: + raise CodecException("no encoding for %s" % v.__class__) + sc.write_str8(k) + sc.write_uint8(type.code) + type.encode(sc, v) self.write_vbin32(sc.encoded) def read_map(self): sc = StringCodec(self.spec, self.read_vbin32()) + if not sc.encoded: + return None count = sc.read_uint32() result = {} while sc.encoded: @@ -182,15 +185,48 @@ class Codec(Packer): return result def write_array(self, a): - pass + sc = StringCodec(self.spec) + if a is not None: + if len(a) > 0: + type = self.spec.encoding(a[0].__class__) + else: + type = self.spec.encoding(None.__class__) + sc.write_uint8(type.code) + sc.write_uint32(len(a)) + for o in a: + type.encode(sc, o) + self.write_vbin32(sc.encoded) def read_array(self): sc = StringCodec(self.spec, self.read_vbin32()) + if not sc.encoded: + return None type = self.spec.types[sc.read_uint8()] count = sc.read_uint32() result = [] - while count: + while count > 0: + result.append(type.decode(sc)) + count -= 1 + return result + + def write_list(self, l): + sc = StringCodec(self.spec) + if l is not None: + sc.write_uint32(len(l)) + for o in l: + type = self.spec.encoding(o.__class__) + sc.write_uint8(type.code) + type.encode(sc, o) + self.write_vbin32(sc.encoded) + def read_list(self): + sc = StringCodec(self.spec, self.read_vbin32()) + if not sc.encoded: + return None + count = sc.read_uint32() + result = [] + while count > 0: + type = self.spec.types[sc.read_uint8()] result.append(type.decode(sc)) - count = count - 1 + count -= 1 return result def read_struct32(self): diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 8c9b9068ac..23966e6176 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -473,6 +473,7 @@ class Spec(Node): basestring: "vbin16", int: "int64", long: "int64", + float: "float", None.__class__: "void", list: "list", tuple: "list", diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index bd3f875ac4..c849c1b0c0 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -55,3 +55,30 @@ class CodecTest(TestCase): "long": 2**32, "none": None, "map": {"string": "nested map"}}) + + def testMapEmpty(self): + self.check("map", {}) + + def testMapNone(self): + self.check("map", None) + + def testList(self): + self.check("list", [1, "two", 3.0, -4]) + + def testListEmpty(self): + self.check("list", []) + + def testListNone(self): + self.check("list", None) + + def testArrayInt(self): + self.check("array", [1, 2, 3, 4]) + + def testArrayString(self): + self.check("array", ["one", "two", "three", "four"]) + + def testArrayEmpty(self): + self.check("array", []) + + def testArrayNone(self): + self.check("array", None) -- cgit v1.2.1 From 778a8312f9601d17ab4f3c6c74092909637fb17c Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 23 May 2008 21:44:33 +0000 Subject: QPID-947: added test for nested lists git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@659673 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/codec010.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index c849c1b0c0..835966e103 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -49,12 +49,16 @@ class CodecTest(TestCase): def testMapNested(self): self.check("map", {"map": {"string": "nested test"}}) + def testMapList(self): + self.check("map", {"list": [1, "two", 3.0, -4]}) + def testMapAll(self): self.check("map", {"string": "this is a test", "int": 3, "long": 2**32, "none": None, - "map": {"string": "nested map"}}) + "map": {"string": "nested map"}, + "list": [1, "two", 3.0, -4]}) def testMapEmpty(self): self.check("map", {}) -- cgit v1.2.1 From 4227fe7613a91ee8817605044a3618d65bc23d76 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 28 May 2008 12:49:11 +0000 Subject: QPID-1095: fixes to dtx error codes for latest spec changes. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@660924 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/dtx.py | 123 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index d676b5806c..cea742b81c 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -199,7 +199,7 @@ class DtxTests(TestBase010): session1.dtx_rollback(xid=tx) #verification: - if failed: self.assertEquals(503, error.args[0].error_code) + if failed: self.assertEquals(530, error.args[0].error_code) else: self.fail("Xid already known, expected exception!") def test_forget_xid_on_completion(self): @@ -439,7 +439,7 @@ class DtxTests(TestBase010): if failed: self.session.dtx_rollback(xid=tx) - self.assertEquals(503, error.args[0].error_code) + self.assertEquals(409, error.args[0].error_code) else: tester.close() other.close() @@ -450,10 +450,6 @@ class DtxTests(TestBase010): Test that a commit with one_phase = False is rejected if the transaction in question has not yet been prepared. """ - """ - Test that a commit with one_phase = True is rejected if the - transaction in question has already been prepared. - """ other = self.connect() tester = other.session("tester", 1) tester.queue_declare(queue="dummy", exclusive=True, auto_delete=True) @@ -471,12 +467,97 @@ class DtxTests(TestBase010): if failed: self.session.dtx_rollback(xid=tx) - self.assertEquals(503, error.args[0].error_code) + self.assertEquals(409, error.args[0].error_code) else: tester.close() other.close() self.fail("Invalid use of one_phase=False, expected exception!") + def test_invalid_commit_not_ended(self): + """ + Test that a commit fails if the xid is still associated with a session. + """ + other = self.connect() + tester = other.session("tester", 1) + self.session.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + self.session.dtx_select() + tx = self.xid("dummy") + self.session.dtx_start(xid=tx) + self.session.message_transfer(self.createMessage(tester, "dummy", "dummy", "whatever")) + + failed = False + try: + tester.dtx_commit(xid=tx, one_phase=False) + except SessionException, e: + failed = True + error = e + + if failed: + self.session.dtx_end(xid=tx) + self.session.dtx_rollback(xid=tx) + self.assertEquals(409, error.args[0].error_code) + else: + tester.close() + other.close() + self.fail("Commit should fail as xid is still associated!") + + def test_invalid_rollback_not_ended(self): + """ + Test that a rollback fails if the xid is still associated with a session. + """ + other = self.connect() + tester = other.session("tester", 1) + self.session.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + self.session.dtx_select() + tx = self.xid("dummy") + self.session.dtx_start(xid=tx) + self.session.message_transfer(self.createMessage(tester, "dummy", "dummy", "whatever")) + + failed = False + try: + tester.dtx_rollback(xid=tx) + except SessionException, e: + failed = True + error = e + + if failed: + self.session.dtx_end(xid=tx) + self.session.dtx_rollback(xid=tx) + self.assertEquals(409, error.args[0].error_code) + else: + tester.close() + other.close() + self.fail("Rollback should fail as xid is still associated!") + + + def test_invalid_prepare_not_ended(self): + """ + Test that a prepare fails if the xid is still associated with a session. + """ + other = self.connect() + tester = other.session("tester", 1) + self.session.queue_declare(queue="dummy", exclusive=True, auto_delete=True) + self.session.dtx_select() + tx = self.xid("dummy") + self.session.dtx_start(xid=tx) + self.session.message_transfer(self.createMessage(tester, "dummy", "dummy", "whatever")) + + failed = False + try: + tester.dtx_prepare(xid=tx) + except SessionException, e: + failed = True + error = e + + if failed: + self.session.dtx_end(xid=tx) + self.session.dtx_rollback(xid=tx) + self.assertEquals(409, error.args[0].error_code) + else: + tester.close() + other.close() + self.fail("Rollback should fail as xid is still associated!") + def test_implicit_end(self): """ Test that an association is implicitly ended when the session @@ -601,6 +682,34 @@ class DtxTests(TestBase010): except SessionException, e: self.assertEquals(503, e.args[0].error_code) + def test_prepare_unknown(self): + session = self.session + try: + session.dtx_prepare(xid=self.xid("unknown")) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) + + def test_commit_unknown(self): + session = self.session + try: + session.dtx_commit(xid=self.xid("unknown")) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) + + def test_rollback_unknown(self): + session = self.session + try: + session.dtx_rollback(xid=self.xid("unknown")) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) + + def test_get_timeout_unknown(self): + session = self.session + try: + session.dtx_get_timeout(xid=self.xid("unknown")) + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) + def xid(self, txid): DtxTests.tx_counter += 1 branchqual = "v%s" % DtxTests.tx_counter -- cgit v1.2.1 From 744d39a42e3c19bd247276388b08744bb6e73089 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 28 May 2008 13:55:06 +0000 Subject: QPID-1095: another error code correction git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@660952 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/dtx.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index cea742b81c..796152794f 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -377,8 +377,7 @@ class DtxTests(TestBase010): session.dtx_end(xid=tx) self.fail("Attempted to end association with unknown xid, expected exception!") except SessionException, e: - #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming... - self.assertEquals(503, e.args[0].error_code) + self.assertEquals(409, e.args[0].error_code) def test_end(self): """ -- cgit v1.2.1 From 8e430931619f5778a15b56b7b6042eac18899637 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 28 May 2008 13:56:59 +0000 Subject: QPID-1098: correction to queue query when queue is not known git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@660953 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/query.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/query.py b/qpid/python/tests_0-10/query.py index 0bbfb079cc..311df84096 100644 --- a/qpid/python/tests_0-10/query.py +++ b/qpid/python/tests_0-10/query.py @@ -30,6 +30,11 @@ class QueryTests(TestBase010): result = session.queue_query(queue="my-queue") self.assertEqual("my-queue", result.queue) + def test_queue_query_unknown(self): + session = self.session + result = session.queue_query(queue="I don't exist") + self.assert_(not result.queue) + def test_exchange_query(self): """ Test that the exchange_query method works as expected -- cgit v1.2.1 From f72b278bcee746b01bc8334a0bd3775817db3162 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 2 Jun 2008 16:01:51 +0000 Subject: QPID-1113 Management cleanup and performance enhancements git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@662470 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 6 +++--- qpid/python/commands/qpid-queue-stats | 8 ++++---- qpid/python/commands/qpid-route | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 58a2272ed2..cc9315f7ea 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -145,7 +145,7 @@ class BrokerManager: print "=======================================================" for ex in exchanges: if self.match (ex.name, filter): - print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindings, ex.name) + print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindingCount, ex.name) def ExchangeListRecurse (self, filter): self.ConnectToBroker () @@ -185,12 +185,12 @@ class BrokerManager: fc = int (args[FILECOUNT]) print "%4c%9c%7c%10d%11dx%-14d%s" % \ (YN (q.durable), YN (q.autoDelete), - YN (q.exclusive), q.bindings, fc, fs, q.name) + YN (q.exclusive), q.bindingCount, fc, fs, q.name) else: if not _durable: print "%4c%9c%7c%10d %s" % \ (YN (q.durable), YN (q.autoDelete), - YN (q.exclusive), q.bindings, q.name) + YN (q.exclusive), q.bindingCount, q.name) def QueueListRecurse (self, filter): self.ConnectToBroker () diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 437714fec4..98dfa7580a 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -126,14 +126,14 @@ class BrokerManager: deltaTime = float (obj.timestamps[0] - lastSample.timestamps[0]) enqueueRate = float (obj.msgTotalEnqueues - lastSample.msgTotalEnqueues) / (deltaTime / 1000000000.0) dequeueRate = float (obj.msgTotalDequeues - lastSample.msgTotalDequeues) / (deltaTime / 1000000000.0) - print "%-41s%10.2f%10d..%-10d%13.2f%13.2f" % \ - (name, deltaTime / 1000000000, obj.msgDepthLow, obj.msgDepthHigh, enqueueRate, dequeueRate) + print "%-41s%10.2f%11d%13.2f%13.2f" % \ + (name, deltaTime / 1000000000, obj.msgDepth, enqueueRate, dequeueRate) def Display (self): self.ConnectToBroker () - print "Queue Name Sec Depth Range Enq Rate Deq Rate" - print "===================================================================================================" + print "Queue Name Sec Depth Enq Rate Deq Rate" + print "========================================================================================" try: while True: sleep (1) diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 4e8dbc3a77..2d5249ab78 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -201,14 +201,14 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." bridgeArgs = {} - bridgeArgs["durable"] = _durable - bridgeArgs["src"] = exchange - bridgeArgs["dest"] = exchange - bridgeArgs["key"] = routingKey - bridgeArgs["tag"] = id - bridgeArgs["excludes"] = excludes - bridgeArgs["src_is_queue"] = 0 - bridgeArgs["src_is_local"] = 0 + bridgeArgs["durable"] = _durable + bridgeArgs["src"] = exchange + bridgeArgs["dest"] = exchange + bridgeArgs["key"] = routingKey + bridgeArgs["tag"] = id + bridgeArgs["excludes"] = excludes + bridgeArgs["srcIsQueue"] = 0 + bridgeArgs["srcIsLocal"] = 0 res = mc.syncCallMethod (self.mch, link.id, link.classKey, "bridge", bridgeArgs) if res.status == 4: print "Can't create a durable route on a non-durable link" -- cgit v1.2.1 From e6a45336be3d9f5ddc4eae7bbca1c2c2e39ad840 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 4 Jun 2008 23:19:59 +0000 Subject: Management cleanup - renamed config/inst elements to properties and statistics git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@663413 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/managementdata.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index a0fa1ece64..d755057e1f 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -467,7 +467,7 @@ class ManagementData: for eIdx in range (len (config)): key = config[eIdx][0] if key != "id": - row = ("config", key) + row = ("property", key) for id in ids: if timestamp == None or \ timestamp < self.tables[classKey][id][0][0]: @@ -480,7 +480,7 @@ class ManagementData: for eIdx in range (len (inst)): key = inst[eIdx][0] if key != "id": - row = ("inst", key) + row = ("statistic", key) for id in ids: (key, value) = self.tables[classKey][id][2][eIdx] row = row + (self.valueDisplay (classKey, key, value),) @@ -511,7 +511,7 @@ class ManagementData: row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) rows.append (row) self.disp.table ("Classes in Schema:", - ("Class", "ConfigElements", "InstElements", "Methods", "Events"), + ("Class", "Properties", "Statistics", "Methods", "Events"), rows) finally: self.lock.release () -- cgit v1.2.1 From 57287a1db8500d0a308e2eb2be39c3e4262e81e2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 5 Jun 2008 17:51:34 +0000 Subject: cleanup old irrelevant tests (from 0-10 preview functions) fix dtx.recover test git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@663675 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10.txt | 13 --- qpid/python/tests_0-10/dtx.py | 7 +- qpid/python/tests_0-10/execution.py | 29 ----- qpid/python/tests_0-10/message.py | 207 ------------------------------------ qpid/python/tests_0-10/testlib.py | 66 ------------ 5 files changed, 2 insertions(+), 320 deletions(-) delete mode 100644 qpid/python/tests_0-10/execution.py delete mode 100644 qpid/python/tests_0-10/testlib.py (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt index cc1e57bca0..e69de29bb2 100644 --- a/qpid/python/cpp_failing_0-10.txt +++ b/qpid/python/cpp_failing_0-10.txt @@ -1,13 +0,0 @@ -tests.codec.FieldTableTestCase.test_field_table_decode -tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair -tests.codec.FieldTableTestCase.test_field_table_name_value_pair -tests_0-10.execution.ExecutionTests.test_flush -tests_0-10.dtx.DtxTests.test_recover -tests_0-10.message.MessageTests.test_no_size -tests_0-10.message.MessageTests.test_qos_prefetch_count -tests_0-10.message.MessageTests.test_qos_prefetch_size -tests_0-10.message.MessageTests.test_recover -tests_0-10.message.MessageTests.test_recover_requeue -tests_0-10.testlib.TestBaseTest.testAssertEmptyFail -tests_0-10.testlib.TestBaseTest.testAssertEmptyPass -tests_0-10.testlib.TestBaseTest.testMessageProperties diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 796152794f..25c2defd3b 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -653,22 +653,19 @@ class DtxTests(TestBase010): session.dtx_rollback(xid=tx) xids = session.dtx_recover().in_doubt - print "xids=%s" % xids #rollback the prepared transactions returned by recover for x in xids: session.dtx_rollback(xid=x) #validate against the expected list of prepared transactions - actual = set(xids) - expected = set(prepared) + actual = set([x.global_id for x in xids]) #TODO: come up with nicer way to test these + expected = set([x.global_id for x in prepared]) intersection = actual.intersection(expected) if intersection != expected: missing = expected.difference(actual) extra = actual.difference(expected) - for x in missing: - session.dtx_rollback(xid=x) self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra)) def test_bad_resume(self): diff --git a/qpid/python/tests_0-10/execution.py b/qpid/python/tests_0-10/execution.py deleted file mode 100644 index 3ff6d8ea65..0000000000 --- a/qpid/python/tests_0-10/execution.py +++ /dev/null @@ -1,29 +0,0 @@ -# -# 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. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase - -class ExecutionTests (TestBase): - def test_flush(self): - channel = self.channel - for i in [1, 2, 3]: - channel.message_transfer( - content=Content(properties={'routing_key':str(i)})) - assert(channel.completion.wait(channel.completion.command_id, timeout=1)) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 8f3d7bdaef..e4dc5566bd 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -304,199 +304,6 @@ class MessageTests(TestBase010): self.fail("Got unexpected message: " + extra.body) except Empty: None - - def test_recover(self): - """ - Test recover behaviour - """ - session = self.session - session.queue_declare(queue="queue-a", exclusive=True, auto_delete=True) - session.queue_bind(exchange="amq.fanout", queue="queue-a") - session.queue_declare(queue="queue-b", exclusive=True, auto_delete=True) - session.queue_bind(exchange="amq.fanout", queue="queue-b") - - self.subscribe(queue="queue-a", destination="unconfirmed", confirm_mode=1) - self.subscribe(queue="queue-b", destination="confirmed", confirm_mode=0) - confirmed = session.incoming("confirmed") - unconfirmed = session.incoming("unconfirmed") - - data = ["One", "Two", "Three", "Four", "Five"] - for d in data: - session.message_transfer(destination="amq.fanout", content=Content(body=d)) - - for q in [confirmed, unconfirmed]: - for d in data: - self.assertEqual(d, q.get(timeout=1).content.body) - self.assertEmpty(q) - - session.message_recover(requeue=False) - - self.assertEmpty(confirmed) - - while len(data): - msg = None - for d in data: - msg = unconfirmed.get(timeout=1) - self.assertEqual(d, msg.body) - self.assertEqual(True, msg.content['redelivered']) - self.assertEmpty(unconfirmed) - data.remove(msg.body) - msg.complete(cumulative=False) - session.message_recover(requeue=False) - - - def test_recover_requeue(self): - """ - Test requeing on recovery - """ - session = self.session - session.queue_declare(queue="test-requeue", exclusive=True, auto_delete=True) - - self.subscribe(queue="test-requeue", destination="consumer_tag", confirm_mode=1) - queue = session.incoming("consumer_tag") - - session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="One")) - session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Two")) - session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Three")) - session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Four")) - session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Five")) - - msg1 = queue.get(timeout=1) - msg2 = queue.get(timeout=1) - msg3 = queue.get(timeout=1) - msg4 = queue.get(timeout=1) - msg5 = queue.get(timeout=1) - - self.assertEqual("One", msg1.content.body) - self.assertEqual("Two", msg2.content.body) - self.assertEqual("Three", msg3.content.body) - self.assertEqual("Four", msg4.content.body) - self.assertEqual("Five", msg5.content.body) - - msg2.complete(cumulative=True) #One and Two - msg4.complete(cumulative=False) #Four - - session.message_cancel(destination="consumer_tag") - - #publish a new message - session.message_transfer(content=Content(properties={'routing_key' : "test-requeue"}, body="Six")) - #requeue unacked messages (Three and Five) - session.message_recover(requeue=True) - - self.subscribe(queue="test-requeue", destination="consumer_tag") - queue2 = session.incoming("consumer_tag") - - msg3b = queue2.get(timeout=1) - msg5b = queue2.get(timeout=1) - - self.assertEqual("Three", msg3b.content.body) - self.assertEqual("Five", msg5b.content.body) - - self.assertEqual(True, msg3b.content['redelivered']) - self.assertEqual(True, msg5b.content['redelivered']) - - self.assertEqual("Six", queue2.get(timeout=1).content.body) - - try: - extra = queue2.get(timeout=1) - self.fail("Got unexpected message in second queue: " + extra.content.body) - except Empty: None - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected message in original queue: " + extra.content.body) - except Empty: None - - - def test_qos_prefetch_count(self): - """ - Test that the prefetch count specified is honoured - """ - #setup: declare queue and subscribe - session = self.session - session.queue_declare(queue="test-prefetch-count", exclusive=True, auto_delete=True) - subscription = self.subscribe(queue="test-prefetch-count", destination="consumer_tag", confirm_mode=1) - queue = session.incoming("consumer_tag") - - #set prefetch to 5: - session.message_qos(prefetch_count=5) - - #publish 10 messages: - for i in range(1, 11): - session.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-count"}, body="Message %d" % i)) - - #only 5 messages should have been delivered: - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - msg.complete() - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - msg.complete() - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - - - def test_qos_prefetch_size(self): - """ - Test that the prefetch size specified is honoured - """ - #setup: declare queue and subscribe - session = self.session - session.queue_declare(queue="test-prefetch-size", exclusive=True, auto_delete=True) - subscription = self.subscribe(queue="test-prefetch-size", destination="consumer_tag", confirm_mode=1) - queue = session.incoming("consumer_tag") - - #set prefetch to 50 bytes (each message is 9 or 10 bytes): - session.message_qos(prefetch_size=50) - - #publish 10 messages: - for i in range(1, 11): - session.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body="Message %d" % i)) - - #only 5 messages should have been delivered (i.e. 45 bytes worth): - for i in range(1, 6): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 6th message in original queue: " + extra.content.body) - except Empty: None - - #ack messages and check that the next set arrive ok: - msg.complete() - - for i in range(6, 11): - msg = queue.get(timeout=1) - self.assertEqual("Message %d" % i, msg.body) - - msg.complete() - - try: - extra = queue.get(timeout=1) - self.fail("Got unexpected 11th message in original queue: " + extra.content.body) - except Empty: None - - #make sure that a single oversized message still gets delivered - large = "abcdefghijklmnopqrstuvwxyz" - large = large + "-" + large; - session.message_transfer(content=Content(properties={'routing_key' : "test-prefetch-size"}, body=large)) - msg = queue.get(timeout=1) - self.assertEqual(large, msg.body) - def test_reject(self): session = self.session session.queue_declare(queue = "q", exclusive=True, auto_delete=True, alternate_exchange="amq.fanout") @@ -972,20 +779,6 @@ class MessageTests(TestBase010): #ensure there are no other messages self.assertEmpty(queueC) - def test_no_size(self): - self.queue_declare(queue = "q", exclusive=True, auto_delete=True) - - ssn = self.session - ssn.message_transfer(content=SizelessContent(properties={'routing_key' : "q"}, body="message-body")) - - ssn.message_subscribe(queue = "q", destination="d", confirm_mode = 0) - ssn.message_flow(unit = ssn.credit_unit.message, value = 0xFFFFFFFF, destination = "d") - ssn.message_flow(unit = ssn.credit_unit.byte, value = 0xFFFFFFFF, destination = "d") - - queue = session.incoming("d") - msg = queue.get(timeout = 3) - self.assertEquals("message-body", msg.body) - def test_empty_body(self): session = self.session session.queue_declare(queue="xyz", exclusive=True, auto_delete=True) diff --git a/qpid/python/tests_0-10/testlib.py b/qpid/python/tests_0-10/testlib.py deleted file mode 100644 index a0355c4ce0..0000000000 --- a/qpid/python/tests_0-10/testlib.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# 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. -# - -# -# Tests for the testlib itself. -# - -from qpid.content import Content -from qpid.testlib import testrunner, TestBase -from Queue import Empty - -import sys -from traceback import * - -def mytrace(frame, event, arg): - print_stack(frame); - print "====" - return mytrace - -class TestBaseTest(TestBase): - """Verify TestBase functions work as expected""" - - def testAssertEmptyPass(self): - """Test assert empty works""" - self.queue_declare(queue="empty") - q = self.consume("empty") - self.assertEmpty(q) - try: - q.get(timeout=1) - self.fail("Queue is not empty.") - except Empty: None # Ignore - - def testAssertEmptyFail(self): - self.queue_declare(queue="full") - q = self.consume("full") - self.channel.message_transfer(content=Content("", properties={'routing_key':"full"})) - try: - self.assertEmpty(q); - self.fail("assertEmpty did not assert on non-empty queue") - except AssertionError: None # Ignore - - def testMessageProperties(self): - """Verify properties are passed with message""" - props={"x":1, "y":2} - self.queue_declare(queue="q") - q = self.consume("q") - self.assertPublishGet(q, routing_key="q", properties=props) - - - -- cgit v1.2.1 From bd82edd601db8e788ebbdcbfa9c0b0aa3cedcc8f Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Thu, 5 Jun 2008 20:41:54 +0000 Subject: Minor additions to Range and RangedSet git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@663730 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/datatypes.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 7249d5621b..7150caded2 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -168,9 +168,15 @@ class Range: def __contains__(self, n): return self.lower <= n and n <= self.upper + + def __iter__(self): + i = self.lower + while i <= self.upper: + yield i + i += 1 def touches(self, r): - # XXX + # XXX: are we doing more checks than we need? return (self.lower - 1 in r or self.upper + 1 in r or r.lower - 1 in self or @@ -223,6 +229,9 @@ class RangedSet: def add(self, lower, upper = None): self.add_range(Range(lower, upper)) + + def __iter__(self): + return iter(self.ranges) def __repr__(self): return str(self.ranges) -- cgit v1.2.1 From 76ac0b8a8d74007d20cd1719086d964d8e394937 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 10 Jun 2008 14:47:54 +0000 Subject: Removed import of deleted test modules. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@666138 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/__init__.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/__init__.py b/qpid/python/tests_0-10/__init__.py index fe96d9e122..1fd7f72357 100644 --- a/qpid/python/tests_0-10/__init__.py +++ b/qpid/python/tests_0-10/__init__.py @@ -24,9 +24,7 @@ from broker import * from dtx import * from example import * from exchange import * -from execution import * from message import * from query import * from queue import * -from testlib import * from tx import * -- cgit v1.2.1 From 06e1426e91903056871d693aab9049f203b7b330 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 10 Jun 2008 15:13:09 +0000 Subject: updated the hello-world script git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@666146 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-010-world | 53 --------------------------------------------- qpid/python/hello-world | 52 +++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 75 deletions(-) delete mode 100755 qpid/python/hello-010-world (limited to 'qpid/python') diff --git a/qpid/python/hello-010-world b/qpid/python/hello-010-world deleted file mode 100755 index a15817cd9d..0000000000 --- a/qpid/python/hello-010-world +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -import sys -from qpid.connection010 import Connection -from qpid.spec010 import load -from qpid.util import connect -from qpid.datatypes import Message, RangedSet -from qpid.log import enable, DEBUG, WARN - -if "-v" in sys.argv: - level = DEBUG -else: - level = WARN - -enable("qpid.io.ctl", level) -enable("qpid.io.cmd", level) - -spec = load("../specs/amqp.0-10.xml") -conn = Connection(connect("0.0.0.0", spec.port), spec) -conn.start(timeout=10) - -ssn = conn.session("my-session", timeout=10) - -ssn.queue_declare("asdf") - - -ssn.message_transfer("this", None, None, Message("testing...")) -props = ssn.delivery_properties(routing_key="asdf") -ssn.message_transfer("is", None, None, Message(props, "more testing...")) -ssn.message_transfer("a") -ssn.message_transfer("test") - -m1 = ssn.incoming("this").get(timeout=10) -print m1 -m2 = ssn.incoming("is").get(timeout=10) -print m2 -m3 = ssn.incoming("a").get(timeout=10) -print m3 -m4 = ssn.incoming("test").get(timeout=10) -print m4 - -print ssn.sender._completed, ssn.sender.next_id -ssn.sync(10) -print ssn.sender.segments - -ssn.channel.session_flush(completed=True) - -ssn.message_accept(RangedSet(m1.id, m2.id, m3.id, m4.id)) - -print ssn.queue_query("testing") - -ssn.close(timeout=10) -conn.close(timeout=10) diff --git a/qpid/python/hello-world b/qpid/python/hello-world index 518992409e..cfcaa962d7 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -1,25 +1,33 @@ #!/usr/bin/env python -import qpid -from qpid.client import Client -from qpid.content import Content +from qpid.connection import Connection +from qpid.util import connect +from qpid.datatypes import uuid4, Message -client = Client("127.0.0.1", 5672) -client.start({"LOGIN": "guest", "PASSWORD": "guest"}) -ssn = client.session() -ssn.open() -ssn.queue_declare(queue="test") -ssn.queue_bind(exchange="amq.direct", queue="test", routing_key="test") -#print ssn.queue_query(queue="test") -ssn.message_subscribe(queue="test", destination="amq.direct") -ssn.message_flow("amq.direct", 0, 0xFFFFFFFF) -ssn.message_flow("amq.direct", 1, 0xFFFFFFFF) -msg = Content("hello world") -msg["content_type"] = "text/plain" -msg["routing_key"] = "test" -msg["reply_to"] = client.structs.reply_to("asdf", "fdsa") -msg["application_headers"] = {"x": 1, "y": 2, "z": "zee"} -ssn.message_transfer(destination="amq.direct", content=msg) -queue = client.queue("amq.direct") -msg = queue.get(timeout=10) -print msg +# connect to the server and start a session +conn = Connection(connect("127.0.0.1", 5672)) +conn.start() +ssn = conn.session(str(uuid4())) + +# create a queue +ssn.queue_declare("test-queue") + +# publish a message +dp = ssn.delivery_properties(routing_key="test-queue") +mp = ssn.message_properties(content_type="text/plain") +msg = Message(dp, "Hello World!") +ssn.message_transfer(message=msg) + +# subscribe to a queue +ssn.message_subscribe(queue="test-queue", destination="messages") +incoming = ssn.incoming("messages") + +# start incoming message flow +incoming.start() + +# grab a message from the queue +print incoming.get(timeout=10) + +# cancel the subscription and close the session and connection +ssn.message_cancel(destination="messages") ssn.close() +conn.close() -- cgit v1.2.1 From dba2ac60fa67c418fe8f93eafbf33404affb5703 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 10 Jun 2008 18:08:59 +0000 Subject: updated hello-world git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@666244 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index cfcaa962d7..5d513cc57b 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -14,17 +14,19 @@ ssn.queue_declare("test-queue") # publish a message dp = ssn.delivery_properties(routing_key="test-queue") mp = ssn.message_properties(content_type="text/plain") -msg = Message(dp, "Hello World!") +msg = Message(dp, mp, "Hello World!") ssn.message_transfer(message=msg) # subscribe to a queue -ssn.message_subscribe(queue="test-queue", destination="messages") +ssn.message_subscribe(destination="messages", queue="test-queue", + accept_mode=ssn.accept_mode.none) incoming = ssn.incoming("messages") # start incoming message flow incoming.start() # grab a message from the queue + print incoming.get(timeout=10) # cancel the subscription and close the session and connection -- cgit v1.2.1 From 38ac6f27ea2cb49b0161202e13f222875e5e2b44 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 11 Jun 2008 17:32:34 +0000 Subject: load the old version of the spec file for old codec tests, removed unused test exclude list git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@666743 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/cpp_failing_0-10_preview.txt | 6 ------ qpid/python/tests/codec.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 qpid/python/cpp_failing_0-10_preview.txt (limited to 'qpid/python') diff --git a/qpid/python/cpp_failing_0-10_preview.txt b/qpid/python/cpp_failing_0-10_preview.txt deleted file mode 100644 index 91c2a7fce4..0000000000 --- a/qpid/python/cpp_failing_0-10_preview.txt +++ /dev/null @@ -1,6 +0,0 @@ -tests.codec.FieldTableTestCase.test_field_table_decode -tests.codec.FieldTableTestCase.test_field_table_multiple_name_value_pair -tests.codec.FieldTableTestCase.test_field_table_name_value_pair -tests_0-10_preview.alternate_exchange.AlternateExchangeTests.test_immediate -tests_0-10_preview.broker.BrokerTests.test_closed_channel - diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py index e23c3deca1..4bd3675af8 100644 --- a/qpid/python/tests/codec.py +++ b/qpid/python/tests/codec.py @@ -59,7 +59,7 @@ SPEC = None def spec(): global SPEC if SPEC == None: - SPEC = load(testrunner.get_spec_file("amqp.0-10-preview.xml")) + SPEC = load(testrunner.get_spec_file("amqp.0-8.xml")) return SPEC # -------------------------------------- -- cgit v1.2.1 From 2becbf6ae901b1c7aecebe95087fd308fc611627 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 11 Jun 2008 21:31:12 +0000 Subject: replaced example usages of message_flow with the start() method git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@666850 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/direct_consumer.py | 5 ++--- qpid/python/examples/direct/listener.py | 3 +-- qpid/python/examples/fanout/fanout_consumer.py | 5 ++--- qpid/python/examples/fanout/listener.py | 5 ++--- qpid/python/examples/pubsub/topic_subscriber.py | 3 +-- qpid/python/examples/request-response/client.py | 3 +-- qpid/python/examples/request-response/server.py | 6 ++---- qpid/python/examples/xml-exchange/listener.py | 5 ++--- qpid/python/examples/xml-exchange/xml_consumer.py | 5 ++--- 9 files changed, 15 insertions(+), 25 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index e421a417c1..23577e9f53 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -51,11 +51,10 @@ queue = session.incoming(local_queue_name) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will # start delivering messages as soon as credit is allocated using -# session.message_flow(). +# queue.start(). session.message_subscribe(queue="message_queue", destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +queue.start() # Initialize 'final' and 'content', variables used to identify the last message. diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index 0716adecef..66927eca4b 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -74,8 +74,7 @@ queue = session.incoming(local_queue_name) # start delivering messages as soon as message_subscribe() is called. session.message_subscribe(queue="message_queue", destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +queue.start() receiver = Receiver() queue.listen (receiver.Handler) diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index 4a0e8ef488..a2b1b30141 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -51,12 +51,11 @@ session.exchange_bind(queue=server_queue_name, exchange="amq.fanout") local_queue_name = "local_queue" local_queue = session.incoming(local_queue_name) -# Call message_consume() to tell the server to deliver messages +# Call message_subscribe() to tell the server to deliver messages # from the AMQP queue to this local client queue. session.message_subscribe(queue=server_queue_name, destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +local_queue.start() print "Subscribed to queue " + server_queue_name sys.stdout.flush() diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index b2ed85045b..74ae858127 100755 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -79,11 +79,10 @@ local_queue = session.incoming(local_queue_name) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_subscribe() is called. +# start delivering messages as soon as local_queue.start() is called. session.message_subscribe(queue=server_queue_name, destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +local_queue.start() receiver = Receiver () local_queue.listen (receiver.Handler) diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index 4ba8e6a680..039cc0c55b 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -41,8 +41,7 @@ def subscribe_queue(server_queue_name, local_queue_name): queue = session.incoming(local_queue_name) session.message_subscribe(queue=server_queue_name, destination=local_queue_name) - session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) - session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) + queue.start() return queue diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 8d40aca5bb..a9ecd5c78f 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -81,8 +81,7 @@ queue = session.incoming(local_queue_name) # available. session.message_subscribe(queue=reply_to, destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +queue.start() # Send some messages to the server's request queue diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 99b1431509..05ee051c57 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -60,9 +60,9 @@ session.exchange_bind(exchange="amq.direct", queue="request", binding_key="reque local_queue_name = "local_queue" session.message_subscribe(queue="request", destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +queue = session.incoming(local_queue_name) +queue.start() # Remind the user to start the client program @@ -72,8 +72,6 @@ sys.stdout.flush() # Respond to each request -queue = session.incoming(local_queue_name) - # If we get a message, send it back to the user (as indicated in the # ReplyTo property) diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py index db12d57e3e..dec824dddf 100755 --- a/qpid/python/examples/xml-exchange/listener.py +++ b/qpid/python/examples/xml-exchange/listener.py @@ -68,11 +68,10 @@ local_queue = session.incoming(local_queue_name) # Call message_subscribe() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_subscribe() is called. +# start delivering messages as soon as local_queue.start() is called. session.message_subscribe(queue="message_queue", destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +local_queue.start() receiver = Receiver () local_queue.listen (receiver.Handler) diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py index 22973b2bd9..0ab079e7a6 100755 --- a/qpid/python/examples/xml-exchange/xml_consumer.py +++ b/qpid/python/examples/xml-exchange/xml_consumer.py @@ -51,11 +51,10 @@ local_queue = session.incoming(local_queue_name) # Call message_consume() to tell the broker to deliver messages # from the AMQP queue to this local client queue. The broker will -# start delivering messages as soon as message_consume() is called. +# start delivering messages as soon as local_queue.start() is called. session.message_subscribe(queue="message_queue", destination=local_queue_name) -session.message_flow(local_queue_name, session.credit_unit.message, 0xFFFFFFFF) -session.message_flow(local_queue_name, session.credit_unit.byte, 0xFFFFFFFF) +local_queue.start() # Initialize 'final' and 'content', variables used to identify the last message. -- cgit v1.2.1 From eb065d21914a9047829887e629ec4b8cd9380c65 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 16 Jun 2008 13:17:11 +0000 Subject: Bugfix: usage line did not show with --help option git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@668151 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-tool | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index bf16faac42..0ab47a01e7 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -140,7 +140,7 @@ class Mcli (Cmd): self.dataObject.close () def Usage (): - print "Usage:", sys.argv[0], "[OPTIONS] []]" + print "Usage: qpid-tool []]" print sys.exit (1) @@ -150,12 +150,14 @@ def Usage (): # Get host name and port if specified on the command line cargs = sys.argv[1:] - -_host = "localhost" +_host = "localhost" if len (cargs) > 0: _host = cargs[0] +if _host[0] == '-': + Usage() + disp = Display () # Attempt to make a connection to the target broker -- cgit v1.2.1 From ead2afe07e0d0b3ba383cca4ea77177218b4d080 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 16 Jun 2008 23:20:52 +0000 Subject: add enough to the server delegate to permit java clients to connect git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@668343 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegates.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index a284fb3b59..69b93e73dc 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -80,6 +80,9 @@ class Delegate: def session_detached(self, ch, d): self.connection.detach(d.name, ch) + def session_request_timeout(self, ch, rt): + ch.session_timeout(rt.timeout); + def session_command_point(self, ch, cp): ssn = ch.session ssn.receiver.next_id = cp.command_id @@ -112,10 +115,10 @@ class Server(Delegate): def start(self): self.connection.read_header() self.connection.write_header(self.spec.major, self.spec.minor) - connection.Channel(self.connection, 0).connection_start() + connection.Channel(self.connection, 0).connection_start(mechanisms=["ANONYMOUS"]) def connection_start_ok(self, ch, start_ok): - ch.connection_tune() + ch.connection_tune(channel_max=65535) def connection_tune_ok(self, ch, tune_ok): pass -- cgit v1.2.1 From 4d9b122efd40ea20ceb4dd9927cb0e0af1a2ed96 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 16 Jun 2008 23:25:01 +0000 Subject: QPID-1142: made session.sync() always set the sync flag on execution_sync git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@668344 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 10 ++++++---- qpid/python/tests/connection.py | 20 +++++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 3d8c0aa7fd..2f70461ab6 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -91,10 +91,11 @@ class Session(Invoker): return tuple(exc) def sync(self, timeout=None): - if currentThread() == self.channel.connection.thread: + ch = self.channel + if ch is not None and currentThread() == ch.connection.thread: raise SessionException("deadlock detected") if not self.auto_sync: - self.execution_sync() + self.execution_sync(sync=True) last = self.sender.next_id - 1 if not wait(self.condition, lambda: last in self.sender._completed or self.exceptions, @@ -174,10 +175,11 @@ class Session(Invoker): else: message = None + hdr = Struct(self.spec["session.header"]) + hdr.sync = self.auto_sync or kwargs.pop("sync", False) + cmd = type.new(args, kwargs) sc = StringCodec(self.spec) - hdr = Struct(self.spec["session.header"]) - hdr.sync = self.auto_sync sc.write_command(hdr, cmd) seg = Segment(True, (message == None or diff --git a/qpid/python/tests/connection.py b/qpid/python/tests/connection.py index 88620bc1c6..23e0c937fb 100644 --- a/qpid/python/tests/connection.py +++ b/qpid/python/tests/connection.py @@ -48,6 +48,9 @@ class TestSession(Delegate): self.session = session self.queue = queue + def execution_sync(self, es): + pass + def queue_query(self, qq): return qq._type.result.type.new((qq.queue,), {}) @@ -90,8 +93,11 @@ class ConnectionTest(TestCase): connect("0.0.0.0", PORT).close() self.server.join(3) + def connect(self): + return Connection(connect("0.0.0.0", PORT), self.spec) + def test(self): - c = Connection(connect("0.0.0.0", PORT), self.spec) + c = self.connect() c.start(10) ssn1 = c.session("test1", timeout=10) @@ -144,7 +150,7 @@ class ConnectionTest(TestCase): c.close(5) def testCloseGet(self): - c = Connection(connect("0.0.0.0", PORT), self.spec) + c = self.connect() c.start(10) ssn = c.session("test", timeout=10) echos = ssn.incoming("echo") @@ -166,7 +172,7 @@ class ConnectionTest(TestCase): pass def testCloseListen(self): - c = Connection(connect("0.0.0.0", PORT), self.spec) + c = self.connect() c.start(10) ssn = c.session("test", timeout=10) echos = ssn.incoming("echo") @@ -198,3 +204,11 @@ class ConnectionTest(TestCase): assert m.body == "test%d" % i assert len(exceptions) == 1 + + def testSync(self): + c = self.connect() + c.start(10) + s = c.session("test") + s.auto_sync = False + s.message_transfer("echo", message=Message("test")) + s.sync(10) -- cgit v1.2.1 From 032a0103e4d168f7856fc7cb26a49675a56b1ba6 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 16 Jun 2008 23:27:54 +0000 Subject: QPID-1143: added buffering, we now only issue one write per assembly git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@668345 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/framer.py | 20 ++++++++++++++++++-- qpid/python/tests/framer.py | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py index 27ea3287f0..f6363b2291 100644 --- a/qpid/python/qpid/framer.py +++ b/qpid/python/qpid/framer.py @@ -20,7 +20,7 @@ import struct, socket from exceptions import Closed from packer import Packer -from threading import Lock +from threading import RLock from logging import getLogger raw = getLogger("qpid.io.raw") @@ -75,12 +75,25 @@ class Framer(Packer): def __init__(self, sock): self.sock = sock - self.sock_lock = Lock() + self.sock_lock = RLock() + self._buf = "" def aborted(self): return False def write(self, buf): + self._buf += buf + + def flush(self): + self.sock_lock.acquire() + try: + self._write(self._buf) + self._buf = "" + frm.debug("FLUSHED") + finally: + self.sock_lock.release() + + def _write(self, buf): while buf: try: n = self.sock.send(buf) @@ -120,6 +133,7 @@ class Framer(Packer): self.sock_lock.acquire() try: self.pack(Framer.HEADER, "AMQP", 1, 1, major, minor) + self.flush() finally: self.sock_lock.release() @@ -130,6 +144,8 @@ class Framer(Packer): track = frame.track & 0x0F self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel) self.write(frame.payload) + if frame.isLastSegment() and frame.isLastFrame(): + self.flush() frm.debug("SENT %s", frame) finally: self.sock_lock.release() diff --git a/qpid/python/tests/framer.py b/qpid/python/tests/framer.py index ea2e04e954..05bb467bbe 100644 --- a/qpid/python/tests/framer.py +++ b/qpid/python/tests/framer.py @@ -37,6 +37,7 @@ class FramerTest(TestCase): while True: frame = conn.read_frame() conn.write_frame(frame) + conn.flush() except Closed: pass @@ -60,6 +61,7 @@ class FramerTest(TestCase): c.write_frame(Frame(0, 1, 2, 3, "IS")) c.write_frame(Frame(0, 1, 2, 3, "A")) c.write_frame(Frame(LAST_FRM, 1, 2, 3, "TEST")) + c.flush() f = c.read_frame() assert f.flags & FIRST_FRM -- cgit v1.2.1 From bf59f162c3c7f310c8480dbc9e4fa0018e8f2159 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 17 Jun 2008 02:15:45 +0000 Subject: QPID-1143: removed race condition in test git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@668378 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/alternate_exchange.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index 9cf331c110..aac8a5e15b 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -112,12 +112,13 @@ class AlternateExchangeTests(TestBase010): session.exchange_declare(exchange="alternate", type="fanout") session2 = self.conn.session("alternate", 2) - session2.queue_declare(queue="q", exclusive=True, auto_delete=True, alternate_exchange="alternate") + session2.queue_declare(queue="q", alternate_exchange="alternate") try: session2.exchange_delete(exchange="alternate") self.fail("Expected deletion of in-use alternate-exchange to fail") except SessionException, e: session = self.session + session.queue_delete(queue="q") session.exchange_delete(exchange="alternate") self.assertEquals(530, e.args[0].error_code) -- cgit v1.2.1 From bec9cf4d06d953569ccdfdb60c5221ab64117369 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 30 Jun 2008 18:24:51 +0000 Subject: Switch to async mode for management communication git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@672854 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 1 + 1 file changed, 1 insertion(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 2ca433e6d2..55479de0e6 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -105,6 +105,7 @@ class managementChannel: self.reqsOutstanding = 0 self.brokerInfo = None + ssn.auto_sync = False ssn.queue_declare (queue=self.topicName, exclusive=True, auto_delete=True) ssn.queue_declare (queue=self.replyName, exclusive=True, auto_delete=True) -- cgit v1.2.1 From 061d6a61e73c8d4e43a711e526d6586db9f54c01 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 30 Jun 2008 18:25:17 +0000 Subject: Cosmetic change: rename ID to be 'tag' git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@672855 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 2d5249ab78..3cd9109a6a 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -35,7 +35,7 @@ def Usage (): print " qpid-route [OPTIONS] link del " print " qpid-route [OPTIONS] link list []" print - print " qpid-route [OPTIONS] route add [id] [exclude-list]" + print " qpid-route [OPTIONS] route add [tag] [exclude-list]" print " qpid-route [OPTIONS] route del " print " qpid-route [OPTIONS] route list []" print " qpid-route [OPTIONS] route flush []" @@ -154,7 +154,7 @@ class RouteManager: for link in links: print "%-16s%-8d %c %-18s%s" % (link.host, link.port, YN(link.durable), link.state, link.lastError) - def AddRoute (self, srcBroker, exchange, routingKey, id, excludes): + def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes): self.src = Broker (srcBroker) mc = self.mclient @@ -205,7 +205,7 @@ class RouteManager: bridgeArgs["src"] = exchange bridgeArgs["dest"] = exchange bridgeArgs["key"] = routingKey - bridgeArgs["tag"] = id + bridgeArgs["tag"] = tag bridgeArgs["excludes"] = excludes bridgeArgs["srcIsQueue"] = 0 bridgeArgs["srcIsLocal"] = 0 @@ -351,11 +351,11 @@ elif group == "route": if nargs < 6 or nargs > 8: Usage () - id = "" + tag = "" excludes = "" - if nargs > 6: id = cargs[6] + if nargs > 6: tag = cargs[6] if nargs > 7: excludes = cargs[7] - rm.AddRoute (cargs[3], cargs[4], cargs[5], id, excludes) + rm.AddRoute (cargs[3], cargs[4], cargs[5], tag, excludes) elif cmd == "del": if nargs != 6: Usage () -- cgit v1.2.1 From 2ebe3bcb668151cfd9a860e4416fe4478d9a56f4 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 11 Jul 2008 20:14:07 +0000 Subject: QPID-1174 Remote Management Agent for management of external components git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@676067 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/managementdata.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index d755057e1f..4c34b514d4 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -71,24 +71,22 @@ class ManagementData: # def registerObjId (self, objId): - boot = objId & 0x7FFF000000000000L - if boot == 0: - return - self.bootSequence = boot + if not objId in self.idBackMap: + self.idBackMap[objId] = self.nextId + self.idMap[self.nextId] = objId + self.nextId += 1 def displayObjId (self, objId): - bank = (objId & 0x0000FFFFFF000000L) >> 24 - id = objId & 0x0000000000FFFFFFL - return bank * 10000 + id + if objId in self.idBackMap: + return self.idBackMap[objId] + else: + return 0 def rawObjId (self, displayId): - bank = displayId / 10000 - id = displayId % 10000 - if bank < 5: - objId = (bank << 24) + id + if displayId in self.idMap: + return self.idMap[displayId] else: - objId = self.bootSequence + (bank << 24) + id - return objId + return 0 def displayClassName (self, cls): (packageName, className, hash) = cls @@ -201,6 +199,9 @@ class ManagementData: self.mclient.schemaListener (self.schemaHandler) self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) self.operational = True + self.idMap = {} + self.idBackMap = {} + self.nextId = 101 def close (self): pass -- cgit v1.2.1 From 12b33f499c8a33d5010fedecdb267c721483f0a5 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 31 Jul 2008 13:15:16 +0000 Subject: QPID-1174 - Management updates for remote agents git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@681362 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-tool | 4 ++ qpid/python/qpid/management.py | 15 ++++--- qpid/python/qpid/managementdata.py | 92 +++++++++++++++++++++++++++++++------- 3 files changed, 87 insertions(+), 24 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 0ab47a01e7..7301dad6e4 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -65,6 +65,7 @@ class Mcli (Cmd): print " schema - Print details of an object class" print " set time-format short - Select short timestamp format (default)" print " set time-format long - Select long timestamp format" + print " id [] - Display translations of display object ids" print " quit or ^D - Exit the program" print @@ -93,6 +94,9 @@ class Mcli (Cmd): except: pass + def do_id (self, data): + self.dataObject.do_id(data) + def complete_schema (self, text, line, begidx, endidx): tokens = split (line) if len (tokens) > 2: diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 55479de0e6..1059c70ada 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -154,7 +154,7 @@ class managementChannel: def accept (self, msg): self.qpidChannel.message_accept(RangedSet(msg.id)) - def message (self, body, routing_key="agent"): + def message (self, body, routing_key="broker"): dp = self.qpidChannel.delivery_properties() dp.routing_key = routing_key mp = self.qpidChannel.message_properties() @@ -227,14 +227,14 @@ class managementClient: """ Invoke a method on a managed object. """ self.method (channel, userSequence, objId, className, methodName, args) - def getObjects (self, channel, userSequence, className): + def getObjects (self, channel, userSequence, className, bank=0): """ Request immediate content from broker """ codec = Codec (self.spec) self.setHeader (codec, ord ('G'), userSequence) ft = {} ft["_class"] = className codec.write_map (ft) - msg = channel.message(codec.encoded) + msg = channel.message(codec.encoded, routing_key="agent.%d" % bank) channel.send ("qpid.management", msg) def syncWaitForStable (self, channel): @@ -273,14 +273,14 @@ class managementClient: self.cv.release () return result - def syncGetObjects (self, channel, className): + def syncGetObjects (self, channel, className, bank=0): """ Synchronous (blocking) get call """ self.cv.acquire () self.syncInFlight = True self.syncResult = [] self.syncSequence = self.seqMgr.reserve ("sync") self.cv.release () - self.getObjects (channel, self.syncSequence, className) + self.getObjects (channel, self.syncSequence, className, bank) self.cv.acquire () starttime = time () while self.syncInFlight: @@ -748,6 +748,8 @@ class managementClient: sequence = self.seqMgr.reserve ((userSequence, classId, methodName)) self.setHeader (codec, ord ('M'), sequence) codec.write_uint64 (objId) # ID of object + codec.write_str8 (methodName) + bank = (objId & 0x0000FFFFFF000000) >> 24 # Encode args according to schema if classId not in self.schema: @@ -777,6 +779,5 @@ class managementClient: packageName = classId[0] className = classId[1] - msg = channel.message(codec.encoded, "agent.method." + packageName + "." + \ - className + "." + methodName) + msg = channel.message(codec.encoded, "agent." + str(bank)) channel.send ("qpid.management", msg) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 4c34b514d4..f6ebf4a381 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -167,10 +167,14 @@ class ManagementData: if self.cli != None: self.cli.setPromptMessage ("Broker Disconnected") - def schemaHandler (self, context, className, configs, insts, methods, events): + def schemaHandler (self, context, classKey, configs, insts, methods, events): """ Callback for schema updates """ - if className not in self.schema: - self.schema[className] = (configs, insts, methods, events) + if classKey not in self.schema: + schemaRev = 0 + for key in self.schema: + if classKey[0] == key[0] and classKey[1] == key[1]: + schemaRev += 1 + self.schema[classKey] = (configs, insts, methods, events, schemaRev) def setCli (self, cliobj): self.cli = cliobj @@ -248,17 +252,17 @@ class ManagementData: return str (value) return "*type-error*" - def getObjIndex (self, className, config): + def getObjIndex (self, classKey, config): """ Concatenate the values from index columns to form a unique object name """ result = "" - schemaConfig = self.schema[className][0] + schemaConfig = self.schema[classKey][0] for item in schemaConfig: if item[5] == 1 and item[0] != "id": if result != "": result = result + "." for key,val in config: if key == item[0]: - result = result + self.valueDisplay (className, key, val) + result = result + self.valueDisplay (classKey, key, val) return result def getClassKey (self, className): @@ -268,11 +272,17 @@ class ManagementData: if key[1] == className: return key else: - package = className[0:dotPos] - name = className[dotPos + 1:] + package = className[0:dotPos] + name = className[dotPos + 1:] + schemaRev = 0 + delim = name.find(".") + if delim != -1: + schemaRev = int(name[delim + 1:]) + name = name[0:delim] for key in self.schema: if key[0] == package and key[1] == name: - return key + if self.schema[key][4] == schemaRev: + return key return None def classCompletions (self, prefix): @@ -508,7 +518,11 @@ class ManagementData: sorted.sort () for classKey in sorted: tuple = self.schema[classKey] - className = classKey[0] + "." + classKey[1] + if tuple[4] == 0: + suffix = "" + else: + suffix = ".%d" % tuple[4] + className = classKey[0] + "." + classKey[1] + suffix row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) rows.append (row) self.disp.table ("Classes in Schema:", @@ -527,6 +541,7 @@ class ManagementData: raise ValueError () rows = [] + schemaRev = self.schema[classKey][4] for config in self.schema[classKey][0]: name = config[0] if name != "id": @@ -554,7 +569,7 @@ class ManagementData: rows.append ((name, typename, unit, "", "", desc)) titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") - self.disp.table ("Schema for class '%s.%s':" % (classKey[0], classKey[1]), titles, rows) + self.disp.table ("Schema for class '%s.%s.%d':" % (classKey[0], classKey[1], schemaRev), titles, rows) for mname in self.schema[classKey][2]: (mdesc, args) = self.schema[classKey][2][mname] @@ -603,13 +618,20 @@ class ManagementData: raise ValueError () schemaMethod = self.schema[classKey][2][methodName] - if len (args) != len (schemaMethod[1]): - print "Wrong number of method args: Need %d, Got %d" % (len (schemaMethod[1]), len (args)) + count = 0 + for arg in range(len(schemaMethod[1])): + if schemaMethod[1][arg][2].find("I") != -1: + count += 1 + if len (args) != count: + print "Wrong number of method args: Need %d, Got %d" % (count, len (args)) raise ValueError () namedArgs = {} - for idx in range (len (args)): - namedArgs[schemaMethod[1][idx][0]] = args[idx] + idx = 0 + for arg in range(len(schemaMethod[1])): + if schemaMethod[1][arg][2].find("I") != -1: + namedArgs[schemaMethod[1][arg][0]] = args[idx] + idx += 1 self.methodSeq = self.methodSeq + 1 self.methodsPending[self.methodSeq] = methodName @@ -623,6 +645,35 @@ class ManagementData: # except ValueError, e: # print "Error invoking method:", e + def makeIdRow (self, displayId): + if displayId in self.idMap: + rawId = self.idMap[displayId] + else: + return None + return (displayId, + rawId, + (rawId & 0x7FFF000000000000) >> 48, + (rawId & 0x0000FFFFFF000000) >> 24, + (rawId & 0x0000000000FFFFFF)) + + def listIds (self, select): + rows = [] + if select == 0: + sorted = self.idMap.keys() + sorted.sort() + for displayId in sorted: + row = self.makeIdRow (displayId) + rows.append(row) + else: + row = self.makeIdRow (select) + if row == None: + print "Display Id %d not known" % select + return + rows.append(row) + self.disp.table("Translation of Display IDs:", + ("DisplayID", "RawID", "BootSequence", "Bank", "Object"), + rows) + def do_list (self, data): tokens = data.split () if len (tokens) == 0: @@ -644,10 +695,17 @@ class ManagementData: print "Not enough arguments supplied" return - userOid = long (tokens[0]) + displayId = long (tokens[0]) methodName = tokens[1] args = tokens[2:] - self.callMethod (userOid, methodName, args) + self.callMethod (displayId, methodName, args) + + def do_id (self, data): + if data == "": + select = 0 + else: + select = int(data) + self.listIds(select) def do_exit (self): self.mclient.removeChannel (self.mch) -- cgit v1.2.1 From d74d8915ee9e9a08de2bb7d989d16f424d43a1d5 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 31 Jul 2008 21:58:39 +0000 Subject: Added signed integer datatypes for use in management schemas git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@681512 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 16 ++++++++++++++++ qpid/python/qpid/managementdata.py | 11 ++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 1059c70ada..83c29a78a5 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -397,6 +397,14 @@ class managementClient: codec.write_uuid (value) elif typecode == 15: # FTABLE codec.write_map (value) + elif typecode == 16: + codec.write_int8 (int(value)) + elif typecode == 17: + codec.write_int16 (int(value)) + elif typecode == 18: + codec.write_int32 (int(value)) + elif typecode == 19: + codec.write_int64 (int(value)) else: raise ValueError ("Invalid type code: %d" % typecode) @@ -432,6 +440,14 @@ class managementClient: data = codec.read_uuid () elif typecode == 15: # FTABLE data = codec.read_map () + elif typecode == 16: + data = codec.read_int8 () + elif typecode == 17: + data = codec.read_int16 () + elif typecode == 18: + data = codec.read_int32 () + elif typecode == 19: + data = codec.read_int64 () else: raise ValueError ("Invalid type code: %d" % typecode) return data diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index f6ebf4a381..e75cd8a99d 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -222,7 +222,8 @@ class ManagementData: if item[0] == key: typecode = item[1] unit = item[2] - if (typecode >= 1 and typecode <= 5) or typecode == 12 or typecode == 13: # numerics + if (typecode >= 1 and typecode <= 5) or typecode == 12 or typecode == 13 or \ + (typecode >= 16 and typecode <= 19): if unit == None or unit == self.lastUnit: return str (value) else: @@ -329,6 +330,14 @@ class ManagementData: return "uuid" elif typecode == 15: return "field-table" + elif typecode == 16: + return "int8" + elif typecode == 17: + return "int16" + elif typecode == 18: + return "int32" + elif typecode == 19: + return "int64" else: raise ValueError ("Invalid type code: %d" % typecode) -- cgit v1.2.1 From 311caa2ede337b3c9e526bb1d79a24c5ff873f44 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 1 Aug 2008 20:54:21 +0000 Subject: Don't pad out the last column of a table git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@681821 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/disp.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/disp.py b/qpid/python/qpid/disp.py index 5746a26e51..d697cd0136 100644 --- a/qpid/python/qpid/disp.py +++ b/qpid/python/qpid/disp.py @@ -45,8 +45,9 @@ class Display: width = cellWidth colWidth.append (width + self.tableSpacing) line = line + head - for i in range (colWidth[col] - len (head)): - line = line + " " + if col < len (heads) - 1: + for i in range (colWidth[col] - len (head)): + line = line + " " col = col + 1 print line line = self.tablePrefix @@ -60,8 +61,9 @@ class Display: col = 0 for width in colWidth: line = line + str (row[col]) - for i in range (width - len (str (row[col]))): - line = line + " " + if col < len (heads) - 1: + for i in range (width - len (str (row[col]))): + line = line + " " col = col + 1 print line -- cgit v1.2.1 From 2f821d39bc528967fa2a7f55841f898e3e115c0a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 5 Aug 2008 15:45:53 +0000 Subject: Restructured qpid-tool commands to allow active-only lists git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@682764 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-tool | 12 ++++++++---- qpid/python/qpid/managementdata.py | 31 ++++++++++++++++++------------- 2 files changed, 26 insertions(+), 17 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 7301dad6e4..60535c253b 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -55,10 +55,11 @@ class Mcli (Cmd): print "Commands:" print " list - Print summary of existing objects by class" print " list - Print list of objects of the specified class" - print " list all - Print contents of all objects of specified class" - print " list active - Print contents of all non-deleted objects of specified class" - print " list - Print contents of one or more objects (infer className)" - print " list - Print contents of one or more objects" + print " list active - Print list of non-deleted objects of the specified class" + print " show - Print contents of all objects of specified class" + print " show active - Print contents of all non-deleted objects of specified class" + print " show - Print contents of one or more objects (infer className)" + print " show - Print contents of one or more objects" print " list is space-separated, ranges may be specified (i.e. 1004-1010)" print " call [] - Invoke a method on an object" print " schema - Print summary of object classes seen on the target" @@ -115,6 +116,9 @@ class Mcli (Cmd): def do_list (self, data): self.dataObject.do_list (data) + def do_show (self, data): + self.dataObject.do_show (data) + def do_call (self, data): try: self.dataObject.do_call (data) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index e75cd8a99d..fc9eb391b7 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -367,7 +367,7 @@ class ManagementData: def listOfIds (self, classKey, tokens): """ Generate a tuple of object ids for a classname based on command tokens. """ list = [] - if tokens[0] == "all": + if len(tokens) == 0 or tokens[0] == "all": for id in self.tables[classKey]: list.append (self.displayObjId (id)) @@ -385,7 +385,7 @@ class ManagementData: if self.getClassForId (self.rawObjId (long (id))) == classKey: list.append (id) else: - list.append (token) + list.append (int(token)) list.sort () result = () @@ -421,26 +421,29 @@ class ManagementData: finally: self.lock.release () - def listObjects (self, className): + def listObjects (self, tokens): """ Generate a display of a list of objects in a class """ + if len(tokens) == 0: + print "Error - No class name provided" + return + self.lock.acquire () try: - classKey = self.getClassKey (className) + classKey = self.getClassKey (tokens[0]) if classKey == None: - print ("Object type %s not known" % className) + print ("Object type %s not known" % tokens[0]) else: rows = [] if classKey in self.tables: - sorted = self.tables[classKey].keys () - sorted.sort () - for objId in sorted: - (ts, config, inst) = self.tables[classKey][objId] + ids = self.listOfIds(classKey, tokens[1:]) + for objId in ids: + (ts, config, inst) = self.tables[classKey][self.rawObjId(objId)] createTime = self.disp.timestamp (ts[1]) destroyTime = "-" if ts[2] > 0: destroyTime = self.disp.timestamp (ts[2]) objIndex = self.getObjIndex (classKey, config) - row = (self.refName (objId), createTime, destroyTime, objIndex) + row = (objId, createTime, destroyTime, objIndex) rows.append (row) self.disp.table ("Objects of type %s.%s" % (classKey[0], classKey[1]), ("ID", "Created", "Destroyed", "Index"), @@ -687,10 +690,12 @@ class ManagementData: tokens = data.split () if len (tokens) == 0: self.listClasses () - elif len (tokens) == 1 and not self.isOid (tokens[0]): - self.listObjects (data) else: - self.showObjects (tokens) + self.listObjects (tokens) + + def do_show (self, data): + tokens = data.split () + self.showObjects (tokens) def do_schema (self, data): if data == "": -- cgit v1.2.1 From b3e71af2418565438e48b02c7bf69ecc9c85bc48 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 12 Aug 2008 15:44:15 +0000 Subject: Merged r685189 (specify literal as decimal for the sake of python 2.3) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@685198 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/spec010.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests/spec010.py b/qpid/python/tests/spec010.py index b08ad5e925..df9cb9590a 100644 --- a/qpid/python/tests/spec010.py +++ b/qpid/python/tests/spec010.py @@ -49,7 +49,7 @@ class SpecTest(TestCase): mp = self.spec["message.message_properties"] rt = self.spec["message.reply_to"] - props = Struct(mp, content_length=0xDEADBEEF, + props = Struct(mp, content_length=3735928559L, reply_to=Struct(rt, exchange="the exchange name", routing_key="the routing key")) dec = self.encdec(mp, props) -- cgit v1.2.1 From 40df26dd75d7096006bd4cf7a11ecfd8eca327be Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 18 Aug 2008 16:52:11 +0000 Subject: QPID-1250: Ensure broker receives session.detached before channel can be reused. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@686818 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegates.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 69b93e73dc..bf26553dda 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -74,8 +74,13 @@ class Delegate: notify(ch.session.condition) def session_detach(self, ch, d): - ssn = self.connection.detach(d.name, ch) + #send back the confirmation of detachment before removing the + #channel from the attached set; this avoids needing to hold the + #connection lock during the sending of this control and ensures + #that if the channel is immediately reused for a new session the + #attach request will follow the detached notification. ch.session_detached(d.name) + ssn = self.connection.detach(d.name, ch) def session_detached(self, ch, d): self.connection.detach(d.name, ch) -- cgit v1.2.1 From 513a46b070c554b3f5b3ec380dd610e22859674e Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 22 Aug 2008 09:51:55 +0000 Subject: Update & correct some of the notes included with the release. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@688045 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/RELEASE_NOTES | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/RELEASE_NOTES b/qpid/python/RELEASE_NOTES index 7005aa83cb..0e75988ae9 100644 --- a/qpid/python/RELEASE_NOTES +++ b/qpid/python/RELEASE_NOTES @@ -1,25 +1,18 @@ -Apache Incubator Qpid Python M2 Release Notes +Apache Incubator Qpid Python M3 Release Notes ------------------------------------------- -The Qpid M2 release contains support the for AMQP 0-8 specification. -You can access the 0-8 specification using the following link. -http://www.amqp.org/tikiwiki/tiki-index.php?page=Download +The Qpid M3 release of the python client contains support the for both + 0-8 and 0-10 of the AMQP specification as well as support for the +non-WIP portion of the 0-9 specification. You can access these +specficiations from: -For full details of Qpid capabilities, as they currently stand, see our -detailed project documentation at: - -http://cwiki.apache.org/confluence/pages/viewpage.action?pageId=28284 - -Please take time to go through the README file provided with the distro. +http://jira.amqp.org/confluence/display/AMQP/Download +For full details of Qpid capabilities, as they currently stand, see our +project page at: -Known Issues/Outstanding Work ------------------------------ - -There are no known issues for the Phyton client. - +http://cwiki.apache.org/confluence/display/qpid/Index -M2 Tasks Completed -------------------- +The README file provided contains some details on running the python +based test suite that is included with this distro. -Bug QPID-467 Complete Interop Testing -- cgit v1.2.1 From 720bc5b1e76bc150e30a41789ae5ca529a03725e Mon Sep 17 00:00:00 2001 From: Aidan Skinner Date: Thu, 28 Aug 2008 19:26:51 +0000 Subject: Add ASL to everywhere, to everything. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@689937 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 18 ++++++++++++++++++ qpid/python/examples/direct/direct_consumer.py | 18 ++++++++++++++++++ qpid/python/examples/direct/direct_producer.py | 18 ++++++++++++++++++ qpid/python/examples/direct/listener.py | 18 ++++++++++++++++++ qpid/python/examples/fanout/fanout_consumer.py | 18 ++++++++++++++++++ qpid/python/examples/fanout/fanout_producer.py | 18 ++++++++++++++++++ qpid/python/examples/fanout/listener.py | 18 ++++++++++++++++++ qpid/python/examples/pubsub/topic_publisher.py | 18 ++++++++++++++++++ qpid/python/examples/pubsub/topic_subscriber.py | 18 ++++++++++++++++++ qpid/python/examples/request-response/client.py | 18 ++++++++++++++++++ qpid/python/examples/request-response/server.py | 18 ++++++++++++++++++ qpid/python/examples/xml-exchange/declare_queues.py | 18 ++++++++++++++++++ qpid/python/examples/xml-exchange/listener.py | 18 ++++++++++++++++++ qpid/python/examples/xml-exchange/xml_consumer.py | 18 ++++++++++++++++++ qpid/python/examples/xml-exchange/xml_producer.py | 18 ++++++++++++++++++ qpid/python/hello-world | 18 ++++++++++++++++++ qpid/python/perftest | 18 ++++++++++++++++++ qpid/python/server | 18 ++++++++++++++++++ qpid/python/server010 | 18 ++++++++++++++++++ qpid/python/tests/spec.py | 18 ++++++++++++++++++ 20 files changed, 360 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index f0c34fa8c9..3cc4a10c0b 100755 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -1,4 +1,22 @@ #!/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. +# """ declare_queues.py diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index 23577e9f53..6e30541d6c 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -1,4 +1,22 @@ #!/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. +# """ direct_consumer.py diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index 870ce66e78..984d112243 100755 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -1,4 +1,22 @@ #!/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. +# """ direct_producer.py diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index 66927eca4b..d1c4b1d645 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -1,4 +1,22 @@ #!/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. +# """ listener.py diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index a2b1b30141..d2a6f5242f 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -1,4 +1,22 @@ #!/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. +# """ fanout_consumer.py diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 3950ca6d2e..4d24a460d1 100755 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -1,4 +1,22 @@ #!/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. +# """ fanout_producer.py diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index 74ae858127..9863357a41 100755 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -1,4 +1,22 @@ #!/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. +# """ listener.py diff --git a/qpid/python/examples/pubsub/topic_publisher.py b/qpid/python/examples/pubsub/topic_publisher.py index 8cf1b08644..b50d5fa8ca 100755 --- a/qpid/python/examples/pubsub/topic_publisher.py +++ b/qpid/python/examples/pubsub/topic_publisher.py @@ -1,4 +1,22 @@ #!/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. +# """ topic_publisher.py diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index 039cc0c55b..7a886974ee 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -1,4 +1,22 @@ #!/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. +# """ topic_subscriber.py diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index a9ecd5c78f..563e4f2992 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -1,4 +1,22 @@ #!/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. +# """ client.py diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 05ee051c57..7300ced03a 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -1,4 +1,22 @@ #!/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. +# """ server.py diff --git a/qpid/python/examples/xml-exchange/declare_queues.py b/qpid/python/examples/xml-exchange/declare_queues.py index bd17da5013..d8c2d0c34f 100755 --- a/qpid/python/examples/xml-exchange/declare_queues.py +++ b/qpid/python/examples/xml-exchange/declare_queues.py @@ -1,4 +1,22 @@ #!/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. +# """ declare_queues.py diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py index dec824dddf..c6fa4ec57d 100755 --- a/qpid/python/examples/xml-exchange/listener.py +++ b/qpid/python/examples/xml-exchange/listener.py @@ -1,4 +1,22 @@ #!/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. +# """ listener.py diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py index 0ab079e7a6..e414d98991 100755 --- a/qpid/python/examples/xml-exchange/xml_consumer.py +++ b/qpid/python/examples/xml-exchange/xml_consumer.py @@ -1,4 +1,22 @@ #!/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. +# """ direct_consumer.py diff --git a/qpid/python/examples/xml-exchange/xml_producer.py b/qpid/python/examples/xml-exchange/xml_producer.py index 72c5bdb53a..2641b185c6 100755 --- a/qpid/python/examples/xml-exchange/xml_producer.py +++ b/qpid/python/examples/xml-exchange/xml_producer.py @@ -1,4 +1,22 @@ #!/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. +# """ xml_producer.py diff --git a/qpid/python/hello-world b/qpid/python/hello-world index 5d513cc57b..5d312e7371 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -1,4 +1,22 @@ #!/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. +# from qpid.connection import Connection from qpid.util import connect from qpid.datatypes import uuid4, Message diff --git a/qpid/python/perftest b/qpid/python/perftest index 2e9148ce50..f4d3c95e96 100755 --- a/qpid/python/perftest +++ b/qpid/python/perftest @@ -1,4 +1,22 @@ #!/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. +# def publisher(n): import qpid diff --git a/qpid/python/server b/qpid/python/server index 37416314e2..56edd38490 100755 --- a/qpid/python/server +++ b/qpid/python/server @@ -1,4 +1,22 @@ #!/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 qpid from qpid.connection import Connection, listen from qpid.delegate import Delegate diff --git a/qpid/python/server010 b/qpid/python/server010 index 0a75e2534e..8dfcd7a585 100755 --- a/qpid/python/server010 +++ b/qpid/python/server010 @@ -1,4 +1,22 @@ #!/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. +# from qpid import delegates from qpid.connection010 import Connection diff --git a/qpid/python/tests/spec.py b/qpid/python/tests/spec.py index ce03640493..d5ea1d682a 100644 --- a/qpid/python/tests/spec.py +++ b/qpid/python/tests/spec.py @@ -1,3 +1,21 @@ +# +# 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. +# from unittest import TestCase from qpid.spec import load from qpid.testlib import testrunner -- cgit v1.2.1 From bf45f1241b9f801b55ede16d77c3dbbe505f0f89 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 3 Sep 2008 18:01:44 +0000 Subject: QPID-1174 Updates to the management framework git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@691700 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 181 +++++++++++++++++++++++++++++++------ qpid/python/qpid/managementdata.py | 80 +++++++++------- 2 files changed, 199 insertions(+), 62 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 83c29a78a5..99c902ab30 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -69,6 +69,53 @@ class mgmtObject (object): for cell in row: setattr (self, cell[0], cell[1]) +class objectId(object): + """ Object that represents QMF object identifiers """ + + def __init__(self, codec): + self.first = codec.read_uint64() + self.second = codec.read_uint64() + + def __cmp__(self, other): + if other == None: + return 1 + if self.first < other.first: + return -1 + if self.first > other.first: + return 1 + if self.second < other.second: + return -1 + if self.second > other.second: + return 1 + return 0 + + + def index(self): + return (self.first, self.second) + + def getFlags(self): + return (self.first & 0xF000000000000000) >> 60 + + def getSequence(self): + return (self.first & 0x0FFF000000000000) >> 48 + + def getBroker(self): + return (self.first & 0x0000FFFFF0000000) >> 28 + + def getBank(self): + return self.first & 0x000000000FFFFFFF + + def getObject(self): + return self.second + + def isDurable(self): + return self.getSequence() == 0 + + def encode(self, codec): + codec.write_uint64(self.first) + codec.write_uint64(self.second) + + class methodResult: """ Object that contains the result of a method call """ @@ -308,6 +355,8 @@ class managementClient: self.handleClassInd (ch, codec) elif hdr[0] == 'h': self.handleHeartbeat (ch, codec) + elif hdr[0] == 'e': + self.handleEvent (ch, codec) else: self.parse (ch, codec, hdr[0], hdr[1]) ch.accept(msg) @@ -386,7 +435,7 @@ class managementClient: elif typecode == 9: # DELTATIME codec.write_uint64 (long (value)) elif typecode == 10: # REF - codec.write_uint64 (long (value)) + value.encode(codec) elif typecode == 11: # BOOL codec.write_uint8 (int (value)) elif typecode == 12: # FLOAT @@ -429,7 +478,7 @@ class managementClient: elif typecode == 9: # DELTATIME data = codec.read_uint64 () elif typecode == 10: # REF - data = codec.read_uint64 () + data = objectId(codec) elif typecode == 11: # BOOL data = codec.read_uint8 () elif typecode == 12: # FLOAT @@ -551,9 +600,9 @@ class managementClient: ch.send ("qpid.management", smsg) def handleClassInd (self, ch, codec): - pname = str (codec.read_str8 ()) - cname = str (codec.read_str8 ()) - hash = codec.read_bin128 () + pname = str (codec.read_str8()) + cname = str (codec.read_str8()) + hash = codec.read_bin128() if pname not in self.packages: return @@ -574,6 +623,32 @@ class managementClient: if self.ctrlCb != None: self.ctrlCb (ch.context, self.CTRL_HEARTBEAT, timestamp) + def handleEvent (self, ch, codec): + if self.eventCb == None: + return + timestamp = codec.read_uint64() + objId = objectId(codec) + packageName = str(codec.read_str8()) + className = str(codec.read_str8()) + hash = codec.read_bin128() + name = str(codec.read_str8()) + classKey = (packageName, className, hash) + if classKey not in self.schema: + return; + schemaClass = self.schema[classKey] + row = [] + es = schemaClass['E'] + arglist = None + for ename in es: + (edesc, eargs) = es[ename] + if ename == name: + arglist = eargs + if arglist == None: + return + for arg in arglist: + row.append((arg[0], self.decodeValue(codec, arg[1]))) + self.eventCb(ch.context, classKey, objId, name, row) + def parseSchema (self, ch, codec): """ Parse a received schema-description message. """ self.decOutstanding (ch) @@ -597,22 +672,23 @@ class managementClient: configs = [] insts = [] methods = {} - events = [] + events = {} configs.append (("id", 4, "", "", 1, 1, None, None, None, None, None)) insts.append (("id", 4, None, None)) for idx in range (configCount): ft = codec.read_map () - name = str (ft["name"]) - type = ft["type"] - access = ft["access"] - index = ft["index"] - unit = None - min = None - max = None - maxlen = None - desc = None + name = str (ft["name"]) + type = ft["type"] + access = ft["access"] + index = ft["index"] + optional = ft["optional"] + unit = None + min = None + max = None + maxlen = None + desc = None for key, value in ft.items (): if key == "unit": @@ -626,7 +702,7 @@ class managementClient: elif key == "desc": desc = str (value) - config = (name, type, unit, desc, access, index, min, max, maxlen) + config = (name, type, unit, desc, access, index, min, max, maxlen, optional) configs.append (config) for idx in range (instCount): @@ -685,6 +761,33 @@ class managementClient: args.append (arg) methods[mname] = (mdesc, args) + for idx in range (eventCount): + ft = codec.read_map () + ename = str (ft["name"]) + argCount = ft["argCount"] + if "desc" in ft: + edesc = str (ft["desc"]) + else: + edesc = None + + args = [] + for aidx in range (argCount): + ft = codec.read_map () + name = str (ft["name"]) + type = ft["type"] + unit = None + desc = None + + for key, value in ft.items (): + if key == "unit": + unit = str (value) + elif key == "desc": + desc = str (value) + + arg = (name, type, unit, desc) + args.append (arg) + events[ename] = (edesc, args) + schemaClass = {} schemaClass['C'] = configs schemaClass['I'] = insts @@ -695,6 +798,22 @@ class managementClient: if self.schemaCb != None: self.schemaCb (ch.context, classKey, configs, insts, methods, events) + def parsePresenceMasks(self, codec, schemaClass): + """ Generate a list of not-present properties """ + excludeList = [] + bit = 0 + for element in schemaClass['C'][1:]: + if element[9] == 1: + if bit == 0: + mask = codec.read_uint8() + bit = 1 + if (mask & bit) == 0: + excludeList.append(element[0]) + bit = bit * 2 + if bit == 256: + bit = 0 + return excludeList + def parseContent (self, ch, cls, codec, seq=0): """ Parse a received content message. """ if (cls == 'C' or (cls == 'B' and seq == 0)) and self.configCb == None: @@ -716,21 +835,26 @@ class managementClient: timestamps.append (codec.read_uint64 ()) # Current Time timestamps.append (codec.read_uint64 ()) # Create Time timestamps.append (codec.read_uint64 ()) # Delete Time - + objId = objectId(codec) schemaClass = self.schema[classKey] if cls == 'C' or cls == 'B': - for element in schemaClass['C'][:]: + notPresent = self.parsePresenceMasks(codec, schemaClass) + + if cls == 'C' or cls == 'B': + row.append(("id", objId)) + for element in schemaClass['C'][1:]: tc = element[1] name = element[0] - data = self.decodeValue (codec, tc) - row.append ((name, data)) + if name in notPresent: + row.append((name, None)) + else: + data = self.decodeValue(codec, tc) + row.append((name, data)) if cls == 'I' or cls == 'B': - if cls == 'B': - start = 1 - else: - start = 0 - for element in schemaClass['I'][start:]: + if cls == 'I': + row.append(("id", objId)) + for element in schemaClass['I'][1:]: tc = element[1] name = element[0] data = self.decodeValue (codec, tc) @@ -763,9 +887,12 @@ class managementClient: codec = Codec (self.spec) sequence = self.seqMgr.reserve ((userSequence, classId, methodName)) self.setHeader (codec, ord ('M'), sequence) - codec.write_uint64 (objId) # ID of object + objId.encode(codec) + codec.write_str8 (classId[0]) + codec.write_str8 (classId[1]) + codec.write_bin128 (classId[2]) codec.write_str8 (methodName) - bank = (objId & 0x0000FFFFFF000000) >> 24 + bank = objId.getBank() # Encode args according to schema if classId not in self.schema: diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index fc9eb391b7..5a8b9cdf9d 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -71,14 +71,14 @@ class ManagementData: # def registerObjId (self, objId): - if not objId in self.idBackMap: - self.idBackMap[objId] = self.nextId + if not objId.index() in self.idBackMap: + self.idBackMap[objId.index()] = self.nextId self.idMap[self.nextId] = objId self.nextId += 1 - def displayObjId (self, objId): - if objId in self.idBackMap: - return self.idBackMap[objId] + def displayObjId (self, objIdIndex): + if objIdIndex in self.idBackMap: + return self.idBackMap[objIdIndex] else: return 0 @@ -86,7 +86,7 @@ class ManagementData: if displayId in self.idMap: return self.idMap[displayId] else: - return 0 + return None def displayClassName (self, cls): (packageName, className, hash) = cls @@ -102,19 +102,20 @@ class ManagementData: self.tables[className] = {} # Register the ID so a more friendly presentation can be displayed - id = long (list[0][1]) - self.registerObjId (id) + objId = list[0][1] + oidx = objId.index() + self.registerObjId (objId) # If this object hasn't been seen before, create a new object record with # the timestamps and empty lists for configuration and instrumentation data. - if id not in self.tables[className]: - self.tables[className][id] = (timestamps, [], []) + if oidx not in self.tables[className]: + self.tables[className][oidx] = (timestamps, [], []) - (unused, oldConf, oldInst) = self.tables[className][id] + (unused, oldConf, oldInst) = self.tables[className][oidx] # For config updates, simply replace old config list with the new one. if context == 0: #config - self.tables[className][id] = (timestamps, list, oldInst) + self.tables[className][oidx] = (timestamps, list, oldInst) # For instrumentation updates, carry the minimum and maximum values for # "hi-lo" stats forward. @@ -132,7 +133,7 @@ class ManagementData: if oldInst[idx][1] < value: value = oldInst[idx][1] newInst.append ((key, value)) - self.tables[className][id] = (timestamps, oldConf, newInst) + self.tables[className][oidx] = (timestamps, oldConf, newInst) finally: self.lock.release () @@ -211,11 +212,13 @@ class ManagementData: pass def refName (self, oid): - if oid == 0: + if oid == None: return "NULL" - return str (self.displayObjId (oid)) + return str (self.displayObjId (oid.index())) def valueDisplay (self, classKey, key, value): + if value == None: + return "" for kind in range (2): schema = self.schema[classKey][kind] for item in schema: @@ -437,7 +440,7 @@ class ManagementData: if classKey in self.tables: ids = self.listOfIds(classKey, tokens[1:]) for objId in ids: - (ts, config, inst) = self.tables[classKey][self.rawObjId(objId)] + (ts, config, inst) = self.tables[classKey][self.rawObjId(objId).index()] createTime = self.disp.timestamp (ts[1]) destroyTime = "-" if ts[2] > 0: @@ -486,32 +489,32 @@ class ManagementData: rows = [] timestamp = None - config = self.tables[classKey][ids[0]][1] + config = self.tables[classKey][ids[0].index()][1] for eIdx in range (len (config)): key = config[eIdx][0] if key != "id": row = ("property", key) for id in ids: if timestamp == None or \ - timestamp < self.tables[classKey][id][0][0]: - timestamp = self.tables[classKey][id][0][0] - (key, value) = self.tables[classKey][id][1][eIdx] + timestamp < self.tables[classKey][id.index()][0][0]: + timestamp = self.tables[classKey][id.index()][0][0] + (key, value) = self.tables[classKey][id.index()][1][eIdx] row = row + (self.valueDisplay (classKey, key, value),) rows.append (row) - inst = self.tables[classKey][ids[0]][2] + inst = self.tables[classKey][ids[0].index()][2] for eIdx in range (len (inst)): key = inst[eIdx][0] if key != "id": row = ("statistic", key) for id in ids: - (key, value) = self.tables[classKey][id][2][eIdx] + (key, value) = self.tables[classKey][id.index()][2][eIdx] row = row + (self.valueDisplay (classKey, key, value),) rows.append (row) titleRow = ("Type", "Element") for id in ids: - titleRow = titleRow + (self.refName (id),) + titleRow = titleRow + (self.refName(id),) caption = "Object of type %s.%s:" % (classKey[0], classKey[1]) if timestamp != None: caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" @@ -563,13 +566,15 @@ class ManagementData: access = self.accessName (config[4]) extra = "" if config[5] == 1: - extra = extra + "index " + extra += "index " if config[6] != None: - extra = extra + "Min: " + str (config[6]) + extra += "Min: " + str(config[6]) + " " if config[7] != None: - extra = extra + "Max: " + str (config[7]) + extra += "Max: " + str(config[7]) + " " if config[8] != None: - extra = extra + "MaxLen: " + str (config[8]) + extra += "MaxLen: " + str(config[8]) + " " + if config[9] == 1: + extra += "optional " rows.append ((name, typename, unit, access, extra, desc)) for config in self.schema[classKey][1]: @@ -613,7 +618,7 @@ class ManagementData: def getClassForId (self, objId): """ Given an object ID, return the class key for the referenced object """ for classKey in self.tables: - if objId in self.tables[classKey]: + if objId.index() in self.tables[classKey]: return classKey return None @@ -659,14 +664,19 @@ class ManagementData: def makeIdRow (self, displayId): if displayId in self.idMap: - rawId = self.idMap[displayId] + objId = self.idMap[displayId] else: return None - return (displayId, - rawId, - (rawId & 0x7FFF000000000000) >> 48, - (rawId & 0x0000FFFFFF000000) >> 24, - (rawId & 0x0000000000FFFFFF)) + if objId.getFlags() == 0: + flags = "" + else: + flags = str(objId.getFlags()) + seq = objId.getSequence() + if seq == 0: + seqText = "" + else: + seqText = str(seq) + return (displayId, flags, seqText, objId.getBroker(), objId.getBank(), hex(objId.getObject())) def listIds (self, select): rows = [] @@ -683,7 +693,7 @@ class ManagementData: return rows.append(row) self.disp.table("Translation of Display IDs:", - ("DisplayID", "RawID", "BootSequence", "Bank", "Object"), + ("DisplayID", "Flags", "BootSequence", "Broker", "Bank", "Object"), rows) def do_list (self, data): -- cgit v1.2.1 From ade2bbc93507522d4a7d200d809febc654595dc4 Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Thu, 4 Sep 2008 14:16:31 +0000 Subject: Added missing DISCLAIMER files to cpp python and ruby git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@692031 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/DISCLAIMER | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 qpid/python/DISCLAIMER (limited to 'qpid/python') diff --git a/qpid/python/DISCLAIMER b/qpid/python/DISCLAIMER new file mode 100644 index 0000000000..c321113c9e --- /dev/null +++ b/qpid/python/DISCLAIMER @@ -0,0 +1,5 @@ +Apache Qpid is an effort undergoing incubation at the Apache Software Foundation (ASF), sponsored by the Apache Incubator PMC. + +Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. + +While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF. -- cgit v1.2.1 From 72961d9b9a4b70b85affbba1d14ec5b913ec5815 Mon Sep 17 00:00:00 2001 From: Nuno Santos Date: Fri, 5 Sep 2008 20:14:29 +0000 Subject: allow creation of objectId from first/second id components git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@692525 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 99c902ab30..8d8339b2c6 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -72,9 +72,13 @@ class mgmtObject (object): class objectId(object): """ Object that represents QMF object identifiers """ - def __init__(self, codec): - self.first = codec.read_uint64() - self.second = codec.read_uint64() + def __init__(self, codec, first=0, second=0): + if codec: + self.first = codec.read_uint64() + self.second = codec.read_uint64() + else: + self.first = first + self.second = second def __cmp__(self, other): if other == None: -- cgit v1.2.1 From 6b6b5d003c1eb75595ca479509c9089bc36ca7a2 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 18 Sep 2008 22:15:46 +0000 Subject: QPID-1288 - New (and greatly improved) API for management console access git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@696832 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 953 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 953 insertions(+) create mode 100644 qpid/python/qpid/qmfconsole.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py new file mode 100644 index 0000000000..96633bca0d --- /dev/null +++ b/qpid/python/qpid/qmfconsole.py @@ -0,0 +1,953 @@ +# +# 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. +# + +""" Console API for Qpid Management Framework """ + +import os +import qpid +import struct +import socket +from qpid.peer import Closed +from qpid.connection import Connection, ConnectionFailed +from qpid.datatypes import uuid4 +from qpid.util import connect +from datatypes import Message, RangedSet +from threading import Lock, Condition +from codec010 import StringCodec as Codec +from time import time +from cStringIO import StringIO + +class Console: + """ To access the asynchronous operations, a class must be derived from + Console with overrides of any combination of the available methods. """ + + def newPackage(self, name): + """ Invoked when a QMF package is discovered. """ + pass + + def newClass(self, classKey): + """ Invoked when a new class is discovered. Session.getSchema can be + used to obtain details about the class.""" + pass + + def newAgent(self, broker, agent): + """ Invoked when a QMF agent is discovered. """ + pass + + def objectProps(self, broker, id, record): + """ Invoked when an object is updated. """ + global B + if record.getClassKey()[1] == "broker": + B = record + + def objectStats(self, broker, id, record): + """ Invoked when an object is updated. """ + pass + + def event(self, broker, event): + """ Invoked when an event is raised. """ + pass + + def heartbeat(self, agent, timestamp): + """ """ + pass + + def brokerInfo(self, broker): + """ """ + pass + +class Session: + """ + An instance of the Session class represents a console session running + against one or more QMF brokers. A single instance of Session is needed + to interact with the management framework as a console. + """ + _CONTEXT_SYNC = 1 + _CONTEXT_STARTUP = 2 + _CONTEXT_MULTIGET = 3 + + GET_WAIT_TIME = 10 + + def __init__(self, console=None): + """ + Initialize a session. If the console argument is provided, the + more advanced asynchronous features are available. If console is + defaulted, the session will operate in a simpler, synchronous manner. + """ + self.console = console + self.brokers = [] + self.packages = {} + self.seqMgr = SequenceManager() + self.cv = Condition() + self.syncSequenceList = [] + self.getResult = [] + + def addBroker(self, host="localhost", port=5672, + authMech="PLAIN", authName="guest", authPass="guest"): + """ Connect to a Qpid broker. Returns an object of type Broker. """ + broker = Broker(self, host, port, authMech, authName, authPass) + self.brokers.append(broker) + return broker + + def delBroker(self, broker): + """ Disconnect from a broker. The 'broker' argument is the object + returned from the addBroker call """ + broker._shutdown() + self.brokers.remove(broker) + del broker + + def getPackages(self): + """ Get the list of known QMF packages """ + for broker in self.brokers: + broker._waitForStable() + list = [] + for package in self.packages: + list.append(package) + return list + + def getClasses(self, packageName): + """ Get the list of known classes within a QMF package """ + for broker in self.brokers: + broker._waitForStable() + list = [] + if packageName in self.packages: + for cname, hash in self.packages[packageName]: + list.append((packageName, cname, hash)) + return list + + def getSchema(self, classKey): + """ Get the schema for a QMF class """ + for broker in self.brokers: + broker._waitForStable() + pname, cname, hash = classKey + if pname in self.packages: + if (cname, hash) in self.packages[pname]: + return self.packages[pname][(cname, hash)] + + def getAgents(self): + """ Get a list of currently known agents """ + for broker in self.brokers: + broker._waitForStable() + pass + + def getObjects(self, **kwargs): + """ Get a list of objects from QMF agents. + All arguments are passed by name(keyword). + + The class for queried objects may be specified in one of the following ways: + + schema = - supply a schema object returned from getSchema + key = - supply a classKey from the list returned by getClasses + name = - supply a class name as a string + + If objects should be obtained from only one agent, use the following argument. + Otherwise, the query will go to all agents. + + agent = - supply an agent from the list returned by getAgents + + If the get query is to be restricted to one broker (as opposed to all connected brokers), + add the following argument: + + broker = - supply a broker as returned by addBroker + """ + if "broker" in kwargs: + brokerList = [].append(kwargs["broker"]) + else: + brokerList = self.brokers + for broker in brokerList: + broker._waitForStable() + + agentList = [] + if "agent" in kwargs: + agent = kwargs["agent"] + if agent.broker not in brokerList: + raise Exception("Supplied agent is not accessible through the supplied broker") + agentList = append(agent) + else: + for broker in brokerList: + for agent in broker.getAgents(): + agentList.append(agent) + + cname = None + if "schema" in kwargs: pname, cname, hash = kwargs["schema"].getKey() + elif "key" in kwargs: pname, cname, hash = kwargs["key"] + elif "name" in kwargs: pname, cname, hash = None, kwargs["name"], None + if cname == None: + raise Exception("No class supplied, use 'schema', 'key', or 'name' argument") + map = {} + map["_class"] = cname + if pname != None: map["_package"] = pname + if hash != None: map["_hash"] = hash + + self.getResult = [] + for agent in agentList: + broker = agent.broker + sendCodec = Codec(broker.conn.spec) + self.cv.acquire() + seq = self.seqMgr._reserve(self._CONTEXT_MULTIGET) + self.syncSequenceList.append(seq) + self.cv.release() + broker._setHeader(sendCodec, 'G', seq) + sendCodec.write_map(map) + smsg = broker._message(sendCodec.encoded, "agent.%d" % agent.bank) + broker._send(smsg) + + starttime = time() + timeout = False + self.cv.acquire() + while len(self.syncSequenceList) > 0: + self.cv.wait(self.GET_WAIT_TIME) + if time() - starttime > self.GET_WAIT_TIME: + for pendingSeq in self.syncSequenceList: + self.seqMgr._release(pendingSeq) + self.syncSequenceList = [] + timeout = True + self.cv.release() + + if len(self.getResult) == 0 and timeout: + raise RuntimeError("No agent responded within timeout period") + return self.getResult + + def setEventFilter(self, **kwargs): + """ """ + pass + + def _handleBrokerResp(self, broker, codec, seq): + broker.brokerId = codec.read_uuid() + if self.console != None: + self.console.brokerInfo(broker) + + # Send a package request + # (effectively inc and dec outstanding by not doing anything) + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) + broker._setHeader(sendCodec, 'P', seq) + smsg = broker._message(sendCodec.encoded) + broker._send(smsg) + + def _handlePackageInd(self, broker, codec, seq): + pname = str(codec.read_str8()) + self.cv.acquire() + if pname not in self.packages: + self.packages[pname] = {} + self.cv.release() + if self.console != None: + self.console.newPackage(pname) + else: + self.cv.release() + + # Send a class request + broker._incOutstanding() + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) + broker._setHeader(sendCodec, 'Q', seq) + sendCodec.write_str8(pname) + smsg = broker._message(sendCodec.encoded) + broker._send(smsg) + + def _handleCommandComplete(self, broker, codec, seq): + code = codec.read_uint32() + text = str(codec.read_str8()) + context = self.seqMgr._release(seq) + if context == self._CONTEXT_STARTUP: + broker._decOutstanding() + elif context == self._CONTEXT_SYNC and seq == broker.syncSequence: + broker.cv.acquire() + broker.syncInFlight = False + broker.cv.notify() + broker.cv.release() + elif context == self._CONTEXT_MULTIGET and seq in self.syncSequenceList: + self.cv.acquire() + self.syncSequenceList.remove(seq) + if len(self.syncSequenceList) == 0: + self.cv.notify() + self.cv.release() + + def _handleClassInd(self, broker, codec, seq): + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + + self.cv.acquire() + if pname not in self.packages: + self.cv.release() + return + if (cname, hash) not in self.packages[pname]: + # Send a schema request for the unknown class + self.cv.release() + broker._incOutstanding() + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) + broker._setHeader(sendCodec, 'S', seq) + sendCodec.write_str8(pname) + sendCodec.write_str8(cname) + sendCodec.write_bin128(hash) + smsg = broker._message(sendCodec.encoded) + broker._send(smsg) + else: + self.cv.release() + + def _handleMethodResp(self, broker, codec, seq): + code = codec.read_uint32() + text = str(codec.read_str8()) + outArgs = {} + obj, method = self.seqMgr._release(seq) + if code == 0: + for arg in method.arguments: + if arg.dir.find("O") != -1: + outArgs[arg.name] = obj._decodeValue(codec, arg.type) + broker.cv.acquire() + broker.syncResult = MethodResult(code, text, outArgs) + broker.syncInFlight = False + broker.cv.notify() + broker.cv.release() + + def _handleHeartbeatInd(self, broker, codec, seq): + timestamp = codec.read_uint64() + pass + + def _handleEventInd(self, broker, codec, seq): + pass + + def _handleSchemaResp(self, broker, codec, seq): + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + classKey = (pname, cname, hash) + _class = SchemaClass(classKey, codec) + self.cv.acquire() + self.packages[pname][(cname, hash)] = _class + self.cv.release() + broker._decOutstanding() + if self.console != None: + self.console.newClass(classKey) + + def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + classKey = (pname, cname, hash) + self.cv.acquire() + if pname not in self.packages: + self.cv.release() + return + if (cname, hash) not in self.packages[pname]: + self.cv.release() + return + self.cv.release() + schema = self.packages[pname][(cname, hash)] + object = Object(self, broker, schema, codec, prop, stat) + + self.cv.acquire() + if seq in self.syncSequenceList: + self.getResult.append(object) + self.cv.release() + return + self.cv.release() + + if self.console != None: + if prop: + self.console.objectProps(broker, object.getObjectId(), object) + if stat: + self.console.objectStats(broker, object.getObjectId(), object) + +class Package: + """ """ + def __init__(self, name): + self.name = name + +class ClassKey: + """ """ + def __init__(self, package, className, hash): + self.package = package + self.className = className + self.hash = hash + +class SchemaClass: + """ """ + def __init__(self, key, codec): + self.classKey = key + self.properties = [] + self.statistics = [] + self.methods = [] + self.events = [] + + propCount = codec.read_uint16() + statCount = codec.read_uint16() + methodCount = codec.read_uint16() + eventCount = codec.read_uint16() + + for idx in range(propCount): + self.properties.append(SchemaProperty(codec)) + for idx in range(statCount): + self.statistics.append(SchemaStatistic(codec)) + for idx in range(methodCount): + self.methods.append(SchemaMethod(codec)) + for idx in range(eventCount): + self.events.append(SchemaEvent(codec)) + + def __repr__(self): + pname, cname, hash = self.classKey + result = "Class: %s:%s " % (pname, cname) + result += "(%08x-%04x-%04x-%04x-%04x%08x)" % struct.unpack ("!LHHHHL", hash) + return result + + def getKey(self): + """ """ + return self.classKey + + def getProperties(self): + """ """ + return self.properties + + def getStatistics(self): + """ """ + return self.statistics + + def getMethods(self): + """ """ + return self.methods + + def getEvents(self): + """ """ + return self.events + +class SchemaProperty: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + self.type = map["type"] + self.access = map["access"] + self.index = map["index"] != 0 + self.optional = map["optional"] != 0 + self.unit = None + self.min = None + self.max = None + self.maxlan = None + self.desc = None + + for key, value in map.items(): + if key == "unit" : self.unit = str(value) + elif key == "min" : self.min = value + elif key == "max" : self.max = value + elif key == "maxlen" : self.maxlen = value + elif key == "desc" : self.desc = str(value) + +class SchemaStatistic: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + self.type = map["type"] + self.unit = None + self.desc = None + + for key, value in map.items(): + if key == "unit" : self.unit = str(value) + elif key == "desc" : self.desc = str(value) + +class SchemaMethod: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + argCount = map["argCount"] + if "desc" in map: + self.desc = str(map["desc"]) + else: + self.desc = None + self.arguments = [] + + for idx in range(argCount): + self.arguments.append(SchemaArgument(codec, methodArg=True)) + +class SchemaEvent: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + argCount = map["argCount"] + if "desc" in map: + self.desc = str(map["desc"]) + else: + self.desc = None + self.arguments = [] + + for idx in range(argCount): + self.arguments.append(SchemaArgument(codec, methodArg=False)) + +class SchemaArgument: + """ """ + def __init__(self, codec, methodArg): + map = codec.read_map() + self.name = str(map["name"]) + self.type = map["type"] + if methodArg: + self.dir = str(map["dir"].upper()) + self.unit = None + self.min = None + self.max = None + self.maxlen = None + self.desc = None + self.default = None + + for key, value in map.items(): + if key == "unit" : self.unit = str(value) + elif key == "min" : self.min = value + elif key == "max" : self.max = value + elif key == "maxlen" : self.maxlen = value + elif key == "desc" : self.desc = str(value) + elif key == "default" : self.default = str(value) + +class ObjectId(object): + """ Object that represents QMF object identifiers """ + def __init__(self, codec, first=0, second=0): + if codec: + self.first = codec.read_uint64() + self.second = codec.read_uint64() + else: + self.first = first + self.second = second + + def __cmp__(self, other): + if other == None: + return 1 + if self.first < other.first: + return -1 + if self.first > other.first: + return 1 + if self.second < other.second: + return -1 + if self.second > other.second: + return 1 + return 0 + + def __repr__(self): + return "%08x-%04x-%04x-%04x-%04x%08x" % ((self.first & 0xFFFFFFFF00000000) >> 32, + (self.first & 0x00000000FFFF0000) >> 16, + (self.first & 0x000000000000FFFF), + (self.second & 0xFFFF000000000000) >> 48, + (self.second & 0x0000FFFF00000000) >> 32, + (self.second & 0x00000000FFFFFFFF)) + + def index(self): + return (self.first, self.second) + + def getFlags(self): + return (self.first & 0xF000000000000000) >> 60 + + def getSequence(self): + return (self.first & 0x0FFF000000000000) >> 48 + + def getBroker(self): + return (self.first & 0x0000FFFFF0000000) >> 28 + + def getBank(self): + return self.first & 0x000000000FFFFFFF + + def getObject(self): + return self.second + + def isDurable(self): + return self.getSequence() == 0 + + def encode(self, codec): + codec.write_uint64(self.first) + codec.write_uint64(self.second) + + +class Object(object): + """ """ + def __init__(self, session, broker, schema, codec, prop, stat): + """ """ + self.session = session + self.broker = broker + self.schema = schema + self.currentTime = codec.read_uint64() + self.createTime = codec.read_uint64() + self.deleteTime = codec.read_uint64() + self.objectId = ObjectId(codec) + self.properties = [] + self.statistics = [] + if prop: + notPresent = self._parsePresenceMasks(codec, schema) + for property in schema.getProperties(): + if property.name in notPresent: + self.properties.append((property, None)) + else: + self.properties.append((property, self._decodeValue(codec, property.type))) + if stat: + for statistic in schema.getStatistics(): + self.statistics.append((statistic, self._decodeValue(codec, statistic.type))) + + def getObjectId(self): + """ """ + return self.objectId + + def getClassKey(self): + """ """ + return self.schema.getKey() + + def getSchema(self): + """ """ + return self.schema + + def getTimestamps(self): + """ """ + return self.currentTime, self.createTime, self.deleteTime + + def getIndex(self): + """ """ + result = "" + for property, value in self.properties: + if property.index: + if result != "": + result += ":" + result += str(value) + return result + + def __repr__(self): + return self.getIndex() + + def __getattr__(self, name): + for method in self.schema.getMethods(): + if name == method.name: + return lambda *args, **kwargs : self._invoke(name, args, kwargs) + for property, value in self.properties: + if name == property.name: + return value + for statistic, value in self.statistics: + if name == statistic.name: + return value + + def _invoke(self, name, args, kwargs): + for method in self.schema.getMethods(): + if name == method.name: + aIdx = 0 + sendCodec = Codec(self.broker.conn.spec) + seq = self.session.seqMgr._reserve((self, method)) + self.broker._setHeader(sendCodec, 'M', seq) + self.objectId.encode(sendCodec) + pname, cname, hash = self.schema.getKey() + sendCodec.write_str8(pname) + sendCodec.write_str8(cname) + sendCodec.write_bin128(hash) + sendCodec.write_str8(name) + for arg in method.arguments: + if arg.dir.find("I") != -1: + self._encodeValue(sendCodec, args[aIdx], arg.type) + aIdx += 1 + smsg = self.broker._message(sendCodec.encoded, "agent." + str(self.objectId.getBank())) + self.broker._send(smsg) + self.broker.cv.acquire() + self.broker.syncInFlight = True + starttime = time() + while self.broker.syncInFlight: + self.broker.cv.wait(self.broker.SYNC_TIME) + if time() - starttime > self.broker.SYNC_TIME: + self.broker.cv.release() + self.session.seqMgr._release(seq) + raise RuntimeError("Timed out waiting for method to respond") + self.broker.cv.release() + return self.broker.syncResult + else: + raise Exception("Invalid Method (software defect)") + + def _parsePresenceMasks(self, codec, schema): + excludeList = [] + bit = 0 + for property in schema.getProperties(): + if property.optional: + if bit == 0: + mask = codec.read_uint8() + bit = 1 + if (mask & bit) == 0: + excludeList.append(property.name) + bit *= 2 + if bit == 256: + bit = 0 + return excludeList + + def _decodeValue(self, codec, typecode): + """ Decode, from the codec, a value based on its typecode. """ + if typecode == 1: data = codec.read_uint8() # U8 + elif typecode == 2: data = codec.read_uint16() # U16 + elif typecode == 3: data = codec.read_uint32() # U32 + elif typecode == 4: data = codec.read_uint64() # U64 + elif typecode == 6: data = str(codec.read_str8()) # SSTR + elif typecode == 7: data = codec.read_vbin32() # LSTR + elif typecode == 8: data = codec.read_int64() # ABSTIME + elif typecode == 9: data = codec.read_uint64() # DELTATIME + elif typecode == 10: data = ObjectId(codec) # REF + elif typecode == 11: data = codec.read_uint8() # BOOL + elif typecode == 12: data = codec.read_float() # FLOAT + elif typecode == 13: data = codec.read_double() # DOUBLE + elif typecode == 14: data = codec.read_uuid() # UUID + elif typecode == 15: data = codec.read_map() # FTABLE + elif typecode == 16: data = codec.read_int8() # S8 + elif typecode == 17: data = codec.read_int16() # S16 + elif typecode == 18: data = codec.read_int32() # S32 + elif typecode == 19: data = codec.read_int64() # S63 + else: + raise ValueError("Invalid type code: %d" % typecode) + return data + + def _encodeValue(self, codec, value, typecode): + """ Encode, into the codec, a value based on its typecode. """ + if typecode == 1: codec.write_uint8 (int(value)) # U8 + elif typecode == 2: codec.write_uint16 (int(value)) # U16 + elif typecode == 3: codec.write_uint32 (long(value)) # U32 + elif typecode == 4: codec.write_uint64 (long(value)) # U64 + elif typecode == 6: codec.write_str8 (value) # SSTR + elif typecode == 7: codec.write_vbin32 (value) # LSTR + elif typecode == 8: codec.write_int64 (long(value)) # ABSTIME + elif typecode == 9: codec.write_uint64 (long(value)) # DELTATIME + elif typecode == 10: value.encode (codec) # REF + elif typecode == 11: codec.write_uint8 (int(value)) # BOOL + elif typecode == 12: codec.write_float (float(value)) # FLOAT + elif typecode == 13: codec.write_double (double(value)) # DOUBLE + elif typecode == 14: codec.write_uuid (value) # UUID + elif typecode == 15: codec.write_map (value) # FTABLE + elif typecode == 16: codec.write_int8 (int(value)) # S8 + elif typecode == 17: codec.write_int16 (int(value)) # S16 + elif typecode == 18: codec.write_int32 (int(value)) # S32 + elif typecode == 19: codec.write_int64 (int(value)) # S64 + else: + raise ValueError ("Invalid type code: %d" % typecode) + +class MethodResult(object): + """ """ + def __init__(self, status, text, outArgs): + """ """ + self.status = status + self.text = text + self.outArgs = outArgs + + def __getattr__(self, name): + if name in self.outArgs: + return self.outArgs[name] + + def __repr__(self): + return "%s (%d) - %s" % (self.text, self.status, self.outArgs) + +class Broker: + """ """ + SYNC_TIME = 10 + + def __init__(self, session, host, port, authMech, authUser, authPass): + self.session = session + self.agents = [] + self.agents.append(Agent(self, 0)) + self.topicBound = False + self.cv = Condition() + self.syncInFlight = False + self.syncRequest = 0 + self.syncResult = None + self.reqsOutstanding = 1 + self.brokerId = None + err = None + try: + self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) + self.conn = Connection(connect(host, port), username=authUser, password=authPass) + self.conn.start() + self.replyName = "reply-%s" % self.amqpSessionId + self.amqpSession = self.conn.session(self.amqpSessionId) + self.amqpSession.auto_sync = False + self.amqpSession.queue_declare(queue=self.replyName, exclusive=True, auto_delete=True) + self.amqpSession.exchange_bind(exchange="amq.direct", + queue=self.replyName, binding_key=self.replyName) + self.amqpSession.message_subscribe(queue=self.replyName, destination="rdest") + self.amqpSession.incoming("rdest").listen(self._replyCb, self._exceptionCb) + self.amqpSession.message_set_flow_mode(destination="rdest", flow_mode=1) + self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="rdest", unit=1, value=0xFFFFFFFF) + + if self.session.console != None: + self.topicName = "topic-%s" % self.amqpSessionId + self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) + self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest") + self.amqpSession.incoming("tdest").listen(self._replyCb) + self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) + self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) + + codec = Codec(self.conn.spec) + self._setHeader(codec, 'B') + msg = self._message(codec.encoded) + self._send(msg) + + except socket.error, e: + err = "Socket Error %s - %s" % (e[0], e[1]) + except Closed, e: + err = "Connect Failed %d - %s" % (e[0], e[1]) + except ConnectionFailed, e: + err = "Connect Failed %d - %s" % (e[0], e[1]) + + if err != None: + raise Exception(err) + + def getBrokerId(self): + """ Get broker's unique identifier (UUID) """ + return self.brokerId + + def getSessionId(self): + """ Get the identifier of the AMQP session to the broker """ + return self.amqpSessionId + + def getAgents(self): + """ Get the list of agents reachable via this broker """ + return self.agents + + def _setHeader(self, codec, opcode, seq=0): + """ Compose the header of a management message. """ + codec.write_uint8(ord('A')) + codec.write_uint8(ord('M')) + codec.write_uint8(ord('1')) + codec.write_uint8(ord(opcode)) + codec.write_uint32(seq) + + def _checkHeader(self, codec): + """ Check the header of a management message and extract the opcode and class. """ + octet = chr(codec.read_uint8()) + if octet != 'A': + return None, None + octet = chr(codec.read_uint8()) + if octet != 'M': + return None, None + octet = chr(codec.read_uint8()) + if octet != '1': + return None, None + opcode = chr(codec.read_uint8()) + seq = codec.read_uint32() + return opcode, seq + + def _message (self, body, routing_key="broker"): + dp = self.amqpSession.delivery_properties() + dp.routing_key = routing_key + mp = self.amqpSession.message_properties() + mp.content_type = "x-application/qmf" + mp.reply_to = self.amqpSession.reply_to("amq.direct", self.replyName) + return Message(dp, mp, body) + + def _send(self, msg, dest="qpid.management"): + self.amqpSession.message_transfer(destination=dest, message=msg) + + def _shutdown(self): + self.amqpSession.incoming("rdest").stop() + if self.session.console != None: + self.amqpSession.incoming("tdest").stop() + self.amqpSession.close() + + def _waitForStable(self): + self.cv.acquire() + if self.reqsOutstanding == 0: + self.cv.release() + return + self.syncInFlight = True + starttime = time() + while self.reqsOutstanding != 0: + self.cv.wait(self.SYNC_TIME) + if time() - starttime > self.SYNC_TIME: + self.cv.release() + raise RuntimeError("Timed out waiting for broker to synchronize") + self.cv.release() + + def _incOutstanding(self): + self.cv.acquire() + self.reqsOutstanding += 1 + self.cv.release() + + def _decOutstanding(self): + self.cv.acquire() + self.reqsOutstanding -= 1 + if self.reqsOutstanding == 0 and not self.topicBound and self.session != None: + self.topicBound = True + self.amqpSession.exchange_bind(exchange="qpid.management", queue=self.topicName, binding_key="mgmt.#") + if self.reqsOutstanding == 0 and self.syncInFlight: + self.syncInFlight = False + self.cv.notify() + self.cv.release() + + def _replyCb(self, msg): + codec = Codec(self.conn.spec, msg.body) + opcode, seq = self._checkHeader(codec) + if opcode == None: + return + if opcode == 'b': self.session._handleBrokerResp (self, codec, seq) + elif opcode == 'p': self.session._handlePackageInd (self, codec, seq) + elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) + elif opcode == 'q': self.session._handleClassInd (self, codec, seq) + elif opcode == 'm': self.session._handleMethodResp (self, codec, seq) + elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq) + elif opcode == 'e': self.session._handleEventInd (self, codec, seq) + elif opcode == 's': self.session._handleSchemaResp (self, codec, seq) + elif opcode == 'c': self.session._handleContentInd (self, codec, seq, prop=True) + elif opcode == 'i': self.session._handleContentInd (self, codec, seq, stat=True) + elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) + + def _exceptionCb(self, data): + pass + +class Agent: + """ """ + def __init__(self, broker, bank): + self.broker = broker + self.bank = bank + +class Event: + """ """ + def __init__(self): + pass + +class SequenceManager: + """ Manage sequence numbers for asynchronous method calls """ + def __init__(self): + self.lock = Lock() + self.sequence = 0 + self.pending = {} + + def _reserve(self, data): + """ Reserve a unique sequence number """ + self.lock.acquire() + result = self.sequence + self.sequence = self.sequence + 1 + self.pending[result] = data + self.lock.release() + return result + + def _release(self, seq): + """ Release a reserved sequence number """ + data = None + self.lock.acquire() + if seq in self.pending: + data = self.pending[seq] + del self.pending[seq] + self.lock.release() + return data + + +# TEST + +c = Console() +s = Session(c) +b = s.addBroker() +cl = s.getClasses("org.apache.qpid.broker") +sch = s.getSchema(cl[0]) + -- cgit v1.2.1 From bb4d2bcb0c11da57773e014e71db2de1e84a7ec8 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 18 Sep 2008 22:19:22 +0000 Subject: QPID-1288 - Removed test code git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@696833 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 96633bca0d..f83b4f315c 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -52,9 +52,7 @@ class Console: def objectProps(self, broker, id, record): """ Invoked when an object is updated. """ - global B - if record.getClassKey()[1] == "broker": - B = record + pass def objectStats(self, broker, id, record): """ Invoked when an object is updated. """ @@ -877,7 +875,7 @@ class Broker: def _decOutstanding(self): self.cv.acquire() self.reqsOutstanding -= 1 - if self.reqsOutstanding == 0 and not self.topicBound and self.session != None: + if self.reqsOutstanding == 0 and not self.topicBound and self.session.console != None: self.topicBound = True self.amqpSession.exchange_bind(exchange="qpid.management", queue=self.topicName, binding_key="mgmt.#") if self.reqsOutstanding == 0 and self.syncInFlight: @@ -945,9 +943,9 @@ class SequenceManager: # TEST -c = Console() -s = Session(c) -b = s.addBroker() -cl = s.getClasses("org.apache.qpid.broker") -sch = s.getSchema(cl[0]) +#c = Console() +#s = Session(c) +#b = s.addBroker() +#cl = s.getClasses("org.apache.qpid.broker") +#sch = s.getSchema(cl[0]) -- cgit v1.2.1 From 49bc5a1a3bf61b521883027fc58b3950920f31f2 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 19 Sep 2008 20:54:08 +0000 Subject: QPID-1288 - Added error handling and remote agent support to the console API. Ported qpid-config and qpid-route to the new API git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@697237 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 220 +++++++++++--------------------- qpid/python/commands/qpid-route | 266 +++++++++++++++------------------------ qpid/python/qpid/qmfconsole.py | 261 ++++++++++++++++++++++++++++++-------- 3 files changed, 387 insertions(+), 360 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index cc9315f7ea..6bc38c7440 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -22,16 +22,7 @@ import os import getopt import sys -import socket -import qpid -from threading import Condition -from qpid.management import managementClient -from qpid.managementdata import Broker -from qpid.peer import Closed -from qpid.connection import Connection, ConnectionFailed -from qpid.datatypes import uuid4 -from qpid.util import connect -from time import sleep +from qpid import qmfconsole _recursive = False _host = "localhost" @@ -78,44 +69,21 @@ def Usage (): class BrokerManager: def __init__ (self): - self.dest = None - self.src = None - self.broker = None - - def SetBroker (self, broker): - self.broker = broker - - def ConnectToBroker (self): - try: - self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (self.broker.host, self.broker.port), - username=self.broker.username, password=self.broker.password) - self.conn.start () - self.session = self.conn.session (self.sessionId) - self.mclient = managementClient (self.conn.spec) - self.mchannel = self.mclient.addChannel (self.session) - except socket.error, e: - print "Socket Error %s - %s" % (e[0], e[1]) - sys.exit (1) - except Closed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit (1) - except ConnectionFailed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit(1) - - def Disconnect (self): - self.mclient.removeChannel (self.mchannel) - self.session.close(timeout=10) - self.conn.close(timeout=10) + self.brokerName = None + self.qmf = None + self.broker = None + + def SetBroker (self, brokerUrl): + self.url = brokerUrl + self.qmf = qmfconsole.Session() + self.broker = self.qmf.addBroker(brokerUrl) + + def Disconnect(self): + self.qmf.delBroker(self.broker) def Overview (self): - self.ConnectToBroker () - mc = self.mclient - mch = self.mchannel - mc.syncWaitForStable (mch) - exchanges = mc.syncGetObjects (mch, "exchange") - queues = mc.syncGetObjects (mch, "queue") + exchanges = self.qmf.getObjects(name="exchange") + queues = self.qmf.getObjects(name="queue") print "Total Exchanges: %d" % len (exchanges) etype = {} for ex in exchanges: @@ -136,11 +104,7 @@ class BrokerManager: print " non-durable: %d" % (len (queues) - _durable) def ExchangeList (self, filter): - self.ConnectToBroker () - mc = self.mclient - mch = self.mchannel - mc.syncWaitForStable (mch) - exchanges = mc.syncGetObjects (mch, "exchange") + exchanges = self.qmf.getObjects(name="exchange") print "Durable Type Bindings Exchange Name" print "=======================================================" for ex in exchanges: @@ -148,18 +112,14 @@ class BrokerManager: print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindingCount, ex.name) def ExchangeListRecurse (self, filter): - self.ConnectToBroker () - mc = self.mclient - mch = self.mchannel - mc.syncWaitForStable (mch) - exchanges = mc.syncGetObjects (mch, "exchange") - bindings = mc.syncGetObjects (mch, "binding") - queues = mc.syncGetObjects (mch, "queue") + exchanges = self.qmf.getObjects(name="exchange") + bindings = self.qmf.getObjects(name="binding") + queues = self.qmf.getObjects(name="queue") for ex in exchanges: if self.match (ex.name, filter): print "Exchange '%s' (%s)" % (ex.name, ex.type) for bind in bindings: - if bind.exchangeRef == ex.id: + if bind.exchangeRef == ex.getObjectId(): qname = "" queue = self.findById (queues, bind.queueRef) if queue != None: @@ -168,12 +128,8 @@ class BrokerManager: def QueueList (self, filter): - self.ConnectToBroker () - mc = self.mclient - mch = self.mchannel - mc.syncWaitForStable (mch) - queues = mc.syncGetObjects (mch, "queue") - journals = mc.syncGetObjects (mch, "journal") + queues = self.qmf.getObjects(name="queue") + journals = self.qmf.getObjects(name="journal") print " Store Size" print "Durable AutoDel Excl Bindings (files x file pages) Queue Name" print "===========================================================================================" @@ -193,18 +149,14 @@ class BrokerManager: YN (q.exclusive), q.bindingCount, q.name) def QueueListRecurse (self, filter): - self.ConnectToBroker () - mc = self.mclient - mch = self.mchannel - mc.syncWaitForStable (mch) - exchanges = mc.syncGetObjects (mch, "exchange") - bindings = mc.syncGetObjects (mch, "binding") - queues = mc.syncGetObjects (mch, "queue") + exchanges = self.qmf.getObjects(name="exchange") + bindings = self.qmf.getObjects(name="binding") + queues = self.qmf.getObjects(name="queue") for queue in queues: if self.match (queue.name, filter): print "Queue '%s'" % queue.name for bind in bindings: - if bind.queueRef == queue.id: + if bind.queueRef == queue.getObjectId(): ename = "" ex = self.findById (exchanges, bind.exchangeRef) if ex != None: @@ -216,30 +168,19 @@ class BrokerManager: def AddExchange (self, args): if len (args) < 2: Usage () - self.ConnectToBroker () etype = args[0] ename = args[1] - - try: - self.session.exchange_declare (exchange=ename, type=etype, durable=_durable) - except Closed, e: - print "Failed:", e + self.broker.getAmqpSession().exchange_declare (exchange=ename, type=etype, durable=_durable) def DelExchange (self, args): if len (args) < 1: Usage () - self.ConnectToBroker () ename = args[0] - - try: - self.session.exchange_delete (exchange=ename) - except Closed, e: - print "Failed:", e + self.broker.getAmqpSession().exchange_delete (exchange=ename) def AddQueue (self, args): if len (args) < 1: Usage () - self.ConnectToBroker () qname = args[0] declArgs = {} if _durable: @@ -251,55 +192,37 @@ class BrokerManager: if _maxQueueCount: declArgs[MAX_QUEUE_COUNT] = _maxQueueCount - try: - self.session.queue_declare (queue=qname, durable=_durable, arguments=declArgs) - except Closed, e: - print "Failed:", e + self.broker.getAmqpSession().queue_declare (queue=qname, durable=_durable, arguments=declArgs) def DelQueue (self, args): if len (args) < 1: Usage () - self.ConnectToBroker () qname = args[0] - - try: - self.session.queue_delete (queue=qname) - except Closed, e: - print "Failed:", e + self.broker.getAmqpSession().queue_delete (queue=qname) def Bind (self, args): if len (args) < 2: Usage () - self.ConnectToBroker () ename = args[0] qname = args[1] key = "" if len (args) > 2: key = args[2] - - try: - self.session.exchange_bind (queue=qname, exchange=ename, binding_key=key) - except Closed, e: - print "Failed:", e + self.broker.getAmqpSession().exchange_bind (queue=qname, exchange=ename, binding_key=key) def Unbind (self, args): if len (args) < 2: Usage () - self.ConnectToBroker () ename = args[0] qname = args[1] key = "" if len (args) > 2: key = args[2] - - try: - self.session.exchange_unbind (queue=qname, exchange=ename, binding_key=key) - except Closed, e: - print "Failed:", e + self.broker.getAmqpSession().exchange_unbind (queue=qname, exchange=ename, binding_key=key) def findById (self, items, id): for item in items: - if item.id == id: + if item.getObjectId() == id: return item return None @@ -343,43 +266,48 @@ for opt in optlist: nargs = len (cargs) bm = BrokerManager () -bm.SetBroker (Broker (_host)) - -if nargs == 0: - bm.Overview () -else: - cmd = cargs[0] - modifier = "" - if nargs > 1: - modifier = cargs[1] - if cmd[0] == 'e': - if _recursive: - bm.ExchangeListRecurse (modifier) - else: - bm.ExchangeList (modifier) - elif cmd[0] == 'q': - if _recursive: - bm.QueueListRecurse (modifier) - else: - bm.QueueList (modifier) - elif cmd == "add": - if modifier == "exchange": - bm.AddExchange (cargs[2:]) - elif modifier == "queue": - bm.AddQueue (cargs[2:]) - else: - Usage () - elif cmd == "del": - if modifier == "exchange": - bm.DelExchange (cargs[2:]) - elif modifier == "queue": - bm.DelQueue (cargs[2:]) + +try: + bm.SetBroker(qmfconsole.BrokerURL(_host)) + if nargs == 0: + bm.Overview () + else: + cmd = cargs[0] + modifier = "" + if nargs > 1: + modifier = cargs[1] + if cmd[0] == 'e': + if _recursive: + bm.ExchangeListRecurse (modifier) + else: + bm.ExchangeList (modifier) + elif cmd[0] == 'q': + if _recursive: + bm.QueueListRecurse (modifier) + else: + bm.QueueList (modifier) + elif cmd == "add": + if modifier == "exchange": + bm.AddExchange (cargs[2:]) + elif modifier == "queue": + bm.AddQueue (cargs[2:]) + else: + Usage () + elif cmd == "del": + if modifier == "exchange": + bm.DelExchange (cargs[2:]) + elif modifier == "queue": + bm.DelQueue (cargs[2:]) + else: + Usage () + elif cmd == "bind": + bm.Bind (cargs[1:]) + elif cmd == "unbind": + bm.Unbind (cargs[1:]) else: Usage () - elif cmd == "bind": - bm.Bind (cargs[1:]) - elif cmd == "unbind": - bm.Unbind (cargs[1:]) - else: - Usage () +except Exception,e: + print "Failed:", e.message + sys.exit(1) + bm.Disconnect() diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 3cd9109a6a..172927b72a 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -22,13 +22,8 @@ import getopt import sys import socket -import qpid import os -from qpid.management import managementClient -from qpid.managementdata import Broker -from qpid.peer import Closed -from qpid.connection import Connection, ConnectionFailed -from qpid.util import connect +from qpid import qmfconsole def Usage (): print "Usage: qpid-route [OPTIONS] link add " @@ -58,93 +53,57 @@ _dellink = False class RouteManager: def __init__ (self, destBroker): - self.dest = Broker (destBroker) + self.dest = qmfconsole.BrokerURL(destBroker) self.src = None - - def ConnectToBroker (self): - broker = self.dest - if _verbose: - print "Connecting to broker: %s:%d" % (broker.host, broker.port) - try: - self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (broker.host, broker.port), \ - username=broker.username, password=broker.password) - self.conn.start () - self.session = self.conn.session(self.sessionId) - self.mclient = managementClient (self.conn.spec) - self.mch = self.mclient.addChannel (self.session) - self.mclient.syncWaitForStable (self.mch) - except socket.error, e: - print "Socket Error %s - %s" % (e[0], e[1]) - sys.exit (1) - except Closed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit (1) - except ConnectionFailed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit(1) + self.qmf = qmfconsole.Session() + self.broker = self.qmf.addBroker(destBroker) def Disconnect (self): - self.mclient.removeChannel (self.mch) - self.session.close(timeout=10) - self.conn.close(timeout=10) + self.qmf.delBroker(self.broker) def getLink (self): - links = self.mclient.syncGetObjects (self.mch, "link") + links = self.qmf.getObjects(name="link") for link in links: if "%s:%d" % (link.host, link.port) == self.src.name (): return link return None def AddLink (self, srcBroker): - self.src = Broker (srcBroker) - mc = self.mclient - + self.src = qmfconsole.BrokerURL(srcBroker) if self.dest.name() == self.src.name(): print "Linking broker to itself is not permitted" sys.exit(1) - brokers = mc.syncGetObjects (self.mch, "broker") + brokers = self.qmf.getObjects(name="broker") broker = brokers[0] link = self.getLink() if link != None: - print "Link already exists" - sys.exit(1) + raise Exception("Link already exists") - connectArgs = {} - connectArgs["host"] = self.src.host - connectArgs["port"] = self.src.port - connectArgs["useSsl"] = False - connectArgs["durable"] = _durable - if self.src.username == "anonymous": - connectArgs["authMechanism"] = "ANONYMOUS" + if self.src.authName == "anonymous": + mech = "ANONYMOUS" else: - connectArgs["authMechanism"] = "PLAIN" - connectArgs["username"] = self.src.username - connectArgs["password"] = self.src.password - res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) + mech = "PLAIN" + res = broker.connect(self.src.host, self.src.port, False, _durable, + mech, self.src.authName, self.src.authPass) if _verbose: - print "Connect method returned:", res.status, res.statusText - link = self.getLink () + print "Connect method returned:", res.status, res.text + link = self.getLink() def DelLink (self, srcBroker): - self.src = Broker (srcBroker) - mc = self.mclient - - brokers = mc.syncGetObjects (self.mch, "broker") + self.src = qmfconsole.BrokerURL(srcBroker) + brokers = self.qmf.getObjects(name="broker") broker = brokers[0] link = self.getLink() if link == None: - print "Link not found" - sys.exit(1) + raise Exception("Link not found") - res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + res = link.close() if _verbose: - print "Close method returned:", res.status, res.statusText + print "Close method returned:", res.status, res.text def ListLinks (self): - mc = self.mclient - links = mc.syncGetObjects (self.mch, "link") + links = self.qmf.getObjects(name="link") if len(links) == 0: print "No Links Found" else: @@ -152,145 +111,118 @@ class RouteManager: print "Host Port Durable State Last Error" print "===================================================================" for link in links: - print "%-16s%-8d %c %-18s%s" % (link.host, link.port, YN(link.durable), link.state, link.lastError) + print "%-16s%-8d %c %-18s%s" % \ + (link.host, link.port, YN(link.durable), link.state, link.lastError) def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes): - self.src = Broker (srcBroker) - mc = self.mclient - + self.src = qmfconsole.BrokerURL(srcBroker) if self.dest.name() == self.src.name(): - print "Linking broker to itself is not permitted" - sys.exit(1) + raise Exception("Linking broker to itself is not permitted") - brokers = mc.syncGetObjects (self.mch, "broker") + brokers = self.qmf.getObjects(name="broker") broker = brokers[0] - link = self.getLink () + link = self.getLink() if link == None: if _verbose: print "Inter-broker link not found, creating..." - connectArgs = {} - connectArgs["host"] = self.src.host - connectArgs["port"] = self.src.port - connectArgs["useSsl"] = False - connectArgs["durable"] = _durable - if self.src.username == "anonymous": - connectArgs["authMechanism"] = "ANONYMOUS" + if self.src.authName == "anonymous": + mech = "ANONYMOUS" else: - connectArgs["authMechanism"] = "PLAIN" - connectArgs["username"] = self.src.username - connectArgs["password"] = self.src.password - res = mc.syncCallMethod (self.mch, broker.id, broker.classKey, "connect", connectArgs) + mech = "PLAIN" + res = broker.connect(self.src.host, self.src.port, False, _durable, + mech, self.src.authName, self.src.authPass) if _verbose: - print "Connect method returned:", res.status, res.statusText - link = self.getLink () + print "Connect method returned:", res.status, res.text + link = self.getLink() if link == None: - print "Protocol Error - Missing link ID" - sys.exit (1) + raise Exception("Protocol Error - Missing link ID") - bridges = mc.syncGetObjects (self.mch, "bridge") + bridges = self.qmf.getObjects(name="bridge") for bridge in bridges: - if bridge.linkRef == link.id and bridge.dest == exchange and bridge.key == routingKey: + if bridge.linkRef == link.getObjectId() and \ + bridge.dest == exchange and bridge.key == routingKey: if not _quiet: - print "Duplicate Route - ignoring: %s(%s)" % (exchange, routingKey) - sys.exit (1) + raise Exception("Duplicate Route - ignoring: %s(%s)" % (exchange, routingKey)) sys.exit (0) if _verbose: print "Creating inter-broker binding..." - bridgeArgs = {} - bridgeArgs["durable"] = _durable - bridgeArgs["src"] = exchange - bridgeArgs["dest"] = exchange - bridgeArgs["key"] = routingKey - bridgeArgs["tag"] = tag - bridgeArgs["excludes"] = excludes - bridgeArgs["srcIsQueue"] = 0 - bridgeArgs["srcIsLocal"] = 0 - res = mc.syncCallMethod (self.mch, link.id, link.classKey, "bridge", bridgeArgs) + res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, 0, 0) if res.status == 4: - print "Can't create a durable route on a non-durable link" - sys.exit(1) + raise Exception("Can't create a durable route on a non-durable link") if _verbose: print "Bridge method returned:", res.status, res.statusText def DelRoute (self, srcBroker, exchange, routingKey): - self.src = Broker (srcBroker) - mc = self.mclient - - link = self.getLink () + self.src = qmfconsole.BrokerURL(srcBroker) + link = self.getLink() if link == None: if not _quiet: - print "No link found from %s to %s" % (self.src.name(), self.dest.name()) - sys.exit (1) + raise Exception("No link found from %s to %s" % (self.src.name(), self.dest.name())) sys.exit (0) - bridges = mc.syncGetObjects (self.mch, "bridge") + bridges = self.qmf.getObjects(name="bridge") for bridge in bridges: - if bridge.linkRef == link.id and bridge.dest == exchange and bridge.key == routingKey: + if bridge.linkRef == link.getObjectId() and bridge.dest == exchange and bridge.key == routingKey: if _verbose: print "Closing bridge..." - res = mc.syncCallMethod (self.mch, bridge.id, bridge.classKey, "close") + res = bridge.close() if res.status != 0: - print "Error closing bridge: %d - %s" % (res.status, res.statusText) - sys.exit (1) + raise Exception("Error closing bridge: %d - %s" % (res.status, res.statusText)) if len (bridges) == 1 and _dellink: link = self.getLink () if link == None: sys.exit (0) if _verbose: print "Last bridge on link, closing link..." - res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + res = link.close() if res.status != 0: - print "Error closing link: %d - %s" % (res.status, res.statusText) - sys.exit (1) + raise Exception("Error closing link: %d - %s" % (res.status, res.statusText)) sys.exit (0) if not _quiet: - print "Route not found" - sys.exit (1) + raise Exception("Route not found") def ListRoutes (self): - mc = self.mclient - links = mc.syncGetObjects (self.mch, "link") - bridges = mc.syncGetObjects (self.mch, "bridge") + links = self.qmf.getObjects(name="link") + bridges = self.qmf.getObjects(name="bridge") for bridge in bridges: myLink = None for link in links: - if bridge.linkRef == link.id: + if bridge.linkRef == link.getObjectId(): myLink = link break if myLink != None: print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, bridge.key) def ClearAllRoutes (self): - mc = self.mclient - links = mc.syncGetObjects (self.mch, "link") - bridges = mc.syncGetObjects (self.mch, "bridge") + links = self.qmf.getObjects(name="link") + bridges = self.qmf.getObjects(name="bridge") for bridge in bridges: if _verbose: myLink = None for link in links: - if bridge.linkRef == link.id: + if bridge.linkRef == link.getObjectId(): myLink = link break if myLink != None: print "Deleting Bridge: %s:%d %s %s... " % (myLink.host, myLink.port, bridge.dest, bridge.key), - res = mc.syncCallMethod (self.mch, bridge.id, bridge.classKey, "close") + res = bridge.close() if res.status != 0: print "Error: %d - %s" % (res.status, res.statusText) elif _verbose: print "Ok" if _dellink: - links = mc.syncGetObjects (self.mch, "link") + links = self.qmf.getObjects(name="link") for link in links: if _verbose: print "Deleting Link: %s:%d... " % (link.host, link.port), - res = mc.syncCallMethod (self.mch, link.id, link.classKey, "close") + res = link.close() if res.status != 0: print "Error: %d - %s" % (res.status, res.statusText) elif _verbose: @@ -331,41 +263,45 @@ else: group = cargs[0] cmd = cargs[1] -rm = RouteManager (destBroker) -rm.ConnectToBroker () - -if group == "link": - if cmd == "add": - if nargs != 4: - Usage() - rm.AddLink (cargs[3]) - elif cmd == "del": - if nargs != 4: - Usage() - rm.DelLink (cargs[3]) - elif cmd == "list": - rm.ListLinks () - -elif group == "route": - if cmd == "add": - if nargs < 6 or nargs > 8: - Usage () - - tag = "" - excludes = "" - if nargs > 6: tag = cargs[6] - if nargs > 7: excludes = cargs[7] - rm.AddRoute (cargs[3], cargs[4], cargs[5], tag, excludes) - elif cmd == "del": - if nargs != 6: - Usage () - else: - rm.DelRoute (cargs[3], cargs[4], cargs[5]) - else: - if cmd == "list": - rm.ListRoutes () - elif cmd == "flush": - rm.ClearAllRoutes () + +try: + rm = RouteManager (destBroker) + if group == "link": + if cmd == "add": + if nargs != 4: + Usage() + rm.AddLink (cargs[3]) + elif cmd == "del": + if nargs != 4: + Usage() + rm.DelLink (cargs[3]) + elif cmd == "list": + rm.ListLinks () + + elif group == "route": + if cmd == "add": + if nargs < 6 or nargs > 8: + Usage () + + tag = "" + excludes = "" + if nargs > 6: tag = cargs[6] + if nargs > 7: excludes = cargs[7] + rm.AddRoute (cargs[3], cargs[4], cargs[5], tag, excludes) + elif cmd == "del": + if nargs != 6: + Usage () + else: + rm.DelRoute (cargs[3], cargs[4], cargs[5]) else: - Usage () + if cmd == "list": + rm.ListRoutes () + elif cmd == "flush": + rm.ClearAllRoutes () + else: + Usage () +except Exception,e: + print "Failed:", e.message + sys.exit(1) + rm.Disconnect () diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index f83b4f315c..435da475d7 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -23,6 +23,7 @@ import os import qpid import struct import socket +import re from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed from qpid.datatypes import uuid4 @@ -46,10 +47,14 @@ class Console: used to obtain details about the class.""" pass - def newAgent(self, broker, agent): + def newAgent(self, agent): """ Invoked when a QMF agent is discovered. """ pass + def delAgent(self, agent): + """ Invoked when a QMF agent disconects. """ + pass + def objectProps(self, broker, id, record): """ Invoked when an object is updated. """ pass @@ -70,6 +75,25 @@ class Console: """ """ pass +class BrokerURL: + def __init__(self, text): + rex = re.compile(r""" + # [ [ / ] @] [ : ] + ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) + match = rex.match(text) + if not match: raise ValueError("'%s' is not a valid broker url" % (text)) + user, password, host, port = match.groups() + + self.host = socket.gethostbyname(host) + if port: self.port = int(port) + else: self.port = 5672 + self.authName = user or "guest" + self.authPass = password or "guest" + self.authMech = "PLAIN" + + def name(self): + return self.host + ":" + str(self.port) + class Session: """ An instance of the Session class represents a console session running @@ -95,12 +119,17 @@ class Session: self.cv = Condition() self.syncSequenceList = [] self.getResult = [] + self.error = None + + def __repr__(self): + return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) - def addBroker(self, host="localhost", port=5672, - authMech="PLAIN", authName="guest", authPass="guest"): + def addBroker(self, target="localhost"): """ Connect to a Qpid broker. Returns an object of type Broker. """ - broker = Broker(self, host, port, authMech, authName, authPass) + url = BrokerURL(target) + broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass) self.brokers.append(broker) + self.getObjects(broker=broker, name="agent") return broker def delBroker(self, broker): @@ -138,11 +167,22 @@ class Session: if (cname, hash) in self.packages[pname]: return self.packages[pname][(cname, hash)] - def getAgents(self): + def getAgents(self, broker=None): """ Get a list of currently known agents """ - for broker in self.brokers: - broker._waitForStable() - pass + brokerList = [] + if broker == None: + for b in self.brokers: + brokerList.append(b) + else: + brokerList.append(broker) + + for b in brokerList: + b._waitForStable() + agentList = [] + for b in brokerList: + for a in b.getAgents(): + agentList.append(a) + return agentList def getObjects(self, **kwargs): """ Get a list of objects from QMF agents. @@ -165,7 +205,8 @@ class Session: broker = - supply a broker as returned by addBroker """ if "broker" in kwargs: - brokerList = [].append(kwargs["broker"]) + brokerList = [] + brokerList.append(kwargs["broker"]) else: brokerList = self.brokers for broker in brokerList: @@ -176,7 +217,7 @@ class Session: agent = kwargs["agent"] if agent.broker not in brokerList: raise Exception("Supplied agent is not accessible through the supplied broker") - agentList = append(agent) + agentList.append(agent) else: for broker in brokerList: for agent in broker.getAgents(): @@ -209,7 +250,7 @@ class Session: starttime = time() timeout = False self.cv.acquire() - while len(self.syncSequenceList) > 0: + while len(self.syncSequenceList) > 0 and self.error == None: self.cv.wait(self.GET_WAIT_TIME) if time() - starttime > self.GET_WAIT_TIME: for pendingSeq in self.syncSequenceList: @@ -218,6 +259,11 @@ class Session: timeout = True self.cv.release() + if self.error: + errorText = self.error + self.error = None + raise Exception(errorText) + if len(self.getResult) == 0 and timeout: raise RuntimeError("No agent responded within timeout period") return self.getResult @@ -351,6 +397,8 @@ class Session: self.cv.release() schema = self.packages[pname][(cname, hash)] object = Object(self, broker, schema, codec, prop, stat) + if pname == "org.apache.qpid.broker" and cname == "agent": + broker._updateAgent(object) self.cv.acquire() if seq in self.syncSequenceList: @@ -365,6 +413,13 @@ class Session: if stat: self.console.objectStats(broker, object.getObjectId(), object) + def _handleError(self, error): + self.error = error + self.cv.acquire() + self.syncSequenceList = [] + self.cv.notify() + self.cv.release() + class Package: """ """ def __init__(self, name): @@ -407,23 +462,23 @@ class SchemaClass: return result def getKey(self): - """ """ + """ Return the class-key for this class. """ return self.classKey def getProperties(self): - """ """ + """ Return the list of properties for the class. """ return self.properties def getStatistics(self): - """ """ + """ Return the list of statistics for the class. """ return self.statistics def getMethods(self): - """ """ + """ Return the list of methods for the class. """ return self.methods def getEvents(self): - """ """ + """ Return the list of events for the class. """ return self.events class SchemaProperty: @@ -448,6 +503,9 @@ class SchemaProperty: elif key == "maxlen" : self.maxlen = value elif key == "desc" : self.desc = str(value) + def __repr__(self): + return self.name + class SchemaStatistic: """ """ def __init__(self, codec): @@ -461,6 +519,9 @@ class SchemaStatistic: if key == "unit" : self.unit = str(value) elif key == "desc" : self.desc = str(value) + def __repr__(self): + return self.name + class SchemaMethod: """ """ def __init__(self, codec): @@ -476,6 +537,19 @@ class SchemaMethod: for idx in range(argCount): self.arguments.append(SchemaArgument(codec, methodArg=True)) + def __repr__(self): + result = self.name + "(" + first = True + for arg in self.arguments: + if arg.dir.find("I") != -1: + if first: + first = False + else: + result += ", " + result += arg.name + result += ")" + return result + class SchemaEvent: """ """ def __init__(self, codec): @@ -491,6 +565,18 @@ class SchemaEvent: for idx in range(argCount): self.arguments.append(SchemaArgument(codec, methodArg=False)) + def __repr__(self): + result = self.name + "(" + first = True + for arg in self.arguments: + if first: + first = False + else: + result += ", " + result += arg.name + result += ")" + return result + class SchemaArgument: """ """ def __init__(self, codec, methodArg): @@ -538,12 +624,8 @@ class ObjectId(object): return 0 def __repr__(self): - return "%08x-%04x-%04x-%04x-%04x%08x" % ((self.first & 0xFFFFFFFF00000000) >> 32, - (self.first & 0x00000000FFFF0000) >> 16, - (self.first & 0x000000000000FFFF), - (self.second & 0xFFFF000000000000) >> 48, - (self.second & 0x0000FFFF00000000) >> 32, - (self.second & 0x00000000FFFFFFFF)) + return "%d-%d-%d-%d-%x" % (self.getFlags(), self.getSequence(), + self.getBroker(), self.getBank(), self.getObject()) def index(self): return (self.first, self.second) @@ -596,23 +678,27 @@ class Object(object): self.statistics.append((statistic, self._decodeValue(codec, statistic.type))) def getObjectId(self): - """ """ + """ Return the object identifier for this object """ return self.objectId def getClassKey(self): - """ """ + """ Return the class-key that references the schema describing this object. """ return self.schema.getKey() def getSchema(self): - """ """ + """ Return the schema that describes this object. """ return self.schema + def getMethods(self): + """ Return a list of methods available for this object. """ + return self.schema.getMethods() + def getTimestamps(self): - """ """ + """ Return the current, creation, and deletion times for this object. """ return self.currentTime, self.createTime, self.deleteTime def getIndex(self): - """ """ + """ Return a string describing this object's primary key. """ result = "" for property, value in self.properties: if property.index: @@ -634,6 +720,7 @@ class Object(object): for statistic, value in self.statistics: if name == statistic.name: return value + raise Exception("Type Object has no attribute '%s'" % name) def _invoke(self, name, args, kwargs): for method in self.schema.getMethods(): @@ -653,20 +740,27 @@ class Object(object): self._encodeValue(sendCodec, args[aIdx], arg.type) aIdx += 1 smsg = self.broker._message(sendCodec.encoded, "agent." + str(self.objectId.getBank())) - self.broker._send(smsg) self.broker.cv.acquire() self.broker.syncInFlight = True + self.broker.cv.release() + + self.broker._send(smsg) + + self.broker.cv.acquire() starttime = time() - while self.broker.syncInFlight: + while self.broker.syncInFlight and self.broker.error == None: self.broker.cv.wait(self.broker.SYNC_TIME) if time() - starttime > self.broker.SYNC_TIME: self.broker.cv.release() self.session.seqMgr._release(seq) raise RuntimeError("Timed out waiting for method to respond") self.broker.cv.release() + if self.broker.error != None: + errorText = self.broker.error + self.broker.error = None + raise Exception(errorText) return self.broker.syncResult - else: - raise Exception("Invalid Method (software defect)") + raise Exception("Invalid Method (software defect) [%s]" % name) def _parsePresenceMasks(self, codec, schema): excludeList = [] @@ -747,18 +841,21 @@ class MethodResult(object): class Broker: """ """ - SYNC_TIME = 10 + SYNC_TIME = 10 def __init__(self, session, host, port, authMech, authUser, authPass): self.session = session - self.agents = [] - self.agents.append(Agent(self, 0)) + self.host = host + self.port = port + self.agents = {} + self.agents[0] = Agent(self, 0, "BrokerAgent") self.topicBound = False self.cv = Condition() self.syncInFlight = False self.syncRequest = 0 self.syncResult = None self.reqsOutstanding = 1 + self.error = None self.brokerId = None err = None try: @@ -767,7 +864,7 @@ class Broker: self.conn.start() self.replyName = "reply-%s" % self.amqpSessionId self.amqpSession = self.conn.session(self.amqpSessionId) - self.amqpSession.auto_sync = False + self.amqpSession.auto_sync = True self.amqpSession.queue_declare(queue=self.replyName, exclusive=True, auto_delete=True) self.amqpSession.exchange_bind(exchange="amq.direct", queue=self.replyName, binding_key=self.replyName) @@ -798,6 +895,7 @@ class Broker: except ConnectionFailed, e: err = "Connect Failed %d - %s" % (e[0], e[1]) + self.active = True if err != None: raise Exception(err) @@ -811,7 +909,36 @@ class Broker: def getAgents(self): """ Get the list of agents reachable via this broker """ - return self.agents + return self.agents.values() + + def getAmqpSession(self): + """ Get the AMQP session object for this connected broker. """ + return self.amqpSession + + def isConnected(self): + return self.active + + def __repr__(self): + if self.active: + if self.port == 5672: + port = "" + else: + port = ":%d" % self.port + return "Broker connected at: amqp://%s%s" % (self.host, port) + else: + return "Disconnected Broker" + + def _updateAgent(self, obj): + if obj.deleteTime == 0: + if obj.objectIdBank not in self.agents: + agent = Agent(self, obj.objectIdBank, obj.label) + self.agents[obj.objectIdBank] = agent + if self.session.console != None: + self.session.console.newAgent(agent) + else: + agent = self.agents.pop(obj.objectIdBank, None) + if agent != None and self.session.console != None: + self.session.console.delAgent(agent) def _setHeader(self, codec, opcode, seq=0): """ Compose the header of a management message. """ @@ -848,10 +975,14 @@ class Broker: self.amqpSession.message_transfer(destination=dest, message=msg) def _shutdown(self): - self.amqpSession.incoming("rdest").stop() - if self.session.console != None: - self.amqpSession.incoming("tdest").stop() - self.amqpSession.close() + if self.active: + self.amqpSession.incoming("rdest").stop() + if self.session.console != None: + self.amqpSession.incoming("tdest").stop() + self.amqpSession.close() + self.active = False + else: + raise Exception("Broker already disconnected") def _waitForStable(self): self.cv.acquire() @@ -877,7 +1008,8 @@ class Broker: self.reqsOutstanding -= 1 if self.reqsOutstanding == 0 and not self.topicBound and self.session.console != None: self.topicBound = True - self.amqpSession.exchange_bind(exchange="qpid.management", queue=self.topicName, binding_key="mgmt.#") + self.amqpSession.exchange_bind(exchange="qpid.management", + queue=self.topicName, binding_key="mgmt.#") if self.reqsOutstanding == 0 and self.syncInFlight: self.syncInFlight = False self.cv.notify() @@ -901,13 +1033,23 @@ class Broker: elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) def _exceptionCb(self, data): - pass + self.active = False + self.error = data + self.cv.acquire() + if self.syncInFlight: + self.cv.notify() + self.cv.release() + self.session._handleError(self.error) class Agent: """ """ - def __init__(self, broker, bank): + def __init__(self, broker, bank, label): self.broker = broker self.bank = bank + self.label = label + + def __repr__(self): + return "Agent at bank %d (%s)" % (self.bank, self.label) class Event: """ """ @@ -941,11 +1083,32 @@ class SequenceManager: return data -# TEST +class DebugConsole(Console): + """ """ + def newPackage(self, name): + print "newPackage:", name + + def newClass(self, classKey): + print "newClass:", classKey + + def newAgent(self, agent): + print "newAgent:", agent + + def delAgent(self, agent): + print "delAgent:", agent -#c = Console() -#s = Session(c) -#b = s.addBroker() -#cl = s.getClasses("org.apache.qpid.broker") -#sch = s.getSchema(cl[0]) + def objectProps(self, broker, id, record): + print "objectProps:", record.getClassKey() + + def objectStats(self, broker, id, record): + print "objectStats:", record.getClassKey() + + def event(self, broker, event): + print "event:", event + + def heartbeat(self, agent, timestamp): + print "heartbeat:", agent + + def brokerInfo(self, broker): + print "brokerInfo:", broker -- cgit v1.2.1 From fd64334f3676e7a0ef031732fbef37b73b90d706 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 19 Sep 2008 21:01:07 +0000 Subject: One more fix for qpid-route git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@697240 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 172927b72a..8a9522e53a 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -154,7 +154,7 @@ class RouteManager: if res.status == 4: raise Exception("Can't create a durable route on a non-durable link") if _verbose: - print "Bridge method returned:", res.status, res.statusText + print "Bridge method returned:", res.status, res.text def DelRoute (self, srcBroker, exchange, routingKey): self.src = qmfconsole.BrokerURL(srcBroker) @@ -171,7 +171,7 @@ class RouteManager: print "Closing bridge..." res = bridge.close() if res.status != 0: - raise Exception("Error closing bridge: %d - %s" % (res.status, res.statusText)) + raise Exception("Error closing bridge: %d - %s" % (res.status, res.text)) if len (bridges) == 1 and _dellink: link = self.getLink () if link == None: @@ -180,7 +180,7 @@ class RouteManager: print "Last bridge on link, closing link..." res = link.close() if res.status != 0: - raise Exception("Error closing link: %d - %s" % (res.status, res.statusText)) + raise Exception("Error closing link: %d - %s" % (res.status, res.text)) sys.exit (0) if not _quiet: raise Exception("Route not found") @@ -213,7 +213,7 @@ class RouteManager: print "Deleting Bridge: %s:%d %s %s... " % (myLink.host, myLink.port, bridge.dest, bridge.key), res = bridge.close() if res.status != 0: - print "Error: %d - %s" % (res.status, res.statusText) + print "Error: %d - %s" % (res.status, res.text) elif _verbose: print "Ok" @@ -224,7 +224,7 @@ class RouteManager: print "Deleting Link: %s:%d... " % (link.host, link.port), res = link.close() if res.status != 0: - print "Error: %d - %s" % (res.status, res.statusText) + print "Error: %d - %s" % (res.status, res.text) elif _verbose: print "Ok" -- cgit v1.2.1 From 6639c7a34f3735d29385ed98c2115024b43976f2 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 22 Sep 2008 14:13:48 +0000 Subject: Cleaned up display and parsing of class names for qpid-tool git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@697838 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/managementdata.py | 44 ++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 5a8b9cdf9d..d86dd3a360 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -90,7 +90,12 @@ class ManagementData: def displayClassName (self, cls): (packageName, className, hash) = cls - return packageName + "." + className + rev = self.schema[cls][4] + if rev == 0: + suffix = "" + else: + suffix = ".%d" % rev + return packageName + ":" + className + suffix def dataHandler (self, context, className, list, timestamps): """ Callback for configuration and instrumentation data updates """ @@ -270,14 +275,21 @@ class ManagementData: return result def getClassKey (self, className): - dotPos = className.find(".") - if dotPos == -1: + delimPos = className.find(":") + if delimPos == -1: + schemaRev = 0 + delim = className.find(".") + if delim != -1: + schemaRev = int(className[delim + 1:]) + name = className[0:delim] + else: + name = className for key in self.schema: - if key[1] == className: + if key[1] == name and self.schema[key][4] == schemaRev: return key else: - package = className[0:dotPos] - name = className[dotPos + 1:] + package = className[0:delimPos] + name = className[delimPos + 1:] schemaRev = 0 delim = name.find(".") if delim != -1: @@ -448,7 +460,7 @@ class ManagementData: objIndex = self.getObjIndex (classKey, config) row = (objId, createTime, destroyTime, objIndex) rows.append (row) - self.disp.table ("Objects of type %s.%s" % (classKey[0], classKey[1]), + self.disp.table ("Objects of type %s" % self.displayClassName(classKey), ("ID", "Created", "Destroyed", "Index"), rows) finally: @@ -515,7 +527,7 @@ class ManagementData: titleRow = ("Type", "Element") for id in ids: titleRow = titleRow + (self.refName(id),) - caption = "Object of type %s.%s:" % (classKey[0], classKey[1]) + caption = "Object of type %s:" % self.displayClassName(classKey) if timestamp != None: caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" self.disp.table (caption, titleRow, rows) @@ -533,12 +545,8 @@ class ManagementData: sorted.sort () for classKey in sorted: tuple = self.schema[classKey] - if tuple[4] == 0: - suffix = "" - else: - suffix = ".%d" % tuple[4] - className = classKey[0] + "." + classKey[1] + suffix - row = (className, len (tuple[0]), len (tuple[1]), len (tuple[2]), len (tuple[3])) + row = (self.displayClassName(classKey), len (tuple[0]), len (tuple[1]), + len (tuple[2]), len (tuple[3])) rows.append (row) self.disp.table ("Classes in Schema:", ("Class", "Properties", "Statistics", "Methods", "Events"), @@ -586,7 +594,7 @@ class ManagementData: rows.append ((name, typename, unit, "", "", desc)) titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") - self.disp.table ("Schema for class '%s.%s.%d':" % (classKey[0], classKey[1], schemaRev), titles, rows) + self.disp.table ("Schema for class '%s':" % self.displayClassName(classKey), titles, rows) for mname in self.schema[classKey][2]: (mdesc, args) = self.schema[classKey][2][mname] @@ -611,7 +619,7 @@ class ManagementData: titles = ("Argument", "Type", "Direction", "Unit", "Notes", "Description") self.disp.table (caption, titles, rows) - except: + except Exception,e: pass self.lock.release () @@ -631,7 +639,7 @@ class ManagementData: raise ValueError () if methodName not in self.schema[classKey][2]: - print "Method '%s' not valid for class '%s.%s'" % (methodName, classKey[0], classKey[1]) + print "Method '%s' not valid for class '%s'" % (methodName, self.displayClassName(classKey)) raise ValueError () schemaMethod = self.schema[classKey][2][methodName] @@ -652,7 +660,7 @@ class ManagementData: self.methodSeq = self.methodSeq + 1 self.methodsPending[self.methodSeq] = methodName - except: + except Exception, e: methodOk = False self.lock.release () if methodOk: -- cgit v1.2.1 From ee61ff860a439cb55084e9a6345fe715c9fc2c7c Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 24 Sep 2008 17:26:59 +0000 Subject: QPID-1291 - William Henry's python test (plus some test harness updates) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@698662 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 9 +++- qpid/python/tests_0-10/management.py | 99 +++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index b5aa59f586..b8f4c29ceb 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -22,7 +22,7 @@ # import sys, re, unittest, os, random, logging, traceback -import qpid.client, qpid.spec +import qpid.client, qpid.spec, qpid.qmfconsole import Queue from fnmatch import fnmatch from getopt import getopt, GetoptError @@ -357,6 +357,11 @@ class TestBase010(unittest.TestCase): username=testrunner.user, password=testrunner.password) self.conn.start(timeout=10) self.session = self.conn.session("test-session", timeout=10) + self.qmf = None + + def startQmf(self): + self.qmf = qpid.qmfconsole.Session() + self.qmf_broker = self.qmf.addBroker("%s:%d" % (testrunner.host, testrunner.port)) def connect(self, host=None, port=None): spec = testrunner.spec @@ -367,6 +372,8 @@ class TestBase010(unittest.TestCase): def tearDown(self): if not self.session.error(): self.session.close(timeout=10) self.conn.close(timeout=10) + if self.qmf: + self.qmf.delBroker(self.qmf_broker) def subscribe(self, session=None, **keys): session = session or self.session diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index f1360a1902..7e18967803 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -20,13 +20,14 @@ from qpid.datatypes import Message, RangedSet from qpid.testlib import TestBase010 from qpid.management import managementChannel, managementClient +from qpid import qmfconsole class ManagementTest (TestBase010): """ Tests for the management hooks """ - def test_broker_connectivity (self): + def test_broker_connectivity_oldAPI (self): """ Call the "echo" method on the broker to verify it is alive and talking. """ @@ -52,6 +53,25 @@ class ManagementTest (TestBase010): self.assertEqual (res.body, body) mc.removeChannel (mch) + def test_broker_connectivity (self): + """ + Call the "echo" method on the broker to verify it is alive and talking. + """ + session = self.session + self.startQmf() + + brokers = self.qmf.getObjects(name="broker") + self.assertEqual (len(brokers), 1) + broker = brokers[0] + + body = "Echo Message Body" + for seq in range (1, 10): + res = broker.echo(seq, body) + self.assertEqual (res.status, 0) + self.assertEqual (res.text, "OK") + self.assertEqual (res.sequence, seq) + self.assertEqual (res.body, body) + def test_system_object (self): session = self.session @@ -105,3 +125,80 @@ class ManagementTest (TestBase010): if exchange.name == name: return exchange return None + + def test_move_queued_messages(self): + """ + Test ability to move messages from the head of one queue to another. + Need to test moveing all and N messages. + """ + self.startQmf() + session = self.session + "Set up source queue" + session.queue_declare(queue="src-queue", exclusive=True, auto_delete=True) + session.exchange_bind(queue="src-queue", exchange="amq.direct", binding_key="routing_key") + move_part = "Move Messages " + twenty = range(1,21) + props = session.delivery_properties(routing_key="routing_key") + for count in twenty: + body = move_part + 'count' + src_msg = Message(props, body) + session.message_transfer(destination="amq.direct", message=src_msg) + + "Set up destination queue" + session.queue_declare(queue="dest-queue", exclusive=True, auto_delete=True) + session.exchange_bind(queue="dest-queue", exchange="amq.direct") + + "Get a management agent session interface" + #qmf_sess = qmfconsole.Session() + #broker = qmf_sess.addBroker("%s:%d" % (testrunner.host, testrunner.port)) + queues = self.qmf.getObjects(name="queue") + + "Move 10 messages from src-queue to dest-queue" + result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("src-queue", "dest-queue", 10) + self.assertEqual (result.status, 0) + + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "src-queue": + sq = q + elif q.name == "dest-queue": + dq = q + + self.assertEqual (sq.msgDepth,10) + self.assertEqual (dq.msgDepth,10) + + "Move all remaining messages to destination" + result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("src-queue", "dest-queue", 0) + self.assertEqual (result.status,0) + + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "src-queue": + sq = q + elif q.name == "dest-queue": + dq = q + self.assertEqual (sq.msgDepth,0) + self.assertEqual (dq.msgDepth,20) + + "Use a bad source queue name" + result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("bad-src-queue", "dest-queue", 0) + self.assertEqual (result.status,4) + + "Use a bad destination queue name" + result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("src-queue", "bad-dest-queue", 0) + self.assertEqual (result.status,4) + + " Use a large qty (40) to move from dest-queue back to " + " src-queue- should move all " + result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("dest-queue", "src-queue", 40) + self.assertEqual (result.status,0) + + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "src-queue": + sq = q + elif q.name == "dest-queue": + dq = q + self.assertEqual (sq.msgDepth,20) + self.assertEqual (dq.msgDepth,0) + -- cgit v1.2.1 From 35c289537158937a6e24bd2ed1cb0c5a0f3374ca Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 24 Sep 2008 17:33:24 +0000 Subject: Remove unneeded import statement git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@698665 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/management.py | 1 - 1 file changed, 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 7e18967803..3fca87a702 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -20,7 +20,6 @@ from qpid.datatypes import Message, RangedSet from qpid.testlib import TestBase010 from qpid.management import managementChannel, managementClient -from qpid import qmfconsole class ManagementTest (TestBase010): """ -- cgit v1.2.1 From f53069721640dcf6eace603f27fd1ed498058950 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 24 Sep 2008 18:03:01 +0000 Subject: Added event handling, did some code cleanup and fixed some small bugs git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@698678 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 3 +- qpid/python/commands/qpid-printevents | 67 ++++++ qpid/python/qpid/qmfconsole.py | 375 +++++++++++++++++++++++----------- 3 files changed, 327 insertions(+), 118 deletions(-) create mode 100755 qpid/python/commands/qpid-printevents (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 6bc38c7440..0998c58bce 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -268,7 +268,7 @@ nargs = len (cargs) bm = BrokerManager () try: - bm.SetBroker(qmfconsole.BrokerURL(_host)) + bm.SetBroker(_host) if nargs == 0: bm.Overview () else: @@ -307,6 +307,7 @@ try: else: Usage () except Exception,e: + raise print "Failed:", e.message sys.exit(1) diff --git a/qpid/python/commands/qpid-printevents b/qpid/python/commands/qpid-printevents new file mode 100755 index 0000000000..970607c797 --- /dev/null +++ b/qpid/python/commands/qpid-printevents @@ -0,0 +1,67 @@ +#!/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 os +import optparse +import sys +import socket +import time +from qpid.qmfconsole import Console, Session + +class EventConsole(Console): + def event(self, broker, event): + print event + + def heartbeat(self, agent, timestamp): + print "Heartbeat" + +## +## Main Program +## +def main(): + _usage = "%prog [options] [broker-addr]..." + _description = \ +"""Collect and print events from one of more Qpid message brokers. If no broker-addr is +supplied, %prog will connect to 'localhost:5672'. +broker-addr is of the form: [username/password@] hostname | ip-address [:] +ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost +""" + p = optparse.OptionParser(usage=_usage, description=_description) + + options, arguments = p.parse_args() + if len(arguments) == 0: + arguments.append("localhost") + + console = EventConsole() + session = Session(console, rcvObjects=False, rcvHeartbeats=False) + for host in arguments: + session.addBroker(host) + + try: + while (True): + time.sleep(10) + except KeyboardInterrupt: + print + sys.exit(0) + +if __name__ == '__main__': + main() + diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 435da475d7..4d06e4a725 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -26,18 +26,25 @@ import socket import re from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed -from qpid.datatypes import uuid4 +from qpid.datatypes import uuid4, Message, RangedSet from qpid.util import connect -from datatypes import Message, RangedSet +from qpid.codec010 import StringCodec as Codec from threading import Lock, Condition -from codec010 import StringCodec as Codec -from time import time +from time import time, strftime, gmtime from cStringIO import StringIO class Console: """ To access the asynchronous operations, a class must be derived from Console with overrides of any combination of the available methods. """ + def brokerConnected(self, broker): + """ Invoked when a connection is established to a broker """ + pass + + def brokerDisconnected(self, broker): + """ Invoked when the connection to a broker is lost """ + pass + def newPackage(self, name): """ Invoked when a QMF package is discovered. """ pass @@ -106,20 +113,38 @@ class Session: GET_WAIT_TIME = 10 - def __init__(self, console=None): + def __init__(self, console=None, rcvObjects=True, rcvEvents=True, rcvHeartbeats=True, + manageConnections=False): """ Initialize a session. If the console argument is provided, the more advanced asynchronous features are available. If console is defaulted, the session will operate in a simpler, synchronous manner. + + The rcvObjects, rcvEvents, and rcvHeartbeats arguments are meaningful only if 'console' + is provided. They control whether object updates, events, and agent-heartbeats are + subscribed to. If the console is not interested in receiving one or more of the above, + setting the argument to False will reduce tha bandwidth used by the API. + + If manageConnections is set to True, the Session object will manage connections to + the brokers. This means that if a broker is unreachable, it will retry until a connection + can be established. If a connection is lost, the Session will attempt to reconnect. + + If manageConnections is set to False, the user is responsible for handing failures. In + this case, an unreachable broker will cause addBroker to raise an exception. """ - self.console = console - self.brokers = [] - self.packages = {} - self.seqMgr = SequenceManager() - self.cv = Condition() - self.syncSequenceList = [] - self.getResult = [] - self.error = None + self.console = console + self.brokers = [] + self.packages = {} + self.seqMgr = SequenceManager() + self.cv = Condition() + self.syncSequenceList = [] + self.getResult = [] + self.error = None + self.bindingKeyList = self._bindingKeys(rcvObjects, rcvEvents, rcvHeartbeats) + self.manageConnections = manageConnections + + if (manageConnections): + raise Exception("manageConnections - not yet implemented") def __repr__(self): return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) @@ -128,6 +153,9 @@ class Session: """ Connect to a Qpid broker. Returns an object of type Broker. """ url = BrokerURL(target) broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass) + if not broker.isConnected and not self.manageConnections: + raise Exception(broker.error) + self.brokers.append(broker) self.getObjects(broker=broker, name="agent") return broker @@ -272,6 +300,26 @@ class Session: """ """ pass + def _bindingKeys(self, rcvObjects, rcvEvents, rcvHeartbeats): + keyList = [] + if rcvObjects and rcvEvents and rcvHeartbeats: + keyList.append("mgmt.#") + else: + if rcvObjects: + keyList.append("mgmt.*.prop.#") + keyList.append("mgmt.*.stat.#") + if rcvEvents: + keyList.append("mgmt.event") + if rcvHeartbeats: + keyList.append("mgmt.*.heartbeat.#") + return keyList + + def _handleBrokerConnect(self, broker): + pass + + def _handleBrokerDisconnect(self, broker): + pass + def _handleBrokerResp(self, broker, codec, seq): broker.brokerId = codec.read_uuid() if self.console != None: @@ -355,7 +403,7 @@ class Session: if code == 0: for arg in method.arguments: if arg.dir.find("O") != -1: - outArgs[arg.name] = obj._decodeValue(codec, arg.type) + outArgs[arg.name] = self._decodeValue(codec, arg.type) broker.cv.acquire() broker.syncResult = MethodResult(code, text, outArgs) broker.syncInFlight = False @@ -364,10 +412,13 @@ class Session: def _handleHeartbeatInd(self, broker, codec, seq): timestamp = codec.read_uint64() - pass + if self.console != None: + self.console.heartbeat(None, timestamp) def _handleEventInd(self, broker, codec, seq): - pass + if self.console != None: + event = Event(self, codec) + self.console.event(broker, event) def _handleSchemaResp(self, broker, codec, seq): pname = str(codec.read_str8()) @@ -402,7 +453,8 @@ class Session: self.cv.acquire() if seq in self.syncSequenceList: - self.getResult.append(object) + if object.getTimestamps()[2] == 0: + self.getResult.append(object) self.cv.release() return self.cv.release() @@ -420,6 +472,79 @@ class Session: self.cv.notify() self.cv.release() + def _decodeValue(self, codec, typecode): + """ Decode, from the codec, a value based on its typecode. """ + if typecode == 1: data = codec.read_uint8() # U8 + elif typecode == 2: data = codec.read_uint16() # U16 + elif typecode == 3: data = codec.read_uint32() # U32 + elif typecode == 4: data = codec.read_uint64() # U64 + elif typecode == 6: data = str(codec.read_str8()) # SSTR + elif typecode == 7: data = codec.read_vbin32() # LSTR + elif typecode == 8: data = codec.read_int64() # ABSTIME + elif typecode == 9: data = codec.read_uint64() # DELTATIME + elif typecode == 10: data = ObjectId(codec) # REF + elif typecode == 11: data = codec.read_uint8() # BOOL + elif typecode == 12: data = codec.read_float() # FLOAT + elif typecode == 13: data = codec.read_double() # DOUBLE + elif typecode == 14: data = codec.read_uuid() # UUID + elif typecode == 15: data = codec.read_map() # FTABLE + elif typecode == 16: data = codec.read_int8() # S8 + elif typecode == 17: data = codec.read_int16() # S16 + elif typecode == 18: data = codec.read_int32() # S32 + elif typecode == 19: data = codec.read_int64() # S63 + else: + raise ValueError("Invalid type code: %d" % typecode) + return data + + def _encodeValue(self, codec, value, typecode): + """ Encode, into the codec, a value based on its typecode. """ + if typecode == 1: codec.write_uint8 (int(value)) # U8 + elif typecode == 2: codec.write_uint16 (int(value)) # U16 + elif typecode == 3: codec.write_uint32 (long(value)) # U32 + elif typecode == 4: codec.write_uint64 (long(value)) # U64 + elif typecode == 6: codec.write_str8 (value) # SSTR + elif typecode == 7: codec.write_vbin32 (value) # LSTR + elif typecode == 8: codec.write_int64 (long(value)) # ABSTIME + elif typecode == 9: codec.write_uint64 (long(value)) # DELTATIME + elif typecode == 10: value.encode (codec) # REF + elif typecode == 11: codec.write_uint8 (int(value)) # BOOL + elif typecode == 12: codec.write_float (float(value)) # FLOAT + elif typecode == 13: codec.write_double (double(value)) # DOUBLE + elif typecode == 14: codec.write_uuid (value) # UUID + elif typecode == 15: codec.write_map (value) # FTABLE + elif typecode == 16: codec.write_int8 (int(value)) # S8 + elif typecode == 17: codec.write_int16 (int(value)) # S16 + elif typecode == 18: codec.write_int32 (int(value)) # S32 + elif typecode == 19: codec.write_int64 (int(value)) # S64 + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def _displayValue(self, value, typecode): + """ """ + if typecode == 1: return str(value) + elif typecode == 2: return str(value) + elif typecode == 3: return str(value) + elif typecode == 4: return str(value) + elif typecode == 6: return str(value) + elif typecode == 7: return str(value) + elif typecode == 8: return strftime("%c", gmtime(value / 1000000000)) + elif typecode == 9: return str(value) + elif typecode == 10: return value.__repr__() + elif typecode == 11: + if value: return 'T' + else: return 'F' + elif typecode == 12: return str(value) + elif typecode == 13: return str(value) + elif typecode == 14: return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack("!LHHHHL", value) + elif typecode == 15: return value.__repr__() + elif typecode == 16: return str(value) + elif typecode == 17: return str(value) + elif typecode == 18: return str(value) + elif typecode == 19: return str(value) + else: + raise ValueError ("Invalid type code: %d" % typecode) + + class Package: """ """ def __init__(self, name): @@ -672,10 +797,10 @@ class Object(object): if property.name in notPresent: self.properties.append((property, None)) else: - self.properties.append((property, self._decodeValue(codec, property.type))) + self.properties.append((property, self.session._decodeValue(codec, property.type))) if stat: for statistic in schema.getStatistics(): - self.statistics.append((statistic, self._decodeValue(codec, statistic.type))) + self.statistics.append((statistic, self.session._decodeValue(codec, statistic.type))) def getObjectId(self): """ Return the object identifier for this object """ @@ -735,9 +860,17 @@ class Object(object): sendCodec.write_str8(cname) sendCodec.write_bin128(hash) sendCodec.write_str8(name) + + count = 0 + for arg in method.arguments: + if arg.dir.find("I") != -1: + count += 1 + if count != len(args): + raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(args))) + for arg in method.arguments: if arg.dir.find("I") != -1: - self._encodeValue(sendCodec, args[aIdx], arg.type) + self.session._encodeValue(sendCodec, args[aIdx], arg.type) aIdx += 1 smsg = self.broker._message(sendCodec.encoded, "agent." + str(self.objectId.getBank())) self.broker.cv.acquire() @@ -777,53 +910,6 @@ class Object(object): bit = 0 return excludeList - def _decodeValue(self, codec, typecode): - """ Decode, from the codec, a value based on its typecode. """ - if typecode == 1: data = codec.read_uint8() # U8 - elif typecode == 2: data = codec.read_uint16() # U16 - elif typecode == 3: data = codec.read_uint32() # U32 - elif typecode == 4: data = codec.read_uint64() # U64 - elif typecode == 6: data = str(codec.read_str8()) # SSTR - elif typecode == 7: data = codec.read_vbin32() # LSTR - elif typecode == 8: data = codec.read_int64() # ABSTIME - elif typecode == 9: data = codec.read_uint64() # DELTATIME - elif typecode == 10: data = ObjectId(codec) # REF - elif typecode == 11: data = codec.read_uint8() # BOOL - elif typecode == 12: data = codec.read_float() # FLOAT - elif typecode == 13: data = codec.read_double() # DOUBLE - elif typecode == 14: data = codec.read_uuid() # UUID - elif typecode == 15: data = codec.read_map() # FTABLE - elif typecode == 16: data = codec.read_int8() # S8 - elif typecode == 17: data = codec.read_int16() # S16 - elif typecode == 18: data = codec.read_int32() # S32 - elif typecode == 19: data = codec.read_int64() # S63 - else: - raise ValueError("Invalid type code: %d" % typecode) - return data - - def _encodeValue(self, codec, value, typecode): - """ Encode, into the codec, a value based on its typecode. """ - if typecode == 1: codec.write_uint8 (int(value)) # U8 - elif typecode == 2: codec.write_uint16 (int(value)) # U16 - elif typecode == 3: codec.write_uint32 (long(value)) # U32 - elif typecode == 4: codec.write_uint64 (long(value)) # U64 - elif typecode == 6: codec.write_str8 (value) # SSTR - elif typecode == 7: codec.write_vbin32 (value) # LSTR - elif typecode == 8: codec.write_int64 (long(value)) # ABSTIME - elif typecode == 9: codec.write_uint64 (long(value)) # DELTATIME - elif typecode == 10: value.encode (codec) # REF - elif typecode == 11: codec.write_uint8 (int(value)) # BOOL - elif typecode == 12: codec.write_float (float(value)) # FLOAT - elif typecode == 13: codec.write_double (double(value)) # DOUBLE - elif typecode == 14: codec.write_uuid (value) # UUID - elif typecode == 15: codec.write_map (value) # FTABLE - elif typecode == 16: codec.write_int8 (int(value)) # S8 - elif typecode == 17: codec.write_int16 (int(value)) # S16 - elif typecode == 18: codec.write_int32 (int(value)) # S32 - elif typecode == 19: codec.write_int64 (int(value)) # S64 - else: - raise ValueError ("Invalid type code: %d" % typecode) - class MethodResult(object): """ """ def __init__(self, status, text, outArgs): @@ -844,10 +930,12 @@ class Broker: SYNC_TIME = 10 def __init__(self, session, host, port, authMech, authUser, authPass): - self.session = session - self.host = host - self.port = port - self.agents = {} + self.session = session + self.host = host + self.port = port + self.authUser = authUser + self.authPass = authPass + self.agents = {} self.agents[0] = Agent(self, 0, "BrokerAgent") self.topicBound = False self.cv = Condition() @@ -857,17 +945,52 @@ class Broker: self.reqsOutstanding = 1 self.error = None self.brokerId = None - err = None + self.isConnected = False + self._tryToConnect() + + def isConnected(self): + return self.isConnected + + def getError(self): + return self.error + + def getBrokerId(self): + """ Get broker's unique identifier (UUID) """ + return self.brokerId + + def getSessionId(self): + """ Get the identifier of the AMQP session to the broker """ + return self.amqpSessionId + + def getAgents(self): + """ Get the list of agents reachable via this broker """ + return self.agents.values() + + def getAmqpSession(self): + """ Get the AMQP session object for this connected broker. """ + return self.amqpSession + + def __repr__(self): + if self.isConnected: + if self.port == 5672: + port = "" + else: + port = ":%d" % self.port + return "Broker connected at: amqp://%s%s" % (self.host, port) + else: + return "Disconnected Broker" + + def _tryToConnect(self): try: self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection(connect(host, port), username=authUser, password=authPass) + self.conn = Connection(connect(self.host, self.port), username=self.authUser, password=self.authPass) self.conn.start() self.replyName = "reply-%s" % self.amqpSessionId self.amqpSession = self.conn.session(self.amqpSessionId) self.amqpSession.auto_sync = True self.amqpSession.queue_declare(queue=self.replyName, exclusive=True, auto_delete=True) self.amqpSession.exchange_bind(exchange="amq.direct", - queue=self.replyName, binding_key=self.replyName) + queue=self.replyName, binding_key=self.replyName) self.amqpSession.message_subscribe(queue=self.replyName, destination="rdest") self.amqpSession.incoming("rdest").listen(self._replyCb, self._exceptionCb) self.amqpSession.message_set_flow_mode(destination="rdest", flow_mode=1) @@ -883,50 +1006,20 @@ class Broker: self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) + self.isConnected = True + self.session._handleBrokerConnect(self) + codec = Codec(self.conn.spec) self._setHeader(codec, 'B') msg = self._message(codec.encoded) self._send(msg) except socket.error, e: - err = "Socket Error %s - %s" % (e[0], e[1]) + self.error = "Socket Error %s - %s" % (e[0], e[1]) except Closed, e: - err = "Connect Failed %d - %s" % (e[0], e[1]) + self.error = "Connect Failed %d - %s" % (e[0], e[1]) except ConnectionFailed, e: - err = "Connect Failed %d - %s" % (e[0], e[1]) - - self.active = True - if err != None: - raise Exception(err) - - def getBrokerId(self): - """ Get broker's unique identifier (UUID) """ - return self.brokerId - - def getSessionId(self): - """ Get the identifier of the AMQP session to the broker """ - return self.amqpSessionId - - def getAgents(self): - """ Get the list of agents reachable via this broker """ - return self.agents.values() - - def getAmqpSession(self): - """ Get the AMQP session object for this connected broker. """ - return self.amqpSession - - def isConnected(self): - return self.active - - def __repr__(self): - if self.active: - if self.port == 5672: - port = "" - else: - port = ":%d" % self.port - return "Broker connected at: amqp://%s%s" % (self.host, port) - else: - return "Disconnected Broker" + self.error = "Connect Failed %d - %s" % (e[0], e[1]) def _updateAgent(self, obj): if obj.deleteTime == 0: @@ -975,12 +1068,12 @@ class Broker: self.amqpSession.message_transfer(destination=dest, message=msg) def _shutdown(self): - if self.active: + if self.isConnected: self.amqpSession.incoming("rdest").stop() if self.session.console != None: self.amqpSession.incoming("tdest").stop() self.amqpSession.close() - self.active = False + self.isConnected = False else: raise Exception("Broker already disconnected") @@ -1008,18 +1101,21 @@ class Broker: self.reqsOutstanding -= 1 if self.reqsOutstanding == 0 and not self.topicBound and self.session.console != None: self.topicBound = True - self.amqpSession.exchange_bind(exchange="qpid.management", - queue=self.topicName, binding_key="mgmt.#") + for key in self.session.bindingKeyList: + self.amqpSession.exchange_bind(exchange="qpid.management", + queue=self.topicName, binding_key=key) if self.reqsOutstanding == 0 and self.syncInFlight: self.syncInFlight = False self.cv.notify() self.cv.release() def _replyCb(self, msg): + self.amqpSession.message_accept(RangedSet(msg.id)) codec = Codec(self.conn.spec, msg.body) opcode, seq = self._checkHeader(codec) if opcode == None: return + if opcode == 'b': self.session._handleBrokerResp (self, codec, seq) elif opcode == 'p': self.session._handlePackageInd (self, codec, seq) elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) @@ -1033,13 +1129,14 @@ class Broker: elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) def _exceptionCb(self, data): - self.active = False + self.isConnected = False self.error = data self.cv.acquire() if self.syncInFlight: self.cv.notify() self.cv.release() self.session._handleError(self.error) + self.session._handleBrokerDisconnect(self) class Agent: """ """ @@ -1053,8 +1150,46 @@ class Agent: class Event: """ """ - def __init__(self): - pass + def __init__(self, session, codec): + self.session = session + self.timestamp = codec.read_int64() + self.objectId = ObjectId(codec) + pname = codec.read_str8() + cname = codec.read_str8() + hash = codec.read_bin128() + self.classKey = (pname, cname, hash) + self.name = codec.read_str8() + if pname in session.packages: + if (cname, hash) in session.packages[pname]: + schema = session.packages[pname][(cname, hash)] + for event in schema.getEvents(): + if event.name == self.name: + self.schemaEvent = event + self.arguments = {} + for arg in event.arguments: + self.arguments[arg.name] = session._decodeValue(codec, arg.type) + + def __repr__(self): + return self.getSyslogText() + + def getClassKey(self): + return self.classKey + + def getArguments(self): + return self.arguments + + def getTimestamp(self): + return self.timerstamp + + def getName(self): + return self.name + + def getSyslogText(self): + out = strftime("%c", gmtime(self.timestamp / 1000000000)) + out += " " + self.classKey[0] + ":" + self.classKey[1] + " " + self.name + for arg in self.schemaEvent.arguments: + out += " " + arg.name + "=" + self.session._displayValue(self.arguments[arg.name], arg.type) + return out class SequenceManager: """ Manage sequence numbers for asynchronous method calls """ @@ -1085,6 +1220,12 @@ class SequenceManager: class DebugConsole(Console): """ """ + def brokerConnected(self, broker): + print "brokerConnected:", broker + + def brokerDisconnected(self, broker): + print "brokerDisconnected:", broker + def newPackage(self, name): print "newPackage:", name -- cgit v1.2.1 From 2ab9c04722b1eff7ca83dd4994524b62e8641832 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 25 Sep 2008 16:22:27 +0000 Subject: QPID-1292 - Patch from William Henry git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@699013 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/management.py | 67 +++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 3fca87a702..0b83045329 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -135,11 +135,11 @@ class ManagementTest (TestBase010): "Set up source queue" session.queue_declare(queue="src-queue", exclusive=True, auto_delete=True) session.exchange_bind(queue="src-queue", exchange="amq.direct", binding_key="routing_key") - move_part = "Move Messages " + twenty = range(1,21) props = session.delivery_properties(routing_key="routing_key") for count in twenty: - body = move_part + 'count' + body = "Move Message %d" % count src_msg = Message(props, body) session.message_transfer(destination="amq.direct", message=src_msg) @@ -147,9 +147,6 @@ class ManagementTest (TestBase010): session.queue_declare(queue="dest-queue", exclusive=True, auto_delete=True) session.exchange_bind(queue="dest-queue", exchange="amq.direct") - "Get a management agent session interface" - #qmf_sess = qmfconsole.Session() - #broker = qmf_sess.addBroker("%s:%d" % (testrunner.host, testrunner.port)) queues = self.qmf.getObjects(name="queue") "Move 10 messages from src-queue to dest-queue" @@ -201,3 +198,63 @@ class ManagementTest (TestBase010): self.assertEqual (sq.msgDepth,20) self.assertEqual (dq.msgDepth,0) + "Consume the messages of the queue and check they are all there in order" + session.message_subscribe(queue="src-queue", destination="tag") + session.message_flow(destination="tag", unit=session.credit_unit.message, value=0xFFFFFFFF) + session.message_flow(destination="tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) + queue = session.incoming("tag") + for count in twenty: + consumed_msg = queue.get(timeout=1) + body = "Move Message %d" % count + self.assertEqual(body, consumed_msg.body) + + def test_purge_queue(self): + """ + Test ability to purge messages from the head of a queue. + Need to test moveing all, 1 (top message) and N messages. + """ + self.startQmf() + session = self.session + "Set up purge queue" + session.queue_declare(queue="purge-queue", exclusive=True, auto_delete=True) + session.exchange_bind(queue="purge-queue", exchange="amq.direct", binding_key="routing_key") + + twenty = range(1,21) + props = session.delivery_properties(routing_key="routing_key") + for count in twenty: + body = "Purge Message %d" % count + msg = Message(props, body) + session.message_transfer(destination="amq.direct", message=msg) + + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "purge-queue": + pq = q + + "Purge top message from purge-queue" + result = pq.purge(1) + self.assertEqual (result.status, 0) + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "purge-queue": + pq = q + self.assertEqual (pq.msgDepth,19) + + "Purge top 9 messages from purge-queue" + result = pq.purge(9) + self.assertEqual (result.status, 0) + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "purge-queue": + pq = q + self.assertEqual (pq.msgDepth,10) + + "Purge all messages from purge-queue" + result = pq.purge(0) + self.assertEqual (result.status, 0) + queues = self.qmf.getObjects(name="queue") + for q in queues: + if q.name == "purge-queue": + pq = q + self.assertEqual (pq.msgDepth,0) + -- cgit v1.2.1 From 1c8f075b4f4ff2ef2d2fe23bca97f3fa9db4e10c Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 26 Sep 2008 01:34:52 +0000 Subject: Added property-selector to the console getObjects function git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@699148 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 22 ++++++------- qpid/python/commands/qpid-route | 24 +++++++-------- qpid/python/qpid/qmfconsole.py | 28 ++++++++++++++--- qpid/python/tests_0-10/management.py | 60 ++++++++++++------------------------ 4 files changed, 65 insertions(+), 69 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 0998c58bce..925b54b682 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -82,8 +82,8 @@ class BrokerManager: self.qmf.delBroker(self.broker) def Overview (self): - exchanges = self.qmf.getObjects(name="exchange") - queues = self.qmf.getObjects(name="queue") + exchanges = self.qmf.getObjects(cls="exchange") + queues = self.qmf.getObjects(cls="queue") print "Total Exchanges: %d" % len (exchanges) etype = {} for ex in exchanges: @@ -104,7 +104,7 @@ class BrokerManager: print " non-durable: %d" % (len (queues) - _durable) def ExchangeList (self, filter): - exchanges = self.qmf.getObjects(name="exchange") + exchanges = self.qmf.getObjects(cls="exchange") print "Durable Type Bindings Exchange Name" print "=======================================================" for ex in exchanges: @@ -112,9 +112,9 @@ class BrokerManager: print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindingCount, ex.name) def ExchangeListRecurse (self, filter): - exchanges = self.qmf.getObjects(name="exchange") - bindings = self.qmf.getObjects(name="binding") - queues = self.qmf.getObjects(name="queue") + exchanges = self.qmf.getObjects(cls="exchange") + bindings = self.qmf.getObjects(cls="binding") + queues = self.qmf.getObjects(cls="queue") for ex in exchanges: if self.match (ex.name, filter): print "Exchange '%s' (%s)" % (ex.name, ex.type) @@ -128,8 +128,8 @@ class BrokerManager: def QueueList (self, filter): - queues = self.qmf.getObjects(name="queue") - journals = self.qmf.getObjects(name="journal") + queues = self.qmf.getObjects(cls="queue") + journals = self.qmf.getObjects(cls="journal") print " Store Size" print "Durable AutoDel Excl Bindings (files x file pages) Queue Name" print "===========================================================================================" @@ -149,9 +149,9 @@ class BrokerManager: YN (q.exclusive), q.bindingCount, q.name) def QueueListRecurse (self, filter): - exchanges = self.qmf.getObjects(name="exchange") - bindings = self.qmf.getObjects(name="binding") - queues = self.qmf.getObjects(name="queue") + exchanges = self.qmf.getObjects(cls="exchange") + bindings = self.qmf.getObjects(cls="binding") + queues = self.qmf.getObjects(cls="queue") for queue in queues: if self.match (queue.name, filter): print "Queue '%s'" % queue.name diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 8a9522e53a..f9f938cdec 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -62,7 +62,7 @@ class RouteManager: self.qmf.delBroker(self.broker) def getLink (self): - links = self.qmf.getObjects(name="link") + links = self.qmf.getObjects(cls="link") for link in links: if "%s:%d" % (link.host, link.port) == self.src.name (): return link @@ -74,7 +74,7 @@ class RouteManager: print "Linking broker to itself is not permitted" sys.exit(1) - brokers = self.qmf.getObjects(name="broker") + brokers = self.qmf.getObjects(cls="broker") broker = brokers[0] link = self.getLink() if link != None: @@ -92,7 +92,7 @@ class RouteManager: def DelLink (self, srcBroker): self.src = qmfconsole.BrokerURL(srcBroker) - brokers = self.qmf.getObjects(name="broker") + brokers = self.qmf.getObjects(cls="broker") broker = brokers[0] link = self.getLink() if link == None: @@ -103,7 +103,7 @@ class RouteManager: print "Close method returned:", res.status, res.text def ListLinks (self): - links = self.qmf.getObjects(name="link") + links = self.qmf.getObjects(cls="link") if len(links) == 0: print "No Links Found" else: @@ -119,7 +119,7 @@ class RouteManager: if self.dest.name() == self.src.name(): raise Exception("Linking broker to itself is not permitted") - brokers = self.qmf.getObjects(name="broker") + brokers = self.qmf.getObjects(cls="broker") broker = brokers[0] link = self.getLink() @@ -140,7 +140,7 @@ class RouteManager: if link == None: raise Exception("Protocol Error - Missing link ID") - bridges = self.qmf.getObjects(name="bridge") + bridges = self.qmf.getObjects(cls="bridge") for bridge in bridges: if bridge.linkRef == link.getObjectId() and \ bridge.dest == exchange and bridge.key == routingKey: @@ -164,7 +164,7 @@ class RouteManager: raise Exception("No link found from %s to %s" % (self.src.name(), self.dest.name())) sys.exit (0) - bridges = self.qmf.getObjects(name="bridge") + bridges = self.qmf.getObjects(cls="bridge") for bridge in bridges: if bridge.linkRef == link.getObjectId() and bridge.dest == exchange and bridge.key == routingKey: if _verbose: @@ -186,8 +186,8 @@ class RouteManager: raise Exception("Route not found") def ListRoutes (self): - links = self.qmf.getObjects(name="link") - bridges = self.qmf.getObjects(name="bridge") + links = self.qmf.getObjects(cls="link") + bridges = self.qmf.getObjects(cls="bridge") for bridge in bridges: myLink = None @@ -199,8 +199,8 @@ class RouteManager: print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, bridge.key) def ClearAllRoutes (self): - links = self.qmf.getObjects(name="link") - bridges = self.qmf.getObjects(name="bridge") + links = self.qmf.getObjects(cls="link") + bridges = self.qmf.getObjects(cls="bridge") for bridge in bridges: if _verbose: @@ -218,7 +218,7 @@ class RouteManager: print "Ok" if _dellink: - links = self.qmf.getObjects(name="link") + links = self.qmf.getObjects(cls="link") for link in links: if _verbose: print "Deleting Link: %s:%d... " % (link.host, link.port), diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 4d06e4a725..3800e54b5b 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -139,6 +139,7 @@ class Session: self.cv = Condition() self.syncSequenceList = [] self.getResult = [] + self.getSelect = [] self.error = None self.bindingKeyList = self._bindingKeys(rcvObjects, rcvEvents, rcvHeartbeats) self.manageConnections = manageConnections @@ -157,7 +158,7 @@ class Session: raise Exception(broker.error) self.brokers.append(broker) - self.getObjects(broker=broker, name="agent") + self.getObjects(broker=broker, cls="agent") return broker def delBroker(self, broker): @@ -220,7 +221,7 @@ class Session: schema = - supply a schema object returned from getSchema key = - supply a classKey from the list returned by getClasses - name = - supply a class name as a string + cls = - supply a class name as a string If objects should be obtained from only one agent, use the following argument. Otherwise, the query will go to all agents. @@ -231,6 +232,10 @@ class Session: add the following argument: broker = - supply a broker as returned by addBroker + + If additional arguments are supplied, they are used as property selectors. For example, + if the argument name="test" is supplied, only objects whose "name" property is "test" + will be returned in the result. """ if "broker" in kwargs: brokerList = [] @@ -254,14 +259,19 @@ class Session: cname = None if "schema" in kwargs: pname, cname, hash = kwargs["schema"].getKey() elif "key" in kwargs: pname, cname, hash = kwargs["key"] - elif "name" in kwargs: pname, cname, hash = None, kwargs["name"], None + elif "cls" in kwargs: pname, cname, hash = None, kwargs["cls"], None if cname == None: - raise Exception("No class supplied, use 'schema', 'key', or 'name' argument") + raise Exception("No class supplied, use 'schema', 'key', or 'cls' argument") map = {} map["_class"] = cname if pname != None: map["_package"] = pname if hash != None: map["_hash"] = hash + self.getSelect = [] + for item in kwargs: + if item != "schema" and item != "key" and item != "cls": + self.getSelect.append((item, kwargs[item])) + self.getResult = [] for agent in agentList: broker = agent.broker @@ -453,7 +463,7 @@ class Session: self.cv.acquire() if seq in self.syncSequenceList: - if object.getTimestamps()[2] == 0: + if object.getTimestamps()[2] == 0 and self._selectMatch(object): self.getResult.append(object) self.cv.release() return @@ -471,6 +481,14 @@ class Session: self.syncSequenceList = [] self.cv.notify() self.cv.release() + + def _selectMatch(self, object): + """ Check the object against self.getSelect to check for a match """ + for key, value in self.getSelect: + for prop, propval in object.properties: + if key == prop.name and value != propval: + return False + return True def _decodeValue(self, codec, typecode): """ Decode, from the codec, a value based on its typecode. """ diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 0b83045329..eea1b29404 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -59,7 +59,7 @@ class ManagementTest (TestBase010): session = self.session self.startQmf() - brokers = self.qmf.getObjects(name="broker") + brokers = self.qmf.getObjects(cls="broker") self.assertEqual (len(brokers), 1) broker = brokers[0] @@ -147,54 +147,44 @@ class ManagementTest (TestBase010): session.queue_declare(queue="dest-queue", exclusive=True, auto_delete=True) session.exchange_bind(queue="dest-queue", exchange="amq.direct") - queues = self.qmf.getObjects(name="queue") + queues = self.qmf.getObjects(cls="queue") "Move 10 messages from src-queue to dest-queue" - result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("src-queue", "dest-queue", 10) + result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("src-queue", "dest-queue", 10) self.assertEqual (result.status, 0) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "src-queue": - sq = q - elif q.name == "dest-queue": - dq = q + sq = self.qmf.getObjects(cls="queue", name="src-queue")[0] + dq = self.qmf.getObjects(cls="queue", name="dest-queue")[0] self.assertEqual (sq.msgDepth,10) self.assertEqual (dq.msgDepth,10) "Move all remaining messages to destination" - result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("src-queue", "dest-queue", 0) + result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("src-queue", "dest-queue", 0) self.assertEqual (result.status,0) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "src-queue": - sq = q - elif q.name == "dest-queue": - dq = q + sq = self.qmf.getObjects(cls="queue", name="src-queue")[0] + dq = self.qmf.getObjects(cls="queue", name="dest-queue")[0] + self.assertEqual (sq.msgDepth,0) self.assertEqual (dq.msgDepth,20) "Use a bad source queue name" - result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("bad-src-queue", "dest-queue", 0) + result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("bad-src-queue", "dest-queue", 0) self.assertEqual (result.status,4) "Use a bad destination queue name" - result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("src-queue", "bad-dest-queue", 0) + result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("src-queue", "bad-dest-queue", 0) self.assertEqual (result.status,4) " Use a large qty (40) to move from dest-queue back to " " src-queue- should move all " - result = self.qmf.getObjects(name="broker")[0].queueMoveMessages("dest-queue", "src-queue", 40) + result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("dest-queue", "src-queue", 40) self.assertEqual (result.status,0) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "src-queue": - sq = q - elif q.name == "dest-queue": - dq = q + sq = self.qmf.getObjects(cls="queue", name="src-queue")[0] + dq = self.qmf.getObjects(cls="queue", name="dest-queue")[0] + self.assertEqual (sq.msgDepth,20) self.assertEqual (dq.msgDepth,0) @@ -226,35 +216,23 @@ class ManagementTest (TestBase010): msg = Message(props, body) session.message_transfer(destination="amq.direct", message=msg) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "purge-queue": - pq = q + pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] "Purge top message from purge-queue" result = pq.purge(1) self.assertEqual (result.status, 0) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "purge-queue": - pq = q + pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,19) "Purge top 9 messages from purge-queue" result = pq.purge(9) self.assertEqual (result.status, 0) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "purge-queue": - pq = q + pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,10) "Purge all messages from purge-queue" result = pq.purge(0) self.assertEqual (result.status, 0) - queues = self.qmf.getObjects(name="queue") - for q in queues: - if q.name == "purge-queue": - pq = q + pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,0) -- cgit v1.2.1 From 45e2225e7dd7d7e2ebdaaee0f7a74e794a0603ba Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 30 Sep 2008 18:47:26 +0000 Subject: Added extra queue option git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@700546 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 925b54b682..13b489abae 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -31,12 +31,13 @@ _fileCount = 8 _fileSize = 24 _maxQueueSize = None _maxQueueCount= None - +_policyType = None FILECOUNT = "qpid.file_count" FILESIZE = "qpid.file_size" MAX_QUEUE_SIZE = "qpid.max_size" MAX_QUEUE_COUNT = "qpid.max_count" +POLICY_TYPE = "qpid.policy_type" def Usage (): print "Usage: qpid-config [OPTIONS]" @@ -61,6 +62,7 @@ def Usage (): print " --file-size N (24) File size in pages (64Kib/page)" print " --max-queue-size N Maximum in-memory queue size as bytes" print " --max-queue-count N Maximum in-memory queue size as a number of messages" + print " --policy-type TYPE Action taken when queue limit is reached (reject, flow_to_disk, ring, ring_strict)" print print "Add Exchange Options:" print " --durable Exchange is durable" @@ -191,6 +193,8 @@ class BrokerManager: declArgs[MAX_QUEUE_SIZE] = _maxQueueSize if _maxQueueCount: declArgs[MAX_QUEUE_COUNT] = _maxQueueCount + if _policyType: + declArgs[POLICY_TYPE] = _policyType self.broker.getAmqpSession().queue_declare (queue=qname, durable=_durable, arguments=declArgs) @@ -243,7 +247,7 @@ def YN (bool): ## try: - longOpts = ("durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=") + longOpts = ("durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=") (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -263,6 +267,8 @@ for opt in optlist: _maxQueueSize = int (opt[1]) if opt[0] == "--max-queue-count": _maxQueueCount = int (opt[1]) + if opt[0] == "--policy-type": + _policyType = opt[1] nargs = len (cargs) bm = BrokerManager () -- cgit v1.2.1 From b56f731ad9d5d4b4b21f953dbd1103662b3d0b06 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 7 Oct 2008 21:47:35 +0000 Subject: QPID-1327 - Event support for Management git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@702651 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 22 +-- qpid/python/commands/qpid-printevents | 5 +- qpid/python/commands/qpid-route | 24 +-- qpid/python/qpid/management.py | 52 ++----- qpid/python/qpid/managementdata.py | 4 +- qpid/python/qpid/qmfconsole.py | 274 +++++++++++++++++----------------- qpid/python/tests_0-10/management.py | 34 ++--- 7 files changed, 194 insertions(+), 221 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 13b489abae..8b011778d6 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -84,8 +84,8 @@ class BrokerManager: self.qmf.delBroker(self.broker) def Overview (self): - exchanges = self.qmf.getObjects(cls="exchange") - queues = self.qmf.getObjects(cls="queue") + exchanges = self.qmf.getObjects(_class="exchange") + queues = self.qmf.getObjects(_class="queue") print "Total Exchanges: %d" % len (exchanges) etype = {} for ex in exchanges: @@ -106,7 +106,7 @@ class BrokerManager: print " non-durable: %d" % (len (queues) - _durable) def ExchangeList (self, filter): - exchanges = self.qmf.getObjects(cls="exchange") + exchanges = self.qmf.getObjects(_class="exchange") print "Durable Type Bindings Exchange Name" print "=======================================================" for ex in exchanges: @@ -114,9 +114,9 @@ class BrokerManager: print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindingCount, ex.name) def ExchangeListRecurse (self, filter): - exchanges = self.qmf.getObjects(cls="exchange") - bindings = self.qmf.getObjects(cls="binding") - queues = self.qmf.getObjects(cls="queue") + exchanges = self.qmf.getObjects(_class="exchange") + bindings = self.qmf.getObjects(_class="binding") + queues = self.qmf.getObjects(_class="queue") for ex in exchanges: if self.match (ex.name, filter): print "Exchange '%s' (%s)" % (ex.name, ex.type) @@ -130,8 +130,8 @@ class BrokerManager: def QueueList (self, filter): - queues = self.qmf.getObjects(cls="queue") - journals = self.qmf.getObjects(cls="journal") + queues = self.qmf.getObjects(_class="queue") + journals = self.qmf.getObjects(_class="journal") print " Store Size" print "Durable AutoDel Excl Bindings (files x file pages) Queue Name" print "===========================================================================================" @@ -151,9 +151,9 @@ class BrokerManager: YN (q.exclusive), q.bindingCount, q.name) def QueueListRecurse (self, filter): - exchanges = self.qmf.getObjects(cls="exchange") - bindings = self.qmf.getObjects(cls="binding") - queues = self.qmf.getObjects(cls="queue") + exchanges = self.qmf.getObjects(_class="exchange") + bindings = self.qmf.getObjects(_class="binding") + queues = self.qmf.getObjects(_class="queue") for queue in queues: if self.match (queue.name, filter): print "Queue '%s'" % queue.name diff --git a/qpid/python/commands/qpid-printevents b/qpid/python/commands/qpid-printevents index 970607c797..6efd472221 100755 --- a/qpid/python/commands/qpid-printevents +++ b/qpid/python/commands/qpid-printevents @@ -30,16 +30,13 @@ class EventConsole(Console): def event(self, broker, event): print event - def heartbeat(self, agent, timestamp): - print "Heartbeat" - ## ## Main Program ## def main(): _usage = "%prog [options] [broker-addr]..." _description = \ -"""Collect and print events from one of more Qpid message brokers. If no broker-addr is +"""Collect and print events from one or more Qpid message brokers. If no broker-addr is supplied, %prog will connect to 'localhost:5672'. broker-addr is of the form: [username/password@] hostname | ip-address [:] ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index f9f938cdec..4dadcd543b 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -62,7 +62,7 @@ class RouteManager: self.qmf.delBroker(self.broker) def getLink (self): - links = self.qmf.getObjects(cls="link") + links = self.qmf.getObjects(_class="link") for link in links: if "%s:%d" % (link.host, link.port) == self.src.name (): return link @@ -74,7 +74,7 @@ class RouteManager: print "Linking broker to itself is not permitted" sys.exit(1) - brokers = self.qmf.getObjects(cls="broker") + brokers = self.qmf.getObjects(_class="broker") broker = brokers[0] link = self.getLink() if link != None: @@ -92,7 +92,7 @@ class RouteManager: def DelLink (self, srcBroker): self.src = qmfconsole.BrokerURL(srcBroker) - brokers = self.qmf.getObjects(cls="broker") + brokers = self.qmf.getObjects(_class="broker") broker = brokers[0] link = self.getLink() if link == None: @@ -103,7 +103,7 @@ class RouteManager: print "Close method returned:", res.status, res.text def ListLinks (self): - links = self.qmf.getObjects(cls="link") + links = self.qmf.getObjects(_class="link") if len(links) == 0: print "No Links Found" else: @@ -119,7 +119,7 @@ class RouteManager: if self.dest.name() == self.src.name(): raise Exception("Linking broker to itself is not permitted") - brokers = self.qmf.getObjects(cls="broker") + brokers = self.qmf.getObjects(_class="broker") broker = brokers[0] link = self.getLink() @@ -140,7 +140,7 @@ class RouteManager: if link == None: raise Exception("Protocol Error - Missing link ID") - bridges = self.qmf.getObjects(cls="bridge") + bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: if bridge.linkRef == link.getObjectId() and \ bridge.dest == exchange and bridge.key == routingKey: @@ -164,7 +164,7 @@ class RouteManager: raise Exception("No link found from %s to %s" % (self.src.name(), self.dest.name())) sys.exit (0) - bridges = self.qmf.getObjects(cls="bridge") + bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: if bridge.linkRef == link.getObjectId() and bridge.dest == exchange and bridge.key == routingKey: if _verbose: @@ -186,8 +186,8 @@ class RouteManager: raise Exception("Route not found") def ListRoutes (self): - links = self.qmf.getObjects(cls="link") - bridges = self.qmf.getObjects(cls="bridge") + links = self.qmf.getObjects(_class="link") + bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: myLink = None @@ -199,8 +199,8 @@ class RouteManager: print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, bridge.key) def ClearAllRoutes (self): - links = self.qmf.getObjects(cls="link") - bridges = self.qmf.getObjects(cls="bridge") + links = self.qmf.getObjects(_class="link") + bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: if _verbose: @@ -218,7 +218,7 @@ class RouteManager: print "Ok" if _dellink: - links = self.qmf.getObjects(cls="link") + links = self.qmf.getObjects(_class="link") for link in links: if _verbose: print "Deleting Link: %s:%d... " % (link.host, link.port), diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 8d8339b2c6..81d9dbe030 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -285,7 +285,7 @@ class managementClient: ft = {} ft["_class"] = className codec.write_map (ft) - msg = channel.message(codec.encoded, routing_key="agent.%d" % bank) + msg = channel.message(codec.encoded, routing_key="agent.1.%d" % bank) channel.send ("qpid.management", msg) def syncWaitForStable (self, channel): @@ -398,7 +398,7 @@ class managementClient: """ Compose the header of a management message. """ codec.write_uint8 (ord ('A')) codec.write_uint8 (ord ('M')) - codec.write_uint8 (ord ('1')) + codec.write_uint8 (ord ('2')) codec.write_uint8 (opcode) codec.write_uint32 (seq) @@ -412,7 +412,7 @@ class managementClient: if octet != 'M': return None octet = chr (codec.read_uint8 ()) - if octet != '1': + if octet != '2': return None opcode = chr (codec.read_uint8 ()) seq = codec.read_uint32 () @@ -433,7 +433,7 @@ class managementClient: elif typecode == 6: codec.write_str8 (value) elif typecode == 7: - codec.write_vbin32 (value) + codec.write_str16 (value) elif typecode == 8: # ABSTIME codec.write_uint64 (long (value)) elif typecode == 9: # DELTATIME @@ -476,7 +476,7 @@ class managementClient: elif typecode == 6: data = str (codec.read_str8 ()) elif typecode == 7: - data = codec.read_vbin32 () + data = codec.read_str16 () elif typecode == 8: # ABSTIME data = codec.read_uint64 () elif typecode == 9: # DELTATIME @@ -604,6 +604,9 @@ class managementClient: ch.send ("qpid.management", smsg) def handleClassInd (self, ch, codec): + kind = codec.read_uint8() + if kind != 1: # This API doesn't handle new-style events + return pname = str (codec.read_str8()) cname = str (codec.read_str8()) hash = codec.read_bin128() @@ -656,13 +659,15 @@ class managementClient: def parseSchema (self, ch, codec): """ Parse a received schema-description message. """ self.decOutstanding (ch) + kind = codec.read_uint8() + if kind != 1: # This API doesn't handle new-style events + return packageName = str (codec.read_str8 ()) className = str (codec.read_str8 ()) hash = codec.read_bin128 () configCount = codec.read_uint16 () instCount = codec.read_uint16 () methodCount = codec.read_uint16 () - eventCount = codec.read_uint16 () if packageName not in self.packages: return @@ -676,7 +681,6 @@ class managementClient: configs = [] insts = [] methods = {} - events = {} configs.append (("id", 4, "", "", 1, 1, None, None, None, None, None)) insts.append (("id", 4, None, None)) @@ -765,42 +769,14 @@ class managementClient: args.append (arg) methods[mname] = (mdesc, args) - for idx in range (eventCount): - ft = codec.read_map () - ename = str (ft["name"]) - argCount = ft["argCount"] - if "desc" in ft: - edesc = str (ft["desc"]) - else: - edesc = None - - args = [] - for aidx in range (argCount): - ft = codec.read_map () - name = str (ft["name"]) - type = ft["type"] - unit = None - desc = None - - for key, value in ft.items (): - if key == "unit": - unit = str (value) - elif key == "desc": - desc = str (value) - - arg = (name, type, unit, desc) - args.append (arg) - events[ename] = (edesc, args) - schemaClass = {} schemaClass['C'] = configs schemaClass['I'] = insts schemaClass['M'] = methods - schemaClass['E'] = events self.schema[classKey] = schemaClass if self.schemaCb != None: - self.schemaCb (ch.context, classKey, configs, insts, methods, events) + self.schemaCb (ch.context, classKey, configs, insts, methods, {}) def parsePresenceMasks(self, codec, schemaClass): """ Generate a list of not-present properties """ @@ -896,7 +872,7 @@ class managementClient: codec.write_str8 (classId[1]) codec.write_bin128 (classId[2]) codec.write_str8 (methodName) - bank = objId.getBank() + bank = "%d.%d" % (objId.getBroker(), objId.getBank()) # Encode args according to schema if classId not in self.schema: @@ -926,5 +902,5 @@ class managementClient: packageName = classId[0] className = classId[1] - msg = channel.message(codec.encoded, "agent." + str(bank)) + msg = channel.message(codec.encoded, "agent." + bank) channel.send ("qpid.management", msg) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index d86dd3a360..2bf66a5e5b 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -546,10 +546,10 @@ class ManagementData: for classKey in sorted: tuple = self.schema[classKey] row = (self.displayClassName(classKey), len (tuple[0]), len (tuple[1]), - len (tuple[2]), len (tuple[3])) + len (tuple[2])) rows.append (row) self.disp.table ("Classes in Schema:", - ("Class", "Properties", "Statistics", "Methods", "Events"), + ("Class", "Properties", "Statistics", "Methods"), rows) finally: self.lock.release () diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 3800e54b5b..c8035e87f2 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -49,7 +49,7 @@ class Console: """ Invoked when a QMF package is discovered. """ pass - def newClass(self, classKey): + def newClass(self, kind, classKey): """ Invoked when a new class is discovered. Session.getSchema can be used to obtain details about the class.""" pass @@ -158,7 +158,7 @@ class Session: raise Exception(broker.error) self.brokers.append(broker) - self.getObjects(broker=broker, cls="agent") + self.getObjects(broker=broker, _class="agent") return broker def delBroker(self, broker): @@ -219,35 +219,36 @@ class Session: The class for queried objects may be specified in one of the following ways: - schema = - supply a schema object returned from getSchema - key = - supply a classKey from the list returned by getClasses - cls = - supply a class name as a string + _schema = - supply a schema object returned from getSchema. + _key = - supply a classKey from the list returned by getClasses. + _class = - supply a class name as a string. If the class name exists + in multiple packages, a _package argument may also be supplied. If objects should be obtained from only one agent, use the following argument. Otherwise, the query will go to all agents. - agent = - supply an agent from the list returned by getAgents + _agent = - supply an agent from the list returned by getAgents. If the get query is to be restricted to one broker (as opposed to all connected brokers), add the following argument: - broker = - supply a broker as returned by addBroker + _broker = - supply a broker as returned by addBroker. If additional arguments are supplied, they are used as property selectors. For example, if the argument name="test" is supplied, only objects whose "name" property is "test" will be returned in the result. """ - if "broker" in kwargs: + if "_broker" in kwargs: brokerList = [] - brokerList.append(kwargs["broker"]) + brokerList.append(kwargs["_broker"]) else: brokerList = self.brokers for broker in brokerList: broker._waitForStable() agentList = [] - if "agent" in kwargs: - agent = kwargs["agent"] + if "_agent" in kwargs: + agent = kwargs["_agent"] if agent.broker not in brokerList: raise Exception("Supplied agent is not accessible through the supplied broker") agentList.append(agent) @@ -257,11 +258,14 @@ class Session: agentList.append(agent) cname = None - if "schema" in kwargs: pname, cname, hash = kwargs["schema"].getKey() - elif "key" in kwargs: pname, cname, hash = kwargs["key"] - elif "cls" in kwargs: pname, cname, hash = None, kwargs["cls"], None + if "_schema" in kwargs: pname, cname, hash = kwargs["_schema"].getKey() + elif "_key" in kwargs: pname, cname, hash = kwargs["_key"] + elif "_class" in kwargs: + pname, cname, hash = None, kwargs["_class"], None + if "_package" in kwargs: + pname = kwargs["_package"] if cname == None: - raise Exception("No class supplied, use 'schema', 'key', or 'cls' argument") + raise Exception("No class supplied, use '_schema', '_key', or '_class' argument") map = {} map["_class"] = cname if pname != None: map["_package"] = pname @@ -269,7 +273,7 @@ class Session: self.getSelect = [] for item in kwargs: - if item != "schema" and item != "key" and item != "cls": + if item[0] != '_': self.getSelect.append((item, kwargs[item])) self.getResult = [] @@ -282,7 +286,7 @@ class Session: self.cv.release() broker._setHeader(sendCodec, 'G', seq) sendCodec.write_map(map) - smsg = broker._message(sendCodec.encoded, "agent.%d" % agent.bank) + smsg = broker._message(sendCodec.encoded, "agent.%s" % agent.bank) broker._send(smsg) starttime = time() @@ -382,6 +386,7 @@ class Session: self.cv.release() def _handleClassInd(self, broker, codec, seq): + kind = codec.read_uint8() pname = str(codec.read_str8()) cname = str(codec.read_str8()) hash = codec.read_bin128() @@ -431,17 +436,18 @@ class Session: self.console.event(broker, event) def _handleSchemaResp(self, broker, codec, seq): + kind = codec.read_uint8() pname = str(codec.read_str8()) cname = str(codec.read_str8()) hash = codec.read_bin128() classKey = (pname, cname, hash) - _class = SchemaClass(classKey, codec) + _class = SchemaClass(kind, classKey, codec) self.cv.acquire() self.packages[pname][(cname, hash)] = _class self.cv.release() broker._decOutstanding() if self.console != None: - self.console.newClass(classKey) + self.console.newClass(kind, classKey) def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): pname = str(codec.read_str8()) @@ -485,7 +491,7 @@ class Session: def _selectMatch(self, object): """ Check the object against self.getSelect to check for a match """ for key, value in self.getSelect: - for prop, propval in object.properties: + for prop, propval in object.getProperties(): if key == prop.name and value != propval: return False return True @@ -497,7 +503,7 @@ class Session: elif typecode == 3: data = codec.read_uint32() # U32 elif typecode == 4: data = codec.read_uint64() # U64 elif typecode == 6: data = str(codec.read_str8()) # SSTR - elif typecode == 7: data = codec.read_vbin32() # LSTR + elif typecode == 7: data = codec.read_str16() # LSTR elif typecode == 8: data = codec.read_int64() # ABSTIME elif typecode == 9: data = codec.read_uint64() # DELTATIME elif typecode == 10: data = ObjectId(codec) # REF @@ -521,7 +527,7 @@ class Session: elif typecode == 3: codec.write_uint32 (long(value)) # U32 elif typecode == 4: codec.write_uint64 (long(value)) # U64 elif typecode == 6: codec.write_str8 (value) # SSTR - elif typecode == 7: codec.write_vbin32 (value) # LSTR + elif typecode == 7: codec.write_str16 (value) # LSTR elif typecode == 8: codec.write_int64 (long(value)) # ABSTIME elif typecode == 9: codec.write_uint64 (long(value)) # DELTATIME elif typecode == 10: value.encode (codec) # REF @@ -577,30 +583,42 @@ class ClassKey: class SchemaClass: """ """ - def __init__(self, key, codec): + CLASS_KIND_TABLE = 1 + CLASS_KIND_EVENT = 2 + + def __init__(self, kind, key, codec): + self.kind = kind self.classKey = key self.properties = [] self.statistics = [] - self.methods = [] - self.events = [] - - propCount = codec.read_uint16() - statCount = codec.read_uint16() - methodCount = codec.read_uint16() - eventCount = codec.read_uint16() - - for idx in range(propCount): - self.properties.append(SchemaProperty(codec)) - for idx in range(statCount): - self.statistics.append(SchemaStatistic(codec)) - for idx in range(methodCount): - self.methods.append(SchemaMethod(codec)) - for idx in range(eventCount): - self.events.append(SchemaEvent(codec)) + self.methods = [] + self.arguments = [] + + if self.kind == self.CLASS_KIND_TABLE: + propCount = codec.read_uint16() + statCount = codec.read_uint16() + methodCount = codec.read_uint16() + for idx in range(propCount): + self.properties.append(SchemaProperty(codec)) + for idx in range(statCount): + self.statistics.append(SchemaStatistic(codec)) + for idx in range(methodCount): + self.methods.append(SchemaMethod(codec)) + + elif self.kind == self.CLASS_KIND_EVENT: + argCount = codec.read_uint16() + for idx in range(argCount): + self.arguments.append(SchemaArgument(codec, methodArg=False)) def __repr__(self): pname, cname, hash = self.classKey - result = "Class: %s:%s " % (pname, cname) + if self.kind == self.CLASS_KIND_TABLE: + kindStr = "Table" + elif self.kind == self.CLASS_KIND_EVENT: + kindStr = "Event" + else: + kindStr = "Unsupported" + result = "%s Class: %s:%s " % (kindStr, pname, cname) result += "(%08x-%04x-%04x-%04x-%04x%08x)" % struct.unpack ("!LHHHHL", hash) return result @@ -620,9 +638,9 @@ class SchemaClass: """ Return the list of methods for the class. """ return self.methods - def getEvents(self): + def getArguments(self): """ Return the list of events for the class. """ - return self.events + return self.arguments class SchemaProperty: """ """ @@ -693,33 +711,6 @@ class SchemaMethod: result += ")" return result -class SchemaEvent: - """ """ - def __init__(self, codec): - map = codec.read_map() - self.name = str(map["name"]) - argCount = map["argCount"] - if "desc" in map: - self.desc = str(map["desc"]) - else: - self.desc = None - self.arguments = [] - - for idx in range(argCount): - self.arguments.append(SchemaArgument(codec, methodArg=False)) - - def __repr__(self): - result = self.name + "(" - first = True - for arg in self.arguments: - if first: - first = False - else: - result += ", " - result += arg.name - result += ")" - return result - class SchemaArgument: """ """ def __init__(self, codec, methodArg): @@ -743,7 +734,7 @@ class SchemaArgument: elif key == "desc" : self.desc = str(value) elif key == "default" : self.default = str(value) -class ObjectId(object): +class ObjectId: """ Object that represents QMF object identifiers """ def __init__(self, codec, first=0, second=0): if codec: @@ -800,80 +791,86 @@ class Object(object): """ """ def __init__(self, session, broker, schema, codec, prop, stat): """ """ - self.session = session - self.broker = broker - self.schema = schema - self.currentTime = codec.read_uint64() - self.createTime = codec.read_uint64() - self.deleteTime = codec.read_uint64() - self.objectId = ObjectId(codec) - self.properties = [] - self.statistics = [] + self._session = session + self._broker = broker + self._schema = schema + self._currentTime = codec.read_uint64() + self._createTime = codec.read_uint64() + self._deleteTime = codec.read_uint64() + self._objectId = ObjectId(codec) + self._properties = [] + self._statistics = [] if prop: notPresent = self._parsePresenceMasks(codec, schema) for property in schema.getProperties(): if property.name in notPresent: - self.properties.append((property, None)) + self._properties.append((property, None)) else: - self.properties.append((property, self.session._decodeValue(codec, property.type))) + self._properties.append((property, self._session._decodeValue(codec, property.type))) if stat: for statistic in schema.getStatistics(): - self.statistics.append((statistic, self.session._decodeValue(codec, statistic.type))) + self._statistics.append((statistic, self._session._decodeValue(codec, statistic.type))) def getObjectId(self): """ Return the object identifier for this object """ - return self.objectId + return self._objectId def getClassKey(self): """ Return the class-key that references the schema describing this object. """ - return self.schema.getKey() + return self._schema.getKey() def getSchema(self): """ Return the schema that describes this object. """ - return self.schema + return self._schema def getMethods(self): """ Return a list of methods available for this object. """ - return self.schema.getMethods() + return self._schema.getMethods() def getTimestamps(self): """ Return the current, creation, and deletion times for this object. """ - return self.currentTime, self.createTime, self.deleteTime + return self._currentTime, self._createTime, self._deleteTime def getIndex(self): """ Return a string describing this object's primary key. """ result = "" - for property, value in self.properties: + for property, value in self._properties: if property.index: if result != "": result += ":" result += str(value) return result + def getProperties(self): + return self._properties + + def getStatistics(self): + return self._statistics + def __repr__(self): return self.getIndex() def __getattr__(self, name): - for method in self.schema.getMethods(): + for method in self._schema.getMethods(): if name == method.name: return lambda *args, **kwargs : self._invoke(name, args, kwargs) - for property, value in self.properties: + for property, value in self._properties: if name == property.name: return value - for statistic, value in self.statistics: + for statistic, value in self._statistics: if name == statistic.name: return value raise Exception("Type Object has no attribute '%s'" % name) def _invoke(self, name, args, kwargs): - for method in self.schema.getMethods(): + for method in self._schema.getMethods(): if name == method.name: aIdx = 0 - sendCodec = Codec(self.broker.conn.spec) - seq = self.session.seqMgr._reserve((self, method)) - self.broker._setHeader(sendCodec, 'M', seq) - self.objectId.encode(sendCodec) - pname, cname, hash = self.schema.getKey() + sendCodec = Codec(self._broker.conn.spec) + seq = self._session.seqMgr._reserve((self, method)) + self._broker._setHeader(sendCodec, 'M', seq) + self._objectId.encode(sendCodec) + pname, cname, hash = self._schema.getKey() sendCodec.write_str8(pname) sendCodec.write_str8(cname) sendCodec.write_bin128(hash) @@ -888,29 +885,30 @@ class Object(object): for arg in method.arguments: if arg.dir.find("I") != -1: - self.session._encodeValue(sendCodec, args[aIdx], arg.type) + self._session._encodeValue(sendCodec, args[aIdx], arg.type) aIdx += 1 - smsg = self.broker._message(sendCodec.encoded, "agent." + str(self.objectId.getBank())) - self.broker.cv.acquire() - self.broker.syncInFlight = True - self.broker.cv.release() + smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % + (self._objectId.getBroker(), self._objectId.getBank())) + self._broker.cv.acquire() + self._broker.syncInFlight = True + self._broker.cv.release() - self.broker._send(smsg) + self._broker._send(smsg) - self.broker.cv.acquire() + self._broker.cv.acquire() starttime = time() - while self.broker.syncInFlight and self.broker.error == None: - self.broker.cv.wait(self.broker.SYNC_TIME) - if time() - starttime > self.broker.SYNC_TIME: - self.broker.cv.release() - self.session.seqMgr._release(seq) + while self._broker.syncInFlight and self._broker.error == None: + self._broker.cv.wait(self._broker.SYNC_TIME) + if time() - starttime > self._broker.SYNC_TIME: + self._broker.cv.release() + self._session.seqMgr._release(seq) raise RuntimeError("Timed out waiting for method to respond") - self.broker.cv.release() - if self.broker.error != None: - errorText = self.broker.error - self.broker.error = None + self._broker.cv.release() + if self._broker.error != None: + errorText = self._broker.error + self._broker.error = None raise Exception(errorText) - return self.broker.syncResult + return self._broker.syncResult raise Exception("Invalid Method (software defect) [%s]" % name) def _parsePresenceMasks(self, codec, schema): @@ -954,7 +952,7 @@ class Broker: self.authUser = authUser self.authPass = authPass self.agents = {} - self.agents[0] = Agent(self, 0, "BrokerAgent") + self.agents[0] = Agent(self, "1.0", "BrokerAgent") self.topicBound = False self.cv = Condition() self.syncInFlight = False @@ -1040,14 +1038,15 @@ class Broker: self.error = "Connect Failed %d - %s" % (e[0], e[1]) def _updateAgent(self, obj): - if obj.deleteTime == 0: - if obj.objectIdBank not in self.agents: - agent = Agent(self, obj.objectIdBank, obj.label) - self.agents[obj.objectIdBank] = agent + bankKey = "%d.%d" % (obj.brokerBank, obj.agentBank) + if obj._deleteTime == 0: + if bankKey not in self.agents: + agent = Agent(self, bankKey, obj.label) + self.agents[bankKey] = agent if self.session.console != None: self.session.console.newAgent(agent) else: - agent = self.agents.pop(obj.objectIdBank, None) + agent = self.agents.pop(bankKey, None) if agent != None and self.session.console != None: self.session.console.delAgent(agent) @@ -1055,7 +1054,7 @@ class Broker: """ Compose the header of a management message. """ codec.write_uint8(ord('A')) codec.write_uint8(ord('M')) - codec.write_uint8(ord('1')) + codec.write_uint8(ord('2')) codec.write_uint8(ord(opcode)) codec.write_uint32(seq) @@ -1068,7 +1067,7 @@ class Broker: if octet != 'M': return None, None octet = chr(codec.read_uint8()) - if octet != '1': + if octet != '2': return None, None opcode = chr(codec.read_uint8()) seq = codec.read_uint32() @@ -1164,28 +1163,24 @@ class Agent: self.label = label def __repr__(self): - return "Agent at bank %d (%s)" % (self.bank, self.label) + return "Agent at bank %s (%s)" % (self.bank, self.label) class Event: """ """ def __init__(self, session, codec): self.session = session - self.timestamp = codec.read_int64() - self.objectId = ObjectId(codec) pname = codec.read_str8() cname = codec.read_str8() hash = codec.read_bin128() self.classKey = (pname, cname, hash) - self.name = codec.read_str8() + self.timestamp = codec.read_int64() + self.schema = None if pname in session.packages: if (cname, hash) in session.packages[pname]: - schema = session.packages[pname][(cname, hash)] - for event in schema.getEvents(): - if event.name == self.name: - self.schemaEvent = event - self.arguments = {} - for arg in event.arguments: - self.arguments[arg.name] = session._decodeValue(codec, arg.type) + self.schema = session.packages[pname][(cname, hash)] + self.arguments = {} + for arg in self.schema.arguments: + self.arguments[arg.name] = session._decodeValue(codec, arg.type) def __repr__(self): return self.getSyslogText() @@ -1202,10 +1197,15 @@ class Event: def getName(self): return self.name + def getSchema(self): + return self.schema + def getSyslogText(self): + if self.schema == None: + return "" out = strftime("%c", gmtime(self.timestamp / 1000000000)) - out += " " + self.classKey[0] + ":" + self.classKey[1] + " " + self.name - for arg in self.schemaEvent.arguments: + out += " " + self.classKey[0] + ":" + self.classKey[1] + for arg in self.schema.arguments: out += " " + arg.name + "=" + self.session._displayValue(self.arguments[arg.name], arg.type) return out @@ -1247,8 +1247,8 @@ class DebugConsole(Console): def newPackage(self, name): print "newPackage:", name - def newClass(self, classKey): - print "newClass:", classKey + def newClass(self, kind, classKey): + print "newClass:", kind, classKey def newAgent(self, agent): print "newAgent:", agent diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index eea1b29404..efec2b8a92 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -59,7 +59,7 @@ class ManagementTest (TestBase010): session = self.session self.startQmf() - brokers = self.qmf.getObjects(cls="broker") + brokers = self.qmf.getObjects(_class="broker") self.assertEqual (len(brokers), 1) broker = brokers[0] @@ -147,43 +147,43 @@ class ManagementTest (TestBase010): session.queue_declare(queue="dest-queue", exclusive=True, auto_delete=True) session.exchange_bind(queue="dest-queue", exchange="amq.direct") - queues = self.qmf.getObjects(cls="queue") + queues = self.qmf.getObjects(_class="queue") "Move 10 messages from src-queue to dest-queue" - result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("src-queue", "dest-queue", 10) + result = self.qmf.getObjects(_class="broker")[0].queueMoveMessages("src-queue", "dest-queue", 10) self.assertEqual (result.status, 0) - sq = self.qmf.getObjects(cls="queue", name="src-queue")[0] - dq = self.qmf.getObjects(cls="queue", name="dest-queue")[0] + sq = self.qmf.getObjects(_class="queue", name="src-queue")[0] + dq = self.qmf.getObjects(_class="queue", name="dest-queue")[0] self.assertEqual (sq.msgDepth,10) self.assertEqual (dq.msgDepth,10) "Move all remaining messages to destination" - result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("src-queue", "dest-queue", 0) + result = self.qmf.getObjects(_class="broker")[0].queueMoveMessages("src-queue", "dest-queue", 0) self.assertEqual (result.status,0) - sq = self.qmf.getObjects(cls="queue", name="src-queue")[0] - dq = self.qmf.getObjects(cls="queue", name="dest-queue")[0] + sq = self.qmf.getObjects(_class="queue", name="src-queue")[0] + dq = self.qmf.getObjects(_class="queue", name="dest-queue")[0] self.assertEqual (sq.msgDepth,0) self.assertEqual (dq.msgDepth,20) "Use a bad source queue name" - result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("bad-src-queue", "dest-queue", 0) + result = self.qmf.getObjects(_class="broker")[0].queueMoveMessages("bad-src-queue", "dest-queue", 0) self.assertEqual (result.status,4) "Use a bad destination queue name" - result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("src-queue", "bad-dest-queue", 0) + result = self.qmf.getObjects(_class="broker")[0].queueMoveMessages("src-queue", "bad-dest-queue", 0) self.assertEqual (result.status,4) " Use a large qty (40) to move from dest-queue back to " " src-queue- should move all " - result = self.qmf.getObjects(cls="broker")[0].queueMoveMessages("dest-queue", "src-queue", 40) + result = self.qmf.getObjects(_class="broker")[0].queueMoveMessages("dest-queue", "src-queue", 40) self.assertEqual (result.status,0) - sq = self.qmf.getObjects(cls="queue", name="src-queue")[0] - dq = self.qmf.getObjects(cls="queue", name="dest-queue")[0] + sq = self.qmf.getObjects(_class="queue", name="src-queue")[0] + dq = self.qmf.getObjects(_class="queue", name="dest-queue")[0] self.assertEqual (sq.msgDepth,20) self.assertEqual (dq.msgDepth,0) @@ -216,23 +216,23 @@ class ManagementTest (TestBase010): msg = Message(props, body) session.message_transfer(destination="amq.direct", message=msg) - pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] + pq = self.qmf.getObjects(_class="queue", name="purge-queue")[0] "Purge top message from purge-queue" result = pq.purge(1) self.assertEqual (result.status, 0) - pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] + pq = self.qmf.getObjects(_class="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,19) "Purge top 9 messages from purge-queue" result = pq.purge(9) self.assertEqual (result.status, 0) - pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] + pq = self.qmf.getObjects(_class="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,10) "Purge all messages from purge-queue" result = pq.purge(0) self.assertEqual (result.status, 0) - pq = self.qmf.getObjects(cls="queue", name="purge-queue")[0] + pq = self.qmf.getObjects(_class="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,0) -- cgit v1.2.1 From 7f4b58e6a0d93de091ff14efc07bb49f42294b81 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 8 Oct 2008 19:44:24 +0000 Subject: QPID-1327 - Added severity field to events, cleaned up routing key usage git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@702977 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 4 +++- qpid/python/qpid/qmfconsole.py | 47 ++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 16 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 81d9dbe030..e6ff5852f2 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -522,7 +522,9 @@ class managementClient: if self.ctrlCb != None: self.ctrlCb (ch.context, self.CTRL_SCHEMA_LOADED, None) ch.ssn.exchange_bind (exchange="qpid.management", - queue=ch.topicName, binding_key="mgmt.#") + queue=ch.topicName, binding_key="console.#") + ch.ssn.exchange_bind (exchange="qpid.management", + queue=ch.topicName, binding_key="schema.#") def handleMethodReply (self, ch, codec, sequence): diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index c8035e87f2..bcd1e59819 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -141,6 +141,10 @@ class Session: self.getResult = [] self.getSelect = [] self.error = None + if self.console == None: + rcvObjects = False + rcvEvents = False + rcvHeartbeats = False self.bindingKeyList = self._bindingKeys(rcvObjects, rcvEvents, rcvHeartbeats) self.manageConnections = manageConnections @@ -316,16 +320,19 @@ class Session: def _bindingKeys(self, rcvObjects, rcvEvents, rcvHeartbeats): keyList = [] + keyList.append("schema.#") if rcvObjects and rcvEvents and rcvHeartbeats: - keyList.append("mgmt.#") + keyList.append("console.#") else: if rcvObjects: - keyList.append("mgmt.*.prop.#") - keyList.append("mgmt.*.stat.#") + keyList.append("console.prop.#") + keyList.append("console.stat.#") + else: + keyList.append("console.prop.org.apache.qpid.broker.agent") if rcvEvents: - keyList.append("mgmt.event") + keyList.append("console.event.#") if rcvHeartbeats: - keyList.append("mgmt.*.heartbeat.#") + keyList.append("console.heartbeat") return keyList def _handleBrokerConnect(self, broker): @@ -1013,14 +1020,13 @@ class Broker: self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFF) self.amqpSession.message_flow(destination="rdest", unit=1, value=0xFFFFFFFF) - if self.session.console != None: - self.topicName = "topic-%s" % self.amqpSessionId - self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) - self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest") - self.amqpSession.incoming("tdest").listen(self._replyCb) - self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) - self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) - self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) + self.topicName = "topic-%s" % self.amqpSessionId + self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) + self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest") + self.amqpSession.incoming("tdest").listen(self._replyCb) + self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) + self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) self.isConnected = True self.session._handleBrokerConnect(self) @@ -1116,7 +1122,7 @@ class Broker: def _decOutstanding(self): self.cv.acquire() self.reqsOutstanding -= 1 - if self.reqsOutstanding == 0 and not self.topicBound and self.session.console != None: + if self.reqsOutstanding == 0 and not self.topicBound: self.topicBound = True for key in self.session.bindingKeyList: self.amqpSession.exchange_bind(exchange="qpid.management", @@ -1174,6 +1180,7 @@ class Event: hash = codec.read_bin128() self.classKey = (pname, cname, hash) self.timestamp = codec.read_int64() + self.severity = codec.read_uint8() self.schema = None if pname in session.packages: if (cname, hash) in session.packages[pname]: @@ -1185,6 +1192,16 @@ class Event: def __repr__(self): return self.getSyslogText() + def _sevName(self): + if self.severity == 0 : return "EMER " + if self.severity == 1 : return "ALERT" + if self.severity == 2 : return "CRIT " + if self.severity == 3 : return "ERROR" + if self.severity == 4 : return "WARN " + if self.severity == 5 : return "NOTIC" + if self.severity == 6 : return "INFO " + if self.severity == 7 : return "DEBUG" + def getClassKey(self): return self.classKey @@ -1204,7 +1221,7 @@ class Event: if self.schema == None: return "" out = strftime("%c", gmtime(self.timestamp / 1000000000)) - out += " " + self.classKey[0] + ":" + self.classKey[1] + out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] for arg in self.schema.arguments: out += " " + arg.name + "=" + self.session._displayValue(self.arguments[arg.name], arg.type) return out -- cgit v1.2.1 From 1e574fc0afaa12c849369bbb4b33e7f2c9a1c465 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 8 Oct 2008 21:17:14 +0000 Subject: Added new queue options to qpid-config (lvq, cluster-durable, optimistic) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@702991 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 97 ++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 33 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 8b011778d6..e7f866edcc 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -24,20 +24,26 @@ import getopt import sys from qpid import qmfconsole -_recursive = False -_host = "localhost" -_durable = False -_fileCount = 8 -_fileSize = 24 -_maxQueueSize = None -_maxQueueCount= None -_policyType = None +_recursive = False +_host = "localhost" +_durable = False +_clusterDurable = False +_fileCount = 8 +_fileSize = 24 +_maxQueueSize = None +_maxQueueCount = None +_policyType = None +_lvq = False +_optimisticConsume = False FILECOUNT = "qpid.file_count" FILESIZE = "qpid.file_size" MAX_QUEUE_SIZE = "qpid.max_size" MAX_QUEUE_COUNT = "qpid.max_count" POLICY_TYPE = "qpid.policy_type" +CLUSTER_DURABLE = "qpid.persist_last_node" +LVQ = "qpid.last_value_queue" +OPTIMISTIC_CONSUME = "qpid.optimistic_consume" def Usage (): print "Usage: qpid-config [OPTIONS]" @@ -57,12 +63,15 @@ def Usage (): print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print print "Add Queue Options:" - print " --durable Queue is durable" - print " --file-count N (8) Number of files in queue's persistence journal" - print " --file-size N (24) File size in pages (64Kib/page)" - print " --max-queue-size N Maximum in-memory queue size as bytes" - print " --max-queue-count N Maximum in-memory queue size as a number of messages" - print " --policy-type TYPE Action taken when queue limit is reached (reject, flow_to_disk, ring, ring_strict)" + print " --durable Queue is durable" + print " --cluster-durable Queue becomes durable if there is only one functioning cluster node" + print " --file-count N (8) Number of files in queue's persistence journal" + print " --file-size N (24) File size in pages (64Kib/page)" + print " --max-queue-size N Maximum in-memory queue size as bytes" + print " --max-queue-count N Maximum in-memory queue size as a number of messages" + print " --policy-type TYPE Action taken when queue limit is reached (reject, flow_to_disk, ring, ring_strict)" + print " --last-value-queue Enable LVQ behavior on the queue" + print " --optimistic-consume Enable optimistic consume on the queue" print print "Add Exchange Options:" print " --durable Exchange is durable" @@ -130,25 +139,33 @@ class BrokerManager: def QueueList (self, filter): - queues = self.qmf.getObjects(_class="queue") - journals = self.qmf.getObjects(_class="journal") - print " Store Size" - print "Durable AutoDel Excl Bindings (files x file pages) Queue Name" - print "===========================================================================================" + queues = self.qmf.getObjects(_class="queue") + + caption = "Queue Name" + maxNameLen = len(caption) + for q in queues: + if len(q.name) > maxNameLen: maxNameLen = len(q.name) + print "%-*s Attributes" % (maxNameLen, caption) + line = "" + for i in range((maxNameLen / 5) + 5): + line += "=====" + print line + for q in queues: - if self.match (q.name, filter): - args = q.arguments - if q.durable and FILESIZE in args and FILECOUNT in args: - fs = int (args[FILESIZE]) - fc = int (args[FILECOUNT]) - print "%4c%9c%7c%10d%11dx%-14d%s" % \ - (YN (q.durable), YN (q.autoDelete), - YN (q.exclusive), q.bindingCount, fc, fs, q.name) - else: - if not _durable: - print "%4c%9c%7c%10d %s" % \ - (YN (q.durable), YN (q.autoDelete), - YN (q.exclusive), q.bindingCount, q.name) + print "%-*s " % (maxNameLen, q.name), + args = q.arguments + if q.durable: print "durable", + if CLUSTER_DURABLE in args and args[CLUSTER_DURABLE] == 1: print "clusterDurable", + if q.autoDelete: print "autoDel", + if q.exclusive: print "excl", + if FILESIZE in args: print "fileSize=%d" % args[FILESIZE], + if FILECOUNT in args: print "fileCount=%d" % args[FILECOUNT], + if MAX_QUEUE_SIZE in args: print "maxQSize=%d" % args[MAX_QUEUE_SIZE], + if MAX_QUEUE_COUNT in args: print "maxQCount=%d" % args[MAX_QUEUE_COUNT], + if POLICY_TYPE in args: print "policy=%s" % args[POLICY_TYPE], + if LVQ in args and args[LVQ] == 1: print "lvq", + if OPTIMISTIC_CONSUME in args and args[OPTIMISTIC_CONSUME] == 1: print "optConsume", + print def QueueListRecurse (self, filter): exchanges = self.qmf.getObjects(_class="exchange") @@ -195,6 +212,12 @@ class BrokerManager: declArgs[MAX_QUEUE_COUNT] = _maxQueueCount if _policyType: declArgs[POLICY_TYPE] = _policyType + if _clusterDurable: + declArgs[CLUSTER_DURABLE] = 1 + if _lvq: + declArgs[LVQ] = 1 + if _optimisticConsume: + declArgs[OPTIMISTIC_CONSUME] = 1 self.broker.getAmqpSession().queue_declare (queue=qname, durable=_durable, arguments=declArgs) @@ -247,7 +270,9 @@ def YN (bool): ## try: - longOpts = ("durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=") + longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", + "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=", + "last-value-queue", "optimistic-consume") (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -259,6 +284,8 @@ for opt in optlist: _host = opt[1] if opt[0] == "--durable": _durable = True + if opt[0] == "--cluster-durable": + _clusterDurable = True if opt[0] == "--file-count": _fileCount = int (opt[1]) if opt[0] == "--file-size": @@ -269,6 +296,10 @@ for opt in optlist: _maxQueueCount = int (opt[1]) if opt[0] == "--policy-type": _policyType = opt[1] + if opt[0] == "--last-value-queue": + _lvq = True + if opt[0] == "--optimistic-consume": + _optimisticConsume = True nargs = len (cargs) bm = BrokerManager () -- cgit v1.2.1 From 90f05a2c343bdddda6863f80563702eaf5eece8a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 9 Oct 2008 13:47:48 +0000 Subject: QPID-1327 - Added optional severity override in the eventRaise method git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@703164 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 1 + 1 file changed, 1 insertion(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index bcd1e59819..59d4752210 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -1201,6 +1201,7 @@ class Event: if self.severity == 5 : return "NOTIC" if self.severity == 6 : return "INFO " if self.severity == 7 : return "DEBUG" + return "INV-%d" % self.severity def getClassKey(self): return self.classKey -- cgit v1.2.1 From d7c677547f17a51f8c97ea10b7516fd44ef8d1d2 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Fri, 10 Oct 2008 16:54:54 +0000 Subject: Handle ttl in messages transfers received by the broker 7 added test for it Moved Timer instance from DtxManager to Broker so it can be shared git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@703521 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index e4dc5566bd..38f87d8526 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -23,7 +23,7 @@ from qpid.datatypes import Message, RangedSet from qpid.session import SessionException from qpid.content import Content - +from time import sleep class MessageTests(TestBase010): """Tests for 'methods' on the amqp message 'class'""" @@ -369,7 +369,7 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 21 + msg_size = 27 #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") @@ -438,7 +438,7 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 19 + msg_size = 27 #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") @@ -810,6 +810,28 @@ class MessageTests(TestBase010): msg = messages.get() assert msg.body == "test" + def test_ttl(self): + q = "test_ttl" + session = self.session + + session.queue_declare(queue=q, exclusive=True, auto_delete=True) + + dp = session.delivery_properties(routing_key=q, ttl=500)#expire in half a second + session.message_transfer(message=Message(dp, "first")) + + dp = session.delivery_properties(routing_key=q, ttl=300000)#expire in fives minutes + session.message_transfer(message=Message(dp, "second")) + + d = "msgs" + session.message_subscribe(queue=q, destination=d) + messages = session.incoming(d) + sleep(1) + session.message_flow(unit = session.credit_unit.message, value=2, destination=d) + session.message_flow(unit = session.credit_unit.byte, value=0xFFFFFFFF, destination=d) + assert messages.get(timeout=1).body == "second" + self.assertEmpty(messages) + + def assertDataEquals(self, session, msg, expected): self.assertEquals(expected, msg.body) -- cgit v1.2.1 From 382eb7c5a9cf91807f68085231b103139e7eea99 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 10 Oct 2008 18:12:42 +0000 Subject: QPID-1327 - Add display of source broker in __repr__ for events git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@703543 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 72 ++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 35 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 59d4752210..64e1df78b0 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -439,7 +439,7 @@ class Session: def _handleEventInd(self, broker, codec, seq): if self.console != None: - event = Event(self, codec) + event = Event(self, broker, codec) self.console.event(broker, event) def _handleSchemaResp(self, broker, codec, seq): @@ -505,24 +505,24 @@ class Session: def _decodeValue(self, codec, typecode): """ Decode, from the codec, a value based on its typecode. """ - if typecode == 1: data = codec.read_uint8() # U8 - elif typecode == 2: data = codec.read_uint16() # U16 - elif typecode == 3: data = codec.read_uint32() # U32 - elif typecode == 4: data = codec.read_uint64() # U64 - elif typecode == 6: data = str(codec.read_str8()) # SSTR - elif typecode == 7: data = codec.read_str16() # LSTR - elif typecode == 8: data = codec.read_int64() # ABSTIME - elif typecode == 9: data = codec.read_uint64() # DELTATIME - elif typecode == 10: data = ObjectId(codec) # REF - elif typecode == 11: data = codec.read_uint8() # BOOL - elif typecode == 12: data = codec.read_float() # FLOAT - elif typecode == 13: data = codec.read_double() # DOUBLE - elif typecode == 14: data = codec.read_uuid() # UUID - elif typecode == 15: data = codec.read_map() # FTABLE - elif typecode == 16: data = codec.read_int8() # S8 - elif typecode == 17: data = codec.read_int16() # S16 - elif typecode == 18: data = codec.read_int32() # S32 - elif typecode == 19: data = codec.read_int64() # S63 + if typecode == 1: data = codec.read_uint8() # U8 + elif typecode == 2: data = codec.read_uint16() # U16 + elif typecode == 3: data = codec.read_uint32() # U32 + elif typecode == 4: data = codec.read_uint64() # U64 + elif typecode == 6: data = str(codec.read_str8()) # SSTR + elif typecode == 7: data = codec.read_str16() # LSTR + elif typecode == 8: data = codec.read_int64() # ABSTIME + elif typecode == 9: data = codec.read_uint64() # DELTATIME + elif typecode == 10: data = ObjectId(codec) # REF + elif typecode == 11: data = codec.read_uint8() != 0 # BOOL + elif typecode == 12: data = codec.read_float() # FLOAT + elif typecode == 13: data = codec.read_double() # DOUBLE + elif typecode == 14: data = codec.read_uuid() # UUID + elif typecode == 15: data = codec.read_map() # FTABLE + elif typecode == 16: data = codec.read_int8() # S8 + elif typecode == 17: data = codec.read_int16() # S16 + elif typecode == 18: data = codec.read_int32() # S32 + elif typecode == 19: data = codec.read_int64() # S63 else: raise ValueError("Invalid type code: %d" % typecode) return data @@ -993,13 +993,13 @@ class Broker: """ Get the AMQP session object for this connected broker. """ return self.amqpSession + def getUrl(self): + """ """ + return "%s:%d" % (self.host, self.port) + def __repr__(self): if self.isConnected: - if self.port == 5672: - port = "" - else: - port = ":%d" % self.port - return "Broker connected at: amqp://%s%s" % (self.host, port) + return "Broker connected at: %s" % self.getUrl() else: return "Disconnected Broker" @@ -1171,10 +1171,14 @@ class Agent: def __repr__(self): return "Agent at bank %s (%s)" % (self.bank, self.label) + def getBroker(self): + return self.broker + class Event: """ """ - def __init__(self, session, codec): + def __init__(self, session, broker, codec): self.session = session + self.broker = broker pname = codec.read_str8() cname = codec.read_str8() hash = codec.read_bin128() @@ -1190,7 +1194,14 @@ class Event: self.arguments[arg.name] = session._decodeValue(codec, arg.type) def __repr__(self): - return self.getSyslogText() + if self.schema == None: + return "" + out = strftime("%c", gmtime(self.timestamp / 1000000000)) + out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] + out += " broker=" + self.broker.getUrl() + for arg in self.schema.arguments: + out += " " + arg.name + "=" + self.session._displayValue(self.arguments[arg.name], arg.type) + return out def _sevName(self): if self.severity == 0 : return "EMER " @@ -1218,15 +1229,6 @@ class Event: def getSchema(self): return self.schema - def getSyslogText(self): - if self.schema == None: - return "" - out = strftime("%c", gmtime(self.timestamp / 1000000000)) - out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] - for arg in self.schema.arguments: - out += " " + arg.name + "=" + self.session._displayValue(self.arguments[arg.name], arg.type) - return out - class SequenceManager: """ Manage sequence numbers for asynchronous method calls """ def __init__(self): -- cgit v1.2.1 From 5605be8d83cb6072780525f2183e637135a9004a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 10 Oct 2008 19:24:40 +0000 Subject: QPID-1349 - Push routing for federation (includes hook for dynamic routing) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@703561 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 4dadcd543b..8077e95278 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -150,7 +150,7 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." - res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, 0, 0) + res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, False, False) if res.status == 4: raise Exception("Can't create a durable route on a non-durable link") if _verbose: -- cgit v1.2.1 From db0c3ecf5d011c30288efd48b6b76077b58bcbed Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 10 Oct 2008 22:18:42 +0000 Subject: QPID-1350 - Object reference following in the QMF console API git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@703588 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 64e1df78b0..a7079ef404 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -227,6 +227,7 @@ class Session: _key = - supply a classKey from the list returned by getClasses. _class = - supply a class name as a string. If the class name exists in multiple packages, a _package argument may also be supplied. + _objectId = - get the object referenced by the object-id If objects should be obtained from only one agent, use the following argument. Otherwise, the query will go to all agents. @@ -268,17 +269,20 @@ class Session: pname, cname, hash = None, kwargs["_class"], None if "_package" in kwargs: pname = kwargs["_package"] - if cname == None: - raise Exception("No class supplied, use '_schema', '_key', or '_class' argument") - map = {} - map["_class"] = cname - if pname != None: map["_package"] = pname - if hash != None: map["_hash"] = hash + if cname == None and "_objectId" not in kwargs: + raise Exception("No class supplied, use '_schema', '_key', '_class', or '_objectId' argument") + map = {} self.getSelect = [] - for item in kwargs: - if item[0] != '_': - self.getSelect.append((item, kwargs[item])) + if "_objectId" in kwargs: + map["_objectid"] = kwargs["_objectId"].__repr__() + else: + map["_class"] = cname + if pname != None: map["_package"] = pname + if hash != None: map["_hash"] = hash + for item in kwargs: + if item[0] != '_': + self.getSelect.append((item, kwargs[item])) self.getResult = [] for agent in agentList: @@ -765,7 +769,7 @@ class ObjectId: return 0 def __repr__(self): - return "%d-%d-%d-%d-%x" % (self.getFlags(), self.getSequence(), + return "%d-%d-%d-%d-%d" % (self.getFlags(), self.getSequence(), self.getBroker(), self.getBank(), self.getObject()) def index(self): @@ -863,6 +867,12 @@ class Object(object): return lambda *args, **kwargs : self._invoke(name, args, kwargs) for property, value in self._properties: if name == property.name: + if property.type == 10: # Dereference references + deref = self._session.getObjects(_objectId=value) + if len(deref) != 1: + return None + else: + return deref[0] return value for statistic, value in self._statistics: if name == statistic.name: -- cgit v1.2.1 From 46b5fe1ed0169a36744f542c8f646609a83490f6 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 13 Oct 2008 16:43:10 +0000 Subject: Allow management clienst to specify transport to use for inter-broker links git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@704159 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 8077e95278..d7140d5e4b 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -50,6 +50,7 @@ _verbose = False _quiet = False _durable = False _dellink = False +_transport = "tcp" class RouteManager: def __init__ (self, destBroker): @@ -84,8 +85,9 @@ class RouteManager: mech = "ANONYMOUS" else: mech = "PLAIN" - res = broker.connect(self.src.host, self.src.port, False, _durable, - mech, self.src.authName, self.src.authPass) + res = broker.connect(self.src.host, self.src.port, _durable, + mech, self.src.authName, self.src.authPass, + _transport) if _verbose: print "Connect method returned:", res.status, res.text link = self.getLink() @@ -131,8 +133,9 @@ class RouteManager: mech = "ANONYMOUS" else: mech = "PLAIN" - res = broker.connect(self.src.host, self.src.port, False, _durable, - mech, self.src.authName, self.src.authPass) + res = broker.connect(self.src.host, self.src.port, _durable, + mech, self.src.authName, self.src.authPass, + _transport) if _verbose: print "Connect method returned:", res.status, res.text link = self.getLink() -- cgit v1.2.1 From 0afd3e14836ad0f2aa354e453edc7fed0a6a1d9b Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 13 Oct 2008 19:38:16 +0000 Subject: Adjust sizes in tests for change to timestamp setting git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@704213 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index 38f87d8526..cbcef5602f 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -369,7 +369,7 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 27 + msg_size = 19 #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") @@ -438,7 +438,7 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "abcdefgh")) #each message is currently interpreted as requiring msg_size bytes of credit - msg_size = 27 + msg_size = 19 #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") -- cgit v1.2.1 From 8f2c6cb7b7c58fdeebd759e310c97d35c4265d08 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 15 Oct 2008 10:03:49 +0000 Subject: c++ broker: Don't hold on to delivery records for accepted/released messages unless required due to being in windowing mode. python client: Modified start() on incoming queue to setthe flow mode as credit (not windowing) git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@704838 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 1 + 1 file changed, 1 insertion(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 2f70461ab6..f500ab112f 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -344,6 +344,7 @@ class Incoming(Queue): self.destination = destination def start(self): + self.session.message_set_flow_mode(self.destination, self.session.flow_mode.credit) for unit in self.session.credit_unit.values(): self.session.message_flow(self.destination, unit, 0xFFFFFFFF) -- cgit v1.2.1 From 658c2efbccea313fc9d8ec4ee658d6e8b30ac61a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 15 Oct 2008 15:36:32 +0000 Subject: QPID-1350 - changed syntax of dereference so it becomes optional git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@704942 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index a7079ef404..5e1df10e5b 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -867,13 +867,13 @@ class Object(object): return lambda *args, **kwargs : self._invoke(name, args, kwargs) for property, value in self._properties: if name == property.name: - if property.type == 10: # Dereference references - deref = self._session.getObjects(_objectId=value) - if len(deref) != 1: - return None - else: - return deref[0] return value + if name == "_" + property.name + "_" and property.type == 10: # Dereference references + deref = self._session.getObjects(_objectId=value) + if len(deref) != 1: + return None + else: + return deref[0] for statistic, value in self._statistics: if name == statistic.name: return value -- cgit v1.2.1 From 978185bafb2b08d89c0b675263dd2b1f715c9e69 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 15 Oct 2008 15:51:15 +0000 Subject: QPID-1360 - Scaling improvements for QMF git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@704944 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-queue-stats | 195 +++++++++++++++------------------- qpid/python/qpid/management.py | 69 ++++++------ qpid/python/qpid/qmfconsole.py | 145 ++++++++++++++++--------- 3 files changed, 214 insertions(+), 195 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 98dfa7580a..c29cab3568 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -26,120 +26,96 @@ import re import socket import qpid from threading import Condition -from qpid.management import managementClient -from qpid.managementdata import Broker +from qpid.qmfconsole import Session, Console from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed from qpid.util import connect from time import sleep -class mgmtObject (object): - """ Generic object that holds the contents of a management object with its - attributes set as object attributes. """ - - def __init__ (self, classKey, timestamps, row): - self.classKey = classKey - self.timestamps = timestamps - for cell in row: - setattr (self, cell[0], cell[1]) - - - -class BrokerManager: - def __init__ (self): - self.dest = None - self.src = None - self.broker = None - self.objects = {} - self.filter = None - - def SetBroker (self, broker): - self.broker = broker - - def ConnectToBroker (self): - try: - self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection (connect (self.broker.host, self.broker.port), - username=self.broker.username, password=self.broker.password) - self.conn.start () - self.session = self.conn.session(self.sessionId) - self.mclient = managementClient (self.conn.spec, None, self.configCb, self.instCb) - self.mchannel = self.mclient.addChannel (self.session) - except socket.error, e: - print "Socket Error %s - %s" % (e[0], e[1]) - sys.exit (1) - except Closed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit (1) - except ConnectionFailed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit(1) - - def setFilter(self,filter): - self.filter = filter - - def Disconnect (self): - self.mclient.removeChannel (self.mchannel) - self.session.close(timeout=10) - self.conn.close(timeout=10) - - def configCb (self, context, classKey, row, timestamps): - className = classKey[1] - if className != "queue": - return - - obj = mgmtObject (classKey, timestamps, row) - if obj.id not in self.objects: - self.objects[obj.id] = (obj.name, None, None) - - def instCb (self, context, classKey, row, timestamps): - className = classKey[1] - if className != "queue": - return - - obj = mgmtObject (classKey, timestamps, row) - if obj.id not in self.objects: - return - - (name, first, last) = self.objects[obj.id] - if first == None: - self.objects[obj.id] = (name, obj, None) - return - - if len(self.filter) > 0 : - match = False +class BrokerManager(Console): + def __init__(self, host): + self.url = host + self.objects = {} + self.filter = None + self.session = Session(self, rcvEvents=False, rcvHeartbeats=False, userBindings=True) + try: + self.broker = self.session.addBroker(self.url) + except socket.error, e: + print "Socket Error %s - %s" % (e[0], e[1]) + sys.exit (1) + except Closed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit (1) + except ConnectionFailed, e: + print "Connect Failed %d - %s" % (e[0], e[1]) + sys.exit(1) + + def setFilter(self,filter): + self.filter = filter + + def objectProps(self, broker, record): + className = record.getClassKey()[1] + if className != "queue": + return + + id = record.getObjectId().__repr__() + if id not in self.objects: + self.objects[id] = (record.name, None, None) + + def objectStats(self, broker, record): + className = record.getClassKey()[1] + if className != "queue": + return + + id = record.getObjectId().__repr__() + if id not in self.objects: + return + + (name, first, last) = self.objects[id] + if first == None: + self.objects[id] = (name, record, None) + return + + if len(self.filter) > 0 : + match = False - for x in self.filter: - if x.match(name): - match = True - break - if match == False: - return - - if last == None: - lastSample = first - else: - lastSample = last - - self.objects[obj.id] = (name, first, obj) - - deltaTime = float (obj.timestamps[0] - lastSample.timestamps[0]) - enqueueRate = float (obj.msgTotalEnqueues - lastSample.msgTotalEnqueues) / (deltaTime / 1000000000.0) - dequeueRate = float (obj.msgTotalDequeues - lastSample.msgTotalDequeues) / (deltaTime / 1000000000.0) - print "%-41s%10.2f%11d%13.2f%13.2f" % \ - (name, deltaTime / 1000000000, obj.msgDepth, enqueueRate, dequeueRate) - - - def Display (self): - self.ConnectToBroker () - print "Queue Name Sec Depth Enq Rate Deq Rate" - print "========================================================================================" - try: - while True: - sleep (1) - except KeyboardInterrupt: - pass - self.Disconnect () + for x in self.filter: + if x.match(name): + match = True + break + if match == False: + return + + if last == None: + lastSample = first + else: + lastSample = last + + self.objects[id] = (name, first, record) + + deltaTime = float (record.getTimestamps()[0] - lastSample.getTimestamps()[0]) + enqueueRate = float (record.msgTotalEnqueues - lastSample.msgTotalEnqueues) / \ + (deltaTime / 1000000000.0) + dequeueRate = float (record.msgTotalDequeues - lastSample.msgTotalDequeues) / \ + (deltaTime / 1000000000.0) + print "%-41s%10.2f%11d%13.2f%13.2f" % \ + (name, deltaTime / 1000000000, record.msgDepth, enqueueRate, dequeueRate) + + + def Display (self): + classes = self.session.getClasses("org.apache.qpid.broker") + for cls in classes: + if cls[1] == "queue": + queueClassKey = cls + self.session.bindClass(queueClassKey) + print "Queue Name Sec Depth Enq Rate Deq Rate" + print "========================================================================================" + try: + while True: + sleep (1) + except KeyboardInterrupt: + print + self.session.delBroker(self.broker) ## ## Main Program @@ -157,8 +133,7 @@ def main(): for s in options.filter.split(","): filter.append(re.compile(s)) - bm = BrokerManager () - bm.SetBroker (Broker (host)) + bm = BrokerManager(host) bm.setFilter(filter) bm.Display() diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index e6ff5852f2..485b64b99f 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -162,8 +162,12 @@ class managementChannel: ssn.exchange_bind (exchange="amq.direct", queue=self.replyName, binding_key=self.replyName) - ssn.message_subscribe (queue=self.topicName, destination="tdest") - ssn.message_subscribe (queue=self.replyName, destination="rdest") + ssn.message_subscribe (queue=self.topicName, destination="tdest", + accept_mode=ssn.accept_mode.none, + acquire_mode=ssn.acquire_mode.pre_acquired) + ssn.message_subscribe (queue=self.replyName, destination="rdest", + accept_mode=ssn.accept_mode.none, + acquire_mode=ssn.acquire_mode.pre_acquired) ssn.incoming ("tdest").listen (self.topicCb, self.exceptionCb) ssn.incoming ("rdest").listen (self.replyCb) @@ -202,9 +206,6 @@ class managementChannel: if self.enabled: self.qpidChannel.message_transfer (destination=exchange, message=msg) - def accept (self, msg): - self.qpidChannel.message_accept(RangedSet(msg.id)) - def message (self, body, routing_key="broker"): dp = self.qpidChannel.delivery_properties() dp.routing_key = routing_key @@ -349,28 +350,27 @@ class managementClient: def topicCb (self, ch, msg): """ Receive messages via the topic queue of a particular channel. """ codec = Codec (self.spec, msg.body) - hdr = self.checkHeader (codec) - if hdr == None: - raise ValueError ("outer header invalid"); + while True: + hdr = self.checkHeader (codec) + if hdr == None: + return - if hdr[0] == 'p': - self.handlePackageInd (ch, codec) - elif hdr[0] == 'q': - self.handleClassInd (ch, codec) - elif hdr[0] == 'h': - self.handleHeartbeat (ch, codec) - elif hdr[0] == 'e': - self.handleEvent (ch, codec) - else: - self.parse (ch, codec, hdr[0], hdr[1]) - ch.accept(msg) + if hdr[0] == 'p': + self.handlePackageInd (ch, codec) + elif hdr[0] == 'q': + self.handleClassInd (ch, codec) + elif hdr[0] == 'h': + self.handleHeartbeat (ch, codec) + elif hdr[0] == 'e': + self.handleEvent (ch, codec) + else: + self.parse (ch, codec, hdr[0], hdr[1]) def replyCb (self, ch, msg): """ Receive messages via the reply queue of a particular channel. """ codec = Codec (self.spec, msg.body) hdr = self.checkHeader (codec) if hdr == None: - ch.accept(msg) return if hdr[0] == 'm': @@ -385,7 +385,6 @@ class managementClient: self.handleClassInd (ch, codec) else: self.parse (ch, codec, hdr[0], hdr[1]) - ch.accept(msg) def exceptCb (self, ch, data): if self.closeCb != None: @@ -403,20 +402,22 @@ class managementClient: codec.write_uint32 (seq) def checkHeader (self, codec): - """ Check the header of a management message and extract the opcode and - class. """ - octet = chr (codec.read_uint8 ()) - if octet != 'A': - return None - octet = chr (codec.read_uint8 ()) - if octet != 'M': - return None - octet = chr (codec.read_uint8 ()) - if octet != '2': + """ Check the header of a management message and extract the opcode and class. """ + try: + octet = chr (codec.read_uint8 ()) + if octet != 'A': + return None + octet = chr (codec.read_uint8 ()) + if octet != 'M': + return None + octet = chr (codec.read_uint8 ()) + if octet != '2': + return None + opcode = chr (codec.read_uint8 ()) + seq = codec.read_uint32 () + return (opcode, seq) + except: return None - opcode = chr (codec.read_uint8 ()) - seq = codec.read_uint32 () - return (opcode, seq) def encodeValue (self, codec, value, typecode): """ Encode, into the codec, a value based on its typecode. """ diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 5e1df10e5b..ed4565dac4 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -62,11 +62,11 @@ class Console: """ Invoked when a QMF agent disconects. """ pass - def objectProps(self, broker, id, record): + def objectProps(self, broker, record): """ Invoked when an object is updated. """ pass - def objectStats(self, broker, id, record): + def objectStats(self, broker, record): """ Invoked when an object is updated. """ pass @@ -111,10 +111,10 @@ class Session: _CONTEXT_STARTUP = 2 _CONTEXT_MULTIGET = 3 - GET_WAIT_TIME = 10 + GET_WAIT_TIME = 60 def __init__(self, console=None, rcvObjects=True, rcvEvents=True, rcvHeartbeats=True, - manageConnections=False): + manageConnections=False, userBindings=False): """ Initialize a session. If the console argument is provided, the more advanced asynchronous features are available. If console is @@ -131,6 +131,12 @@ class Session: If manageConnections is set to False, the user is responsible for handing failures. In this case, an unreachable broker will cause addBroker to raise an exception. + + If userBindings is set to False (the default) and rcvObjects is True, the console will + receive data for all object classes. If userBindings is set to True, the user must select + which classes the console shall receive by invoking the bindPackage or bindClass methods. + This allows the console to be configured to receive only information that is relavant to + a particular application. If rcvObjects id False, userBindings has no meaning. """ self.console = console self.brokers = [] @@ -141,14 +147,21 @@ class Session: self.getResult = [] self.getSelect = [] self.error = None + self.rcvObjects = rcvObjects + self.rcvEvents = rcvEvents + self.rcvHeartbeats = rcvHeartbeats + self.userBindings = userBindings if self.console == None: - rcvObjects = False - rcvEvents = False - rcvHeartbeats = False - self.bindingKeyList = self._bindingKeys(rcvObjects, rcvEvents, rcvHeartbeats) + self.rcvObjects = False + self.rcvEvents = False + self.rcvHeartbeats = False + self.bindingKeyList = self._bindingKeys() self.manageConnections = manageConnections - if (manageConnections): + if self.userBindings and not self.rcvObjects: + raise Exception("userBindings can't be set unless rcvObjects is set and a console is provided") + + if manageConnections: raise Exception("manageConnections - not yet implemented") def __repr__(self): @@ -200,6 +213,23 @@ class Session: if (cname, hash) in self.packages[pname]: return self.packages[pname][(cname, hash)] + def bindPackage(self, packageName): + """ """ + if not self.userBindings or not self.rcvObjects: + raise Exception("userBindings option not set for Session") + for broker in self.brokers: + broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, + binding_key="console.obj.%s" % packageName) + + def bindClass(self, classKey): + """ """ + if not self.userBindings or not self.rcvObjects: + raise Exception("userBindings option not set for Session") + pname, cname, hash = classKey + for broker in self.brokers: + broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, + binding_key="console.obj.%s.%s" % (pname, cname)) + def getAgents(self, broker=None): """ Get a list of currently known agents """ brokerList = [] @@ -322,20 +352,19 @@ class Session: """ """ pass - def _bindingKeys(self, rcvObjects, rcvEvents, rcvHeartbeats): + def _bindingKeys(self): keyList = [] keyList.append("schema.#") - if rcvObjects and rcvEvents and rcvHeartbeats: + if self.rcvObjects and self.rcvEvents and self.rcvHeartbeats and not self.userBindings: keyList.append("console.#") else: - if rcvObjects: - keyList.append("console.prop.#") - keyList.append("console.stat.#") + if self.rcvObjects and not self.userBindings: + keyList.append("console.obj.#") else: - keyList.append("console.prop.org.apache.qpid.broker.agent") - if rcvEvents: + keyList.append("console.obj.org.apache.qpid.broker.agent") + if self.rcvEvents: keyList.append("console.event.#") - if rcvHeartbeats: + if self.rcvHeartbeats: keyList.append("console.heartbeat") return keyList @@ -488,9 +517,9 @@ class Session: if self.console != None: if prop: - self.console.objectProps(broker, object.getObjectId(), object) + self.console.objectProps(broker, object) if stat: - self.console.objectStats(broker, object.getObjectId(), object) + self.console.objectStats(broker, object) def _handleError(self, error): self.error = error @@ -858,6 +887,15 @@ class Object(object): def getStatistics(self): return self._statistics + def mergeUpdate(self, newer): + """ Replace properties and/or statistics with a newly received update """ + if self._objectId != newer._objectId: + raise Exception("Objects with different object-ids") + if len(newer.getProperties()) > 0: + self.properties = newer.getProperties() + if len(newer.getStatistics()) > 0: + self.statistics = newer.getStatistics() + def __repr__(self): return self.getIndex() @@ -960,7 +998,7 @@ class MethodResult(object): class Broker: """ """ - SYNC_TIME = 10 + SYNC_TIME = 60 def __init__(self, session, host, port, authMech, authUser, authPass): self.session = session @@ -1024,7 +1062,9 @@ class Broker: self.amqpSession.queue_declare(queue=self.replyName, exclusive=True, auto_delete=True) self.amqpSession.exchange_bind(exchange="amq.direct", queue=self.replyName, binding_key=self.replyName) - self.amqpSession.message_subscribe(queue=self.replyName, destination="rdest") + self.amqpSession.message_subscribe(queue=self.replyName, destination="rdest", + accept_mode=self.amqpSession.accept_mode.none, + acquire_mode=self.amqpSession.acquire_mode.pre_acquired) self.amqpSession.incoming("rdest").listen(self._replyCb, self._exceptionCb) self.amqpSession.message_set_flow_mode(destination="rdest", flow_mode=1) self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFF) @@ -1032,7 +1072,9 @@ class Broker: self.topicName = "topic-%s" % self.amqpSessionId self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) - self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest") + self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest", + accept_mode=self.amqpSession.accept_mode.none, + acquire_mode=self.amqpSession.acquire_mode.pre_acquired) self.amqpSession.incoming("tdest").listen(self._replyCb) self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) @@ -1076,18 +1118,21 @@ class Broker: def _checkHeader(self, codec): """ Check the header of a management message and extract the opcode and class. """ - octet = chr(codec.read_uint8()) - if octet != 'A': - return None, None - octet = chr(codec.read_uint8()) - if octet != 'M': - return None, None - octet = chr(codec.read_uint8()) - if octet != '2': + try: + octet = chr(codec.read_uint8()) + if octet != 'A': + return None, None + octet = chr(codec.read_uint8()) + if octet != 'M': + return None, None + octet = chr(codec.read_uint8()) + if octet != '2': + return None, None + opcode = chr(codec.read_uint8()) + seq = codec.read_uint32() + return opcode, seq + except: return None, None - opcode = chr(codec.read_uint8()) - seq = codec.read_uint32() - return opcode, seq def _message (self, body, routing_key="broker"): dp = self.amqpSession.delivery_properties() @@ -1143,23 +1188,21 @@ class Broker: self.cv.release() def _replyCb(self, msg): - self.amqpSession.message_accept(RangedSet(msg.id)) codec = Codec(self.conn.spec, msg.body) - opcode, seq = self._checkHeader(codec) - if opcode == None: - return - - if opcode == 'b': self.session._handleBrokerResp (self, codec, seq) - elif opcode == 'p': self.session._handlePackageInd (self, codec, seq) - elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) - elif opcode == 'q': self.session._handleClassInd (self, codec, seq) - elif opcode == 'm': self.session._handleMethodResp (self, codec, seq) - elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq) - elif opcode == 'e': self.session._handleEventInd (self, codec, seq) - elif opcode == 's': self.session._handleSchemaResp (self, codec, seq) - elif opcode == 'c': self.session._handleContentInd (self, codec, seq, prop=True) - elif opcode == 'i': self.session._handleContentInd (self, codec, seq, stat=True) - elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) + while True: + opcode, seq = self._checkHeader(codec) + if opcode == None: return + if opcode == 'b': self.session._handleBrokerResp (self, codec, seq) + elif opcode == 'p': self.session._handlePackageInd (self, codec, seq) + elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) + elif opcode == 'q': self.session._handleClassInd (self, codec, seq) + elif opcode == 'm': self.session._handleMethodResp (self, codec, seq) + elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq) + elif opcode == 'e': self.session._handleEventInd (self, codec, seq) + elif opcode == 's': self.session._handleSchemaResp (self, codec, seq) + elif opcode == 'c': self.session._handleContentInd (self, codec, seq, prop=True) + elif opcode == 'i': self.session._handleContentInd (self, codec, seq, stat=True) + elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) def _exceptionCb(self, data): self.isConnected = False @@ -1286,10 +1329,10 @@ class DebugConsole(Console): def delAgent(self, agent): print "delAgent:", agent - def objectProps(self, broker, id, record): + def objectProps(self, broker, record): print "objectProps:", record.getClassKey() - def objectStats(self, broker, id, record): + def objectStats(self, broker, record): print "objectStats:", record.getClassKey() def event(self, broker, event): -- cgit v1.2.1 From 875ff415fe9760f1c8c5454ae6657da81af059fe Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 20 Oct 2008 15:27:37 +0000 Subject: Allow transport to be set on qpid-route. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@706320 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 16 ++++++++++------ qpid/python/qpid/qmfconsole.py | 10 +++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index d7140d5e4b..8a779bb7a3 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -40,6 +40,8 @@ def Usage (): print " -q [ --quiet ] Quiet output, don't print duplicate warnings" print " -d [ --durable ] Added configuration shall be durable" print " -e [ --del-empty-link ] Delete link after deleting last route on the link" + print " -t [ --transport ]" + print " Specify transport to use for links, defaults to tcp" print print " dest-broker and src-broker are in the form: [username/password@] hostname | ip-address [:]" print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" @@ -65,13 +67,13 @@ class RouteManager: def getLink (self): links = self.qmf.getObjects(_class="link") for link in links: - if "%s:%d" % (link.host, link.port) == self.src.name (): + if self.src.match(link.host, link.port): return link return None def AddLink (self, srcBroker): self.src = qmfconsole.BrokerURL(srcBroker) - if self.dest.name() == self.src.name(): + if self.dest.match(self.src.host, self.src.port): print "Linking broker to itself is not permitted" sys.exit(1) @@ -118,7 +120,7 @@ class RouteManager: def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes): self.src = qmfconsole.BrokerURL(srcBroker) - if self.dest.name() == self.src.name(): + if self.dest.match(self.src.host, self.src.port): raise Exception("Linking broker to itself is not permitted") brokers = self.qmf.getObjects(_class="broker") @@ -241,8 +243,8 @@ def YN(val): ## try: - longOpts = ("verbose", "quiet", "durable", "del-empty-link") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "vqde", longOpts) + longOpts = ("verbose", "quiet", "durable", "del-empty-link", "transport=") + (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "vqdet:", longOpts) except: Usage () @@ -255,6 +257,8 @@ for opt in optlist: _durable = True if opt[0] == "-e" or opt[0] == "--del-empty-link": _dellink = True + if opt[0] == "-t" or opt[0] == "--transport": + _transport = opt[1] nargs = len (cargs) if nargs < 2: @@ -304,7 +308,7 @@ try: else: Usage () except Exception,e: - print "Failed:", e.message + print "Failed:", e.args[0] sys.exit(1) rm.Disconnect () diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index ed4565dac4..bfa48c7540 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -91,7 +91,8 @@ class BrokerURL: if not match: raise ValueError("'%s' is not a valid broker url" % (text)) user, password, host, port = match.groups() - self.host = socket.gethostbyname(host) + socket.gethostbyname(host) + self.host = host if port: self.port = int(port) else: self.port = 5672 self.authName = user or "guest" @@ -101,6 +102,9 @@ class BrokerURL: def name(self): return self.host + ":" + str(self.port) + def match(self, host, port): + return socket.gethostbyname(self.host) == socket.gethostbyname(host) and self.port == port + class Session: """ An instance of the Session class represents a console session running @@ -784,8 +788,8 @@ class ObjectId: self.first = first self.second = second - def __cmp__(self, other): - if other == None: + def __cmp__(self, other): + if other == None or not isinstance(other, ObjectId) : return 1 if self.first < other.first: return -1 -- cgit v1.2.1 From f1a80c1b4fc685331e709788a71112afd1855bf9 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 23 Oct 2008 19:07:05 +0000 Subject: Marked old python management API as obsolete. Updated 0-10 tests to use new qmfconsole API git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707449 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 4 ++++ qpid/python/qpid/managementdata.py | 5 +++++ qpid/python/tests_0-10/management.py | 31 ++++++++----------------------- 3 files changed, 17 insertions(+), 23 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 485b64b99f..fd60ac22cf 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -17,6 +17,10 @@ # under the License. # +############################################################################### +## This file is being obsoleted by qpid/qmfconsole.py +############################################################################### + """ Management API for Qpid """ diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 2bf66a5e5b..5bceb48b7d 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -19,6 +19,11 @@ # under the License. # + +############################################################################### +## This file is being obsoleted by qpid/qmfconsole.py +############################################################################### + import qpid import re import socket diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index efec2b8a92..a47982173b 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -72,39 +72,25 @@ class ManagementTest (TestBase010): self.assertEqual (res.body, body) def test_system_object (self): - session = self.session - - mc = managementClient (session.spec) - mch = mc.addChannel (session) - - mc.syncWaitForStable (mch) - systems = mc.syncGetObjects (mch, "system") + self.startQmf() + systems = self.qmf.getObjects(_class="system") self.assertEqual (len (systems), 1) - mc.removeChannel (mch) def test_self_session_id (self): - session = self.session - - mc = managementClient (session.spec) - mch = mc.addChannel (session) + self.startQmf() + sessionId = self.qmf_broker.getSessionId() + brokerSessions = self.qmf.getObjects(_class="session") - info = mc.syncWaitForStable (mch) - brokerSessions = mc.syncGetObjects (mch, "session") found = False for bs in brokerSessions: - if bs.name == info.sessionId: + if bs.name == sessionId: found = True self.assertEqual (found, True) - mc.removeChannel (mch) def test_standard_exchanges (self): - session = self.session - - mc = managementClient (session.spec) - mch = mc.addChannel (session) + self.startQmf() - mc.syncWaitForStable (mch) - exchanges = mc.syncGetObjects (mch, "exchange") + exchanges = self.qmf.getObjects(_class="exchange") exchange = self.findExchange (exchanges, "") self.assertEqual (exchange.type, "direct") exchange = self.findExchange (exchanges, "amq.direct") @@ -117,7 +103,6 @@ class ManagementTest (TestBase010): self.assertEqual (exchange.type, "headers") exchange = self.findExchange (exchanges, "qpid.management") self.assertEqual (exchange.type, "topic") - mc.removeChannel (mch) def findExchange (self, exchanges, name): for exchange in exchanges: -- cgit v1.2.1 From b68eb071da901cacb742132a5ff323998040b3b7 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 23 Oct 2008 20:29:58 +0000 Subject: QPID-1348 - Reference implementation of dynamic binding git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707468 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/models/fedsim/__init__.py | 1 + qpid/python/models/fedsim/fedsim.py | 434 +++++++++++++++++++++++++++++++ qpid/python/models/fedsim/testBig.py | 88 +++++++ qpid/python/models/fedsim/testRing.py | 48 ++++ qpid/python/models/fedsim/testStar.py | 65 +++++ qpid/python/models/fedsim/testStarAdd.py | 56 ++++ 6 files changed, 692 insertions(+) create mode 100644 qpid/python/models/fedsim/__init__.py create mode 100644 qpid/python/models/fedsim/fedsim.py create mode 100644 qpid/python/models/fedsim/testBig.py create mode 100644 qpid/python/models/fedsim/testRing.py create mode 100644 qpid/python/models/fedsim/testStar.py create mode 100644 qpid/python/models/fedsim/testStarAdd.py (limited to 'qpid/python') diff --git a/qpid/python/models/fedsim/__init__.py b/qpid/python/models/fedsim/__init__.py new file mode 100644 index 0000000000..792d600548 --- /dev/null +++ b/qpid/python/models/fedsim/__init__.py @@ -0,0 +1 @@ +# diff --git a/qpid/python/models/fedsim/fedsim.py b/qpid/python/models/fedsim/fedsim.py new file mode 100644 index 0000000000..edb6c4c8ed --- /dev/null +++ b/qpid/python/models/fedsim/fedsim.py @@ -0,0 +1,434 @@ +# +# 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. +# + +class Sim: + def __init__(self): + self.brokers = {} + self.clients = {} + self.errors = 0 + self.warnings = 0 + + def error(self, text): + self.errors += 1 + print "###### Error:", text + + def warning(self, text): + self.warnings += 1 + print "###### Warning:", text + + def end(self): + print "========================" + print "Errors: %d, Warnings: %d" % (self.errors, self.warnings) + print "========================" + + def dumpState(self): + print "============================" + print "===== Federation State =====" + print "============================" + for broker in self.brokers: + for exchange in self.brokers[broker].exchanges: + print "Exchange %s.%s" % (broker, exchange) + for key in self.brokers[broker].exchanges[exchange].keys: + print " Key %s" % key + for queue in self.brokers[broker].exchanges[exchange].keys[key]: + print " Queue %s origins=%s" % \ + (queue.name, self.brokers[broker].exchanges[exchange].keys[key][queue].originList) + + def addBroker(self, name): + if name in self.brokers: + raise Exception("Broker of same name already exists") + broker = Broker(self, name) + self.brokers[name] = broker + return broker + + def addClient(self, name, broker): + if name in self.clients: + raise Exception("Client of same name already exists") + client = Client(self, name, broker) + self.clients[name] = client + return client + + def link(self, left, right, bidir=True): + print "====== link %s to %s, bidir=%s" % (left.tag, right.tag, bidir) + l1 = left.createLink(right) + l1.bridge("amq.direct") + if bidir: + l2 = right.createLink(left) + l2.bridge("amq.direct") + + def bind(self, client, key): + print "====== bind Client(%s): k=%s" % (client.name, key) + client.bind(key) + + def unbind(self, client, key): + print "====== unbind Client(%s): k=%s" % (client.name, key) + client.unbind(key) + + def sendMessage(self, key, broker, body="Message Body"): + print "====== sendMessage: broker=%s k=%s" % (broker.tag, key) + msg = Message(key, body) + exchange = broker.exchanges["amq.direct"] + for client in self.clients: + self.clients[client].expect(key); + exchange.receive(key, msg, True) + for client in self.clients: + self.clients[client].checkReception() + + +class Destination: + def receive(self, key, msg, fromUser=False): + pass + + +class Client(Destination): + def __init__(self, sim, name, broker): + self.sim = sim + self.name = name + self.broker = broker + self.broker.connect(self) + self.queue = self.broker.declare_queue(name) + self.subscription = self.broker.subscribe(self, name) + self.expected = None + self.boundKeys = [] + + def bind(self, key): + self.boundKeys.append(key) + self.broker.bind("amq.direct", self.name, key) + + def unbind(self, key): + self.boundKeys.remove(key) + self.broker.unbind("amq.direct", self.name, key) + + def receive(self, key, msg, fromUser=False): + print "Client(%s) received [%s]: %s" % (self.name, key, msg.body) + if self.expected == key: + self.expected = None + else: + self.sim.error("Client(%s) received unexpected message with key [%s]" % \ + (self.name, self.expected)) + + def expect(self, key): + if key in self.boundKeys: + self.expected = key + + def checkReception(self): + if self.expected: + self.sim.error("Client(%s) never received message with key [%s]" % \ + (self.name, self.expected)) + +class Broker(Client): + def __init__(self, sim, tag): + self.sim = sim + self.tag = tag + self.connections = {} + self.exchanges = {} + self.queues = {} + self.subscriptions = {} + self.links = {} + self.directExchange = self.declare_exchange("amq.direct") + + def connect(self, client): + if client in self.connections: + raise Exception("Client already connected") + self.connections[client] = Connection(client) + + def declare_queue(self, name, tag=None, exclude=None): + if name in self.queues: + raise Exception("Queue already exists") + self.queues[name] = Queue(self, name, tag, exclude) + + def subscribe(self, dest, queueName): + if queueName not in self.queues: + raise Exception("Queue does not exist") + self.queues[queueName].setDest(dest) + + def declare_exchange(self, name): + if name in self.exchanges: + return + exchange = Exchange(self, name) + self.exchanges[name] = exchange + return exchange + + def bind(self, exchangeName, queueName, key, tagList=[], fedOp=None, origin=None): + if exchangeName not in self.exchanges: + raise Exception("Exchange not found") + if queueName not in self.queues: + raise Exception("Queue not found") + exchange = self.exchanges[exchangeName] + queue = self.queues[queueName] + exchange.bind(queue, key, tagList, fedOp, origin) + + def unbind(self, exchangeName, queueName, key): + if exchangeName not in self.exchanges: + raise Exception("Exchange not found") + if queueName not in self.queues: + raise Exception("Queue not found") + exchange = self.exchanges[exchangeName] + queue = self.queues[queueName] + exchange.unbind(queue, key) + + def createLink(self, other): + if other in self.links: + raise Exception("Peer broker already linked") + link = Link(self, other) + self.links[other] = link + return link + + +class Connection: + def __init__(self, client): + self.client = client + + +class Exchange(Destination): + def __init__(self, broker, name): + self.broker = broker + self.sim = broker.sim + self.name = name + self.keys = {} + self.bridges = [] + + def bind(self, queue, key, tagList, fedOp, origin): + if not fedOp: fedOp = "bind" + print "Exchange(%s.%s) bind q=%s, k=%s, tags=%s, op=%s, origin=%s" % \ + (self.broker.tag, self.name, queue.name, key, tagList, fedOp, origin), + + if self.broker.tag in tagList: + print "(tag ignored)" + return + + if fedOp == "bind" or fedOp == "unbind": + if key not in self.keys: + self.keys[key] = {} + queueMap = self.keys[key] + + if fedOp == "bind": + ## + ## Add local or federation binding case + ## + if queue in queueMap: + if origin and origin in queueMap[queue].originList: + print "(dup ignored)" + elif origin: + queueMap[queue].originList.append(origin) + print "(origin added)" + else: + binding = Binding(origin) + queueMap[queue] = binding + print "(binding added)" + + elif fedOp == "unbind": + ## + ## Delete federation binding case + ## + if queue in queueMap: + binding = queueMap[queue] + if origin and origin in binding.originList: + binding.originList.remove(origin) + if len(binding.originList) == 0: + queueMap.pop(queue) + if len(queueMap) == 0: + self.keys.pop(key) + print "(last origin del)" + else: + print "(removed origin)" + else: + print "(origin not found)" + else: + print "(queue not found)" + + elif fedOp == "reorigin": + print "(ok)" + self.reorigin() + + elif fedOp == "hello": + print "(ok)" + + else: + raise Exception("Unknown fed-opcode '%s'" % fedOp) + + newTagList = [] + newTagList.append(self.broker.tag) + for tag in tagList: + newTagList.append(tag) + if origin: + propOrigin = origin + else: + propOrigin = self.broker.tag + + for bridge in self.bridges: + if bridge.isDynamic(): + bridge.propagate(key, newTagList, fedOp, propOrigin) + + def reorigin(self): + myTag = [] + myTag.append(self.broker.tag) + for key in self.keys: + queueMap = self.keys[key] + found = False + for queue in queueMap: + binding = queueMap[queue] + if binding.isLocal(): + found = True + if found: + for bridge in self.bridges: + if bridge.isDynamic(): + bridge.propagate(key, myTag, "bind", self.broker.tag) + + def unbind(self, queue, key): + print "Exchange(%s.%s) unbind q=%s, k=%s" % (self.broker.tag, self.name, queue.name, key), + if key not in self.keys: + print "(key not known)" + return + queueMap = self.keys[key] + if queue not in queueMap: + print "(queue not bound)" + return + queueMap.pop(queue) + if len(queueMap) == 0: + self.keys.pop(key) + print "(ok, remove bound-key)" + else: + print "(ok)" + + count = 0 + for queue in queueMap: + if len(queueMap[queue].originList) == 0: + count += 1 + + if count == 0: + myTag = [] + myTag.append(self.broker.tag) + for bridge in self.bridges: + if bridge.isDynamic(): + bridge.propagate(key, myTag, "unbind", self.broker.tag) + + def receive(self, key, msg, fromUser=False): + sent = False + if key in self.keys: + queueMap = self.keys[key] + for queue in queueMap: + if queue.enqueue(msg): + sent = True + if not sent and not fromUser: + self.sim.warning("Exchange(%s.%s) received unroutable message: k=%s" % \ + (self.broker.tag, self.name, key)) + + def addDynamicBridge(self, bridge): + if bridge in self.bridges: + raise Exception("Dynamic bridge already added to exchange") + self.bridges.append(bridge) + + for b in self.bridges: + if b != bridge: + b.sendReorigin() + self.reorigin() + +class Queue: + def __init__(self, broker, name, tag=None, exclude=None): + self.broker = broker + self.name = name + self.tag = tag + self.exclude = exclude + self.dest = None + + def setDest(self, dest): + self.dest = dest + + def enqueue(self, msg): + print "Queue(%s.%s) rcvd k=%s, tags=%s" % (self.broker.tag, self.name, msg.key, msg.tags), + if self.dest == None: + print "(dropped, no dest)" + return False + if self.exclude and msg.tagFound(self.exclude): + print "(dropped, tag)" + return False + if self.tag: + msg.appendTag(self.tag) + print "(ok)" + self.dest.receive(msg.key, msg) + return True + + +class Binding: + def __init__(self, origin): + self.originList = [] + if origin: + self.originList.append(origin) + + def isLocal(self): + return len(self.originList) == 0 + + +class Link: + def __init__(self, local, remote): + self.local = local + self.remote = remote + self.remote.connect(self) + self.bridges = [] + + def bridge(self, exchangeName): + bridge = Bridge(self, exchangeName) + + +class Bridge: + def __init__(self, link, exchangeName): + self.link = link + self.exchangeName = exchangeName + if self.exchangeName not in link.local.exchanges: + raise Exception("Exchange not found") + self.exchange = link.local.exchanges[self.exchangeName] + self.queueName = "bridge." + link.local.tag + self.link.remote.declare_queue(self.queueName, self.link.remote.tag, self.link.local.tag) + self.link.remote.subscribe(self.exchange, self.queueName) + self.exchange.addDynamicBridge(self) + + def isDynamic(self): + return True + + def localTag(self): + return self.link.local.tag + + def remoteTag(self): + return self.link.remote.tag + + def propagate(self, key, tagList, fedOp, origin): + if self.link.remote.tag not in tagList: + self.link.remote.bind(self.exchangeName, self.queueName, key, tagList, fedOp, origin) + + def sendReorigin(self): + myTag = [] + myTag.append(self.link.local.tag) + self.link.remote.bind(self.exchangeName, self.queueName, "", myTag, "reorigin", "") + + +class Message: + def __init__(self, key, body): + self.key = key + self.body = body + self.tags = [] + + def appendTag(self, tag): + if tag not in self.tags: + self.tags.append(tag) + + def tagFound(self, tag): + return tag in self.tags + + diff --git a/qpid/python/models/fedsim/testBig.py b/qpid/python/models/fedsim/testBig.py new file mode 100644 index 0000000000..416a086983 --- /dev/null +++ b/qpid/python/models/fedsim/testBig.py @@ -0,0 +1,88 @@ +# +# 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. +# + +from fedsim import Sim + +sim = Sim() +b1 = sim.addBroker("B1") +b2 = sim.addBroker("B2") +b3 = sim.addBroker("B3") +b4 = sim.addBroker("B4") +b5 = sim.addBroker("B5") +b6 = sim.addBroker("B6") +b7 = sim.addBroker("B7") +b8 = sim.addBroker("B8") + +c1 = sim.addClient("C1", b1) +c3 = sim.addClient("C3", b3) +c4 = sim.addClient("C4", b4) +c5 = sim.addClient("C5", b5) +c8 = sim.addClient("C8", b8) + +sim.link(b1, b2) +sim.link(b3, b2) +sim.link(b4, b2) +sim.link(b5, b2) + +sim.link(b6, b7) +sim.link(b6, b8) + +sim.bind(c1, "A") +sim.bind(c3, "B") +sim.bind(c8, "A") + +sim.link(b5, b6) + +sim.bind(c4, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) +sim.sendMessage("A", b4) +sim.sendMessage("A", b5) +sim.sendMessage("A", b6) +sim.sendMessage("A", b7) +sim.sendMessage("A", b8) + +sim.sendMessage("B", b1) +sim.sendMessage("B", b2) +sim.sendMessage("B", b3) +sim.sendMessage("B", b4) +sim.sendMessage("B", b5) +sim.sendMessage("B", b6) +sim.sendMessage("B", b7) +sim.sendMessage("B", b8) + +sim.unbind(c1, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) +sim.sendMessage("A", b4) +sim.sendMessage("A", b5) +sim.sendMessage("A", b6) +sim.sendMessage("A", b7) +sim.sendMessage("A", b8) + +sim.unbind(c4, "A") +sim.unbind(c3, "B") +sim.unbind(c8, "A") + +sim.dumpState() +sim.end() diff --git a/qpid/python/models/fedsim/testRing.py b/qpid/python/models/fedsim/testRing.py new file mode 100644 index 0000000000..c883b54993 --- /dev/null +++ b/qpid/python/models/fedsim/testRing.py @@ -0,0 +1,48 @@ +# +# 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. +# + +from fedsim import Sim + +sim = Sim() +b1 = sim.addBroker("B1") +b2 = sim.addBroker("B2") +b3 = sim.addBroker("B3") + +sim.link(b1, b2, False) +sim.link(b2, b3, False) +sim.link(b3, b1, False) + +c1 = sim.addClient("C1", b1) +c2 = sim.addClient("C2", b2) +c3 = sim.addClient("C3", b3) + +sim.bind(c1, "A") +sim.bind(c2, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) + +sim.unbind(c2, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) + +sim.end() diff --git a/qpid/python/models/fedsim/testStar.py b/qpid/python/models/fedsim/testStar.py new file mode 100644 index 0000000000..e6b801446f --- /dev/null +++ b/qpid/python/models/fedsim/testStar.py @@ -0,0 +1,65 @@ +# +# 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. +# + +from fedsim import Sim + +sim = Sim() +b1 = sim.addBroker("B1") +b2 = sim.addBroker("B2") +b3 = sim.addBroker("B3") +bc = sim.addBroker("BC") + +sim.link(b1, bc) +sim.link(b2, bc) +sim.link(b3, bc) + +c1 = sim.addClient("C1", b1) +c2 = sim.addClient("C2", b2) +c3 = sim.addClient("C3", b3) +cc = sim.addClient("CC", bc) + +sim.bind(c1, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) +sim.sendMessage("A", bc) + +sim.bind(c2, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) +sim.sendMessage("A", bc) + +sim.unbind(c1, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) +sim.sendMessage("A", bc) + +sim.unbind(c2, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", b3) +sim.sendMessage("A", bc) + +sim.end() diff --git a/qpid/python/models/fedsim/testStarAdd.py b/qpid/python/models/fedsim/testStarAdd.py new file mode 100644 index 0000000000..e0eb44952a --- /dev/null +++ b/qpid/python/models/fedsim/testStarAdd.py @@ -0,0 +1,56 @@ +# +# 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. +# + +from fedsim import Sim + +sim = Sim() +b1 = sim.addBroker("B1") +b2 = sim.addBroker("B2") +b3 = sim.addBroker("B3") +bc = sim.addBroker("BC") + +sim.link(b1, bc) +sim.link(b2, bc) + +c1 = sim.addClient("C1", b1) +c2 = sim.addClient("C2", b2) +c3 = sim.addClient("C3", b3) +cc = sim.addClient("CC", bc) + +sim.bind(c1, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", bc) + +sim.bind(c2, "A") + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", bc) + +sim.bind(c3, "A") +sim.link(b3, bc) + +sim.sendMessage("A", b1) +sim.sendMessage("A", b2) +sim.sendMessage("A", bc) + +sim.end() + -- cgit v1.2.1 From 8a17f96b1254b5a827726e892edaf373d00dd0b8 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 24 Oct 2008 00:38:30 +0000 Subject: Added alternative functions for invoking methods git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707514 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 100 ++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 26 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index bfa48c7540..febcae5ea2 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -82,6 +82,10 @@ class Console: """ """ pass + def methodResponse(self, broker, seq, response): + """ """ + pass + class BrokerURL: def __init__(self, text): rex = re.compile(r""" @@ -458,16 +462,21 @@ class Session: code = codec.read_uint32() text = str(codec.read_str8()) outArgs = {} - obj, method = self.seqMgr._release(seq) + method, synchronous = self.seqMgr._release(seq) if code == 0: for arg in method.arguments: if arg.dir.find("O") != -1: outArgs[arg.name] = self._decodeValue(codec, arg.type) - broker.cv.acquire() - broker.syncResult = MethodResult(code, text, outArgs) - broker.syncInFlight = False - broker.cv.notify() - broker.cv.release() + result = MethodResult(code, text, outArgs) + if synchronous: + broker.cv.acquire() + broker.syncResult = result + broker.syncInFlight = False + broker.cv.notify() + broker.cv.release() + else: + if self.console: + self.console.methodResponse(broker, seq, result) def _handleHeartbeatInd(self, broker, codec, seq): timestamp = codec.read_uint64() @@ -612,6 +621,41 @@ class Session: else: raise ValueError ("Invalid type code: %d" % typecode) + def _sendMethodRequest(self, broker, schemaKey, objectId, name, argList): + """ This function can be used to send a method request to an object given only the + broker, schemaKey, and objectId. This is an uncommon usage pattern as methods are + normally invoked on the object itself. + """ + schema = self.getSchema(schemaKey) + for method in schema.getMethods(): + if name == method.name: + aIdx = 0 + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve((method, False)) + broker._setHeader(sendCodec, 'M', seq) + objectId.encode(sendCodec) + pname, cname, hash = schemaKey + sendCodec.write_str8(pname) + sendCodec.write_str8(cname) + sendCodec.write_bin128(hash) + sendCodec.write_str8(name) + + count = 0 + for arg in method.arguments: + if arg.dir.find("I") != -1: + count += 1 + if count != len(argList): + raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(argList))) + + for arg in method.arguments: + if arg.dir.find("I") != -1: + self._encodeValue(sendCodec, argList[aIdx], arg.type) + aIdx += 1 + smsg = broker._message(sendCodec.encoded, "agent.%d.%d" % + (objectId.getBroker(), objectId.getBank())) + broker._send(smsg) + return seq + return None class Package: """ """ @@ -921,12 +965,12 @@ class Object(object): return value raise Exception("Type Object has no attribute '%s'" % name) - def _invoke(self, name, args, kwargs): + def _sendMethodRequest(self, name, args, kwargs, synchronous=False): for method in self._schema.getMethods(): if name == method.name: aIdx = 0 sendCodec = Codec(self._broker.conn.spec) - seq = self._session.seqMgr._reserve((self, method)) + seq = self._session.seqMgr._reserve((method, synchronous)) self._broker._setHeader(sendCodec, 'M', seq) self._objectId.encode(sendCodec) pname, cname, hash = self._schema.getKey() @@ -948,26 +992,30 @@ class Object(object): aIdx += 1 smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % (self._objectId.getBroker(), self._objectId.getBank())) - self._broker.cv.acquire() - self._broker.syncInFlight = True - self._broker.cv.release() - + if synchronous: + self._broker.cv.acquire() + self._broker.syncInFlight = True + self._broker.cv.release() self._broker._send(smsg) + return seq + return None - self._broker.cv.acquire() - starttime = time() - while self._broker.syncInFlight and self._broker.error == None: - self._broker.cv.wait(self._broker.SYNC_TIME) - if time() - starttime > self._broker.SYNC_TIME: - self._broker.cv.release() - self._session.seqMgr._release(seq) - raise RuntimeError("Timed out waiting for method to respond") - self._broker.cv.release() - if self._broker.error != None: - errorText = self._broker.error - self._broker.error = None - raise Exception(errorText) - return self._broker.syncResult + def _invoke(self, name, args, kwargs): + if self._sendMethodRequest(name, args, kwargs, True): + self._broker.cv.acquire() + starttime = time() + while self._broker.syncInFlight and self._broker.error == None: + self._broker.cv.wait(self._broker.SYNC_TIME) + if time() - starttime > self._broker.SYNC_TIME: + self._broker.cv.release() + self._session.seqMgr._release(seq) + raise RuntimeError("Timed out waiting for method to respond") + self._broker.cv.release() + if self._broker.error != None: + errorText = self._broker.error + self._broker.error = None + raise Exception(errorText) + return self._broker.syncResult raise Exception("Invalid Method (software defect) [%s]" % name) def _parsePresenceMasks(self, codec, schema): -- cgit v1.2.1 From d33da71d0e2dbf23345c6bd45895eb918739c0c5 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 24 Oct 2008 00:45:11 +0000 Subject: QPID-1348 - Dynamic binding for federation. Parameterized exchange names for CPP examples git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707515 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 8a779bb7a3..7d6d3e333e 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -34,6 +34,8 @@ def Usage (): print " qpid-route [OPTIONS] route del " print " qpid-route [OPTIONS] route list []" print " qpid-route [OPTIONS] route flush []" + print " qpid-route [OPTIONS] dynamic add [tag] [exclude-list]" + print " qpid-route [OPTIONS] dynamic del " print print "Options:" print " -v [ --verbose ] Verbose output" @@ -118,7 +120,7 @@ class RouteManager: print "%-16s%-8d %c %-18s%s" % \ (link.host, link.port, YN(link.durable), link.state, link.lastError) - def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes): + def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes, dynamic=False): self.src = qmfconsole.BrokerURL(srcBroker) if self.dest.match(self.src.host, self.src.port): raise Exception("Linking broker to itself is not permitted") @@ -155,13 +157,13 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." - res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, False, False) - if res.status == 4: - raise Exception("Can't create a durable route on a non-durable link") + res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, False, dynamic) + if res.status != 0: + raise Exception(res.text) if _verbose: print "Bridge method returned:", res.status, res.text - def DelRoute (self, srcBroker, exchange, routingKey): + def DelRoute (self, srcBroker, exchange, routingKey, dynamic=False): self.src = qmfconsole.BrokerURL(srcBroker) link = self.getLink() if link == None: @@ -171,7 +173,8 @@ class RouteManager: bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: - if bridge.linkRef == link.getObjectId() and bridge.dest == exchange and bridge.key == routingKey: + if bridge.linkRef == link.getObjectId() and bridge.dest == exchange and bridge.key == routingKey \ + and bridge.dynamic == dynamic: if _verbose: print "Closing bridge..." res = bridge.close() @@ -201,7 +204,11 @@ class RouteManager: myLink = link break if myLink != None: - print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, bridge.key) + if bridge.dynamic: + keyText = "" + else: + keyText = bridge.key + print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, keyText) def ClearAllRoutes (self): links = self.qmf.getObjects(_class="link") @@ -285,6 +292,22 @@ try: elif cmd == "list": rm.ListLinks () + elif group == "dynamic": + if cmd == "add": + if nargs < 5 or nargs > 7: + Usage () + + tag = "" + excludes = "" + if nargs > 5: tag = cargs[5] + if nargs > 6: excludes = cargs[6] + rm.AddRoute (cargs[3], cargs[4], "", tag, excludes, dynamic=True) + elif cmd == "del": + if nargs != 5: + Usage () + else: + rm.DelRoute (cargs[3], cargs[4], "", dynamic=True) + elif group == "route": if cmd == "add": if nargs < 6 or nargs > 8: @@ -294,12 +317,12 @@ try: excludes = "" if nargs > 6: tag = cargs[6] if nargs > 7: excludes = cargs[7] - rm.AddRoute (cargs[3], cargs[4], cargs[5], tag, excludes) + rm.AddRoute (cargs[3], cargs[4], cargs[5], tag, excludes, dynamic=False) elif cmd == "del": if nargs != 6: Usage () else: - rm.DelRoute (cargs[3], cargs[4], cargs[5]) + rm.DelRoute (cargs[3], cargs[4], cargs[5], dynamic=False) else: if cmd == "list": rm.ListRoutes () -- cgit v1.2.1 From c1c0020001879493aee524951eedb1d1d9c97c04 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 24 Oct 2008 15:12:53 +0000 Subject: Added exchange options to qpid-config git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707651 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 83 ++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 24 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index e7f866edcc..6917d4c5c0 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -35,6 +35,8 @@ _maxQueueCount = None _policyType = None _lvq = False _optimisticConsume = False +_msgSequence = False +_ive = False FILECOUNT = "qpid.file_count" FILESIZE = "qpid.file_size" @@ -44,6 +46,8 @@ POLICY_TYPE = "qpid.policy_type" CLUSTER_DURABLE = "qpid.persist_last_node" LVQ = "qpid.last_value_queue" OPTIMISTIC_CONSUME = "qpid.optimistic_consume" +MSG_SEQUENCE = "qpid.msg_sequence" +IVE = "qpid.ive" def Usage (): print "Usage: qpid-config [OPTIONS]" @@ -74,7 +78,12 @@ def Usage (): print " --optimistic-consume Enable optimistic consume on the queue" print print "Add Exchange Options:" - print " --durable Exchange is durable" + print " --durable Exchange is durable" + print " --sequence Exchange will insert a 'qpid.msg_sequence' field in the message header" + print " with a value that increments for each message forwarded." + print " --ive Exchange will behave as an 'initial-value-exchange', keeping a reference" + print " to the last message forwarded and enqueuing that message to newly bound" + print " queues." print sys.exit (1) @@ -116,11 +125,26 @@ class BrokerManager: def ExchangeList (self, filter): exchanges = self.qmf.getObjects(_class="exchange") - print "Durable Type Bindings Exchange Name" - print "=======================================================" + caption1 = "Type " + caption2 = "Exchange Name" + maxNameLen = len(caption2) + for ex in exchanges: + if self.match(ex.name, filter): + if len(ex.name) > maxNameLen: maxNameLen = len(ex.name) + print "%s%-*s Attributes" % (caption1, maxNameLen, caption2) + line = "" + for i in range(((maxNameLen + len(caption1)) / 5) + 5): + line += "=====" + print line + for ex in exchanges: if self.match (ex.name, filter): - print "%4c %-10s%5d %s" % (YN (ex.durable), ex.type, ex.bindingCount, ex.name) + print "%-10s%-*s " % (ex.type, maxNameLen, ex.name), + args = ex.arguments + if ex.durable: print "--durable", + if MSG_SEQUENCE in args and args[MSG_SEQUENCE] == 1: print "--sequence", + if IVE in args and args[IVE] == 1: print "--ive", + print def ExchangeListRecurse (self, filter): exchanges = self.qmf.getObjects(_class="exchange") @@ -144,7 +168,8 @@ class BrokerManager: caption = "Queue Name" maxNameLen = len(caption) for q in queues: - if len(q.name) > maxNameLen: maxNameLen = len(q.name) + if self.match (q.name, filter): + if len(q.name) > maxNameLen: maxNameLen = len(q.name) print "%-*s Attributes" % (maxNameLen, caption) line = "" for i in range((maxNameLen / 5) + 5): @@ -152,20 +177,21 @@ class BrokerManager: print line for q in queues: - print "%-*s " % (maxNameLen, q.name), - args = q.arguments - if q.durable: print "durable", - if CLUSTER_DURABLE in args and args[CLUSTER_DURABLE] == 1: print "clusterDurable", - if q.autoDelete: print "autoDel", - if q.exclusive: print "excl", - if FILESIZE in args: print "fileSize=%d" % args[FILESIZE], - if FILECOUNT in args: print "fileCount=%d" % args[FILECOUNT], - if MAX_QUEUE_SIZE in args: print "maxQSize=%d" % args[MAX_QUEUE_SIZE], - if MAX_QUEUE_COUNT in args: print "maxQCount=%d" % args[MAX_QUEUE_COUNT], - if POLICY_TYPE in args: print "policy=%s" % args[POLICY_TYPE], - if LVQ in args and args[LVQ] == 1: print "lvq", - if OPTIMISTIC_CONSUME in args and args[OPTIMISTIC_CONSUME] == 1: print "optConsume", - print + if self.match (q.name, filter): + print "%-*s " % (maxNameLen, q.name), + args = q.arguments + if q.durable: print "--durable", + if CLUSTER_DURABLE in args and args[CLUSTER_DURABLE] == 1: print "--cluster-durable", + if q.autoDelete: print "auto-del", + if q.exclusive: print "excl", + if FILESIZE in args: print "--file-size=%d" % args[FILESIZE], + if FILECOUNT in args: print "--file-count=%d" % args[FILECOUNT], + if MAX_QUEUE_SIZE in args: print "--max-queue-size=%d" % args[MAX_QUEUE_SIZE], + if MAX_QUEUE_COUNT in args: print "--max-queue-count=%d" % args[MAX_QUEUE_COUNT], + if POLICY_TYPE in args: print "--policy-type=%s" % args[POLICY_TYPE], + if LVQ in args and args[LVQ] == 1: print "--last-value-queue", + if OPTIMISTIC_CONSUME in args and args[OPTIMISTIC_CONSUME] == 1: print "--optimistic-consume", + print def QueueListRecurse (self, filter): exchanges = self.qmf.getObjects(_class="exchange") @@ -189,7 +215,12 @@ class BrokerManager: Usage () etype = args[0] ename = args[1] - self.broker.getAmqpSession().exchange_declare (exchange=ename, type=etype, durable=_durable) + declArgs = {} + if _msgSequence: + declArgs[MSG_SEQUENCE] = 1 + if _ive: + declArgs[IVE] = 1 + self.broker.getAmqpSession().exchange_declare (exchange=ename, type=etype, durable=_durable, arguments=declArgs) def DelExchange (self, args): if len (args) < 1: @@ -265,6 +296,7 @@ def YN (bool): return 'Y' return 'N' + ## ## Main Program ## @@ -272,7 +304,7 @@ def YN (bool): try: longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=", - "last-value-queue", "optimistic-consume") + "last-value-queue", "optimistic-consume", "sequence", "ive") (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -300,6 +332,10 @@ for opt in optlist: _lvq = True if opt[0] == "--optimistic-consume": _optimisticConsume = True + if opt[0] == "--sequence": + _msgSequence = True + if opt[0] == "--ive": + _ive = True nargs = len (cargs) bm = BrokerManager () @@ -313,12 +349,12 @@ try: modifier = "" if nargs > 1: modifier = cargs[1] - if cmd[0] == 'e': + if cmd == "exchanges": if _recursive: bm.ExchangeListRecurse (modifier) else: bm.ExchangeList (modifier) - elif cmd[0] == 'q': + elif cmd == "queues": if _recursive: bm.QueueListRecurse (modifier) else: @@ -344,7 +380,6 @@ try: else: Usage () except Exception,e: - raise print "Failed:", e.message sys.exit(1) -- cgit v1.2.1 From 92c702abaa7ef5731212ac7fb4acbad7428a34c7 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 24 Oct 2008 15:15:59 +0000 Subject: Use a str16 instead of str8 for qmf method response text git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707653 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/management.py | 2 +- qpid/python/qpid/qmfconsole.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index fd60ac22cf..863e25e24c 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -534,7 +534,7 @@ class managementClient: def handleMethodReply (self, ch, codec, sequence): status = codec.read_uint32 () - sText = str (codec.read_str8 ()) + sText = str (codec.read_str16 ()) data = self.seqMgr.release (sequence) if data == None: diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index febcae5ea2..4c7b47e38a 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -460,7 +460,7 @@ class Session: def _handleMethodResp(self, broker, codec, seq): code = codec.read_uint32() - text = str(codec.read_str8()) + text = str(codec.read_str16()) outArgs = {} method, synchronous = self.seqMgr._release(seq) if code == 0: -- cgit v1.2.1 From c45de28831e0c56454bd5b52ec90d14af9e5a89c Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 24 Oct 2008 19:13:33 +0000 Subject: Fixed the following problems in qmfconsole.py: - Fixed typo in getTimestamp() - Updated lock usage to use the try: acquire() finally: release() pattern - Removed unused match method from URL - Fixed a bug where 'packages' was accessed without lock protection git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@707724 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 264 +++++++++++++++++++++++------------------ 1 file changed, 147 insertions(+), 117 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 4c7b47e38a..7bfe5d1128 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -106,9 +106,6 @@ class BrokerURL: def name(self): return self.host + ":" + str(self.port) - def match(self, host, port): - return socket.gethostbyname(self.host) == socket.gethostbyname(host) and self.port == port - class Session: """ An instance of the Session class represents a console session running @@ -326,10 +323,12 @@ class Session: for agent in agentList: broker = agent.broker sendCodec = Codec(broker.conn.spec) - self.cv.acquire() - seq = self.seqMgr._reserve(self._CONTEXT_MULTIGET) - self.syncSequenceList.append(seq) - self.cv.release() + try: + self.cv.acquire() + seq = self.seqMgr._reserve(self._CONTEXT_MULTIGET) + self.syncSequenceList.append(seq) + finally: + self.cv.release() broker._setHeader(sendCodec, 'G', seq) sendCodec.write_map(map) smsg = broker._message(sendCodec.encoded, "agent.%s" % agent.bank) @@ -337,15 +336,17 @@ class Session: starttime = time() timeout = False - self.cv.acquire() - while len(self.syncSequenceList) > 0 and self.error == None: - self.cv.wait(self.GET_WAIT_TIME) - if time() - starttime > self.GET_WAIT_TIME: - for pendingSeq in self.syncSequenceList: - self.seqMgr._release(pendingSeq) - self.syncSequenceList = [] - timeout = True - self.cv.release() + try: + self.cv.acquire() + while len(self.syncSequenceList) > 0 and self.error == None: + self.cv.wait(self.GET_WAIT_TIME) + if time() - starttime > self.GET_WAIT_TIME: + for pendingSeq in self.syncSequenceList: + self.seqMgr._release(pendingSeq) + self.syncSequenceList = [] + timeout = True + finally: + self.cv.release() if self.error: errorText = self.error @@ -397,14 +398,16 @@ class Session: def _handlePackageInd(self, broker, codec, seq): pname = str(codec.read_str8()) - self.cv.acquire() - if pname not in self.packages: - self.packages[pname] = {} - self.cv.release() - if self.console != None: - self.console.newPackage(pname) - else: + notify = False + try: + self.cv.acquire() + if pname not in self.packages: + self.packages[pname] = {} + notify = True + finally: self.cv.release() + if notify and self.console != None: + self.console.newPackage(pname) # Send a class request broker._incOutstanding() @@ -422,30 +425,38 @@ class Session: if context == self._CONTEXT_STARTUP: broker._decOutstanding() elif context == self._CONTEXT_SYNC and seq == broker.syncSequence: - broker.cv.acquire() - broker.syncInFlight = False - broker.cv.notify() - broker.cv.release() + try: + broker.cv.acquire() + broker.syncInFlight = False + broker.cv.notify() + finally: + broker.cv.release() elif context == self._CONTEXT_MULTIGET and seq in self.syncSequenceList: - self.cv.acquire() - self.syncSequenceList.remove(seq) - if len(self.syncSequenceList) == 0: - self.cv.notify() - self.cv.release() + try: + self.cv.acquire() + self.syncSequenceList.remove(seq) + if len(self.syncSequenceList) == 0: + self.cv.notify() + finally: + self.cv.release() def _handleClassInd(self, broker, codec, seq): kind = codec.read_uint8() pname = str(codec.read_str8()) cname = str(codec.read_str8()) hash = codec.read_bin128() + unknown = False - self.cv.acquire() - if pname not in self.packages: + try: + self.cv.acquire() + if pname in self.packages: + if (cname, hash) not in self.packages[pname]: + unknown = True + finally: self.cv.release() - return - if (cname, hash) not in self.packages[pname]: + + if unknown: # Send a schema request for the unknown class - self.cv.release() broker._incOutstanding() sendCodec = Codec(broker.conn.spec) seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) @@ -455,8 +466,6 @@ class Session: sendCodec.write_bin128(hash) smsg = broker._message(sendCodec.encoded) broker._send(smsg) - else: - self.cv.release() def _handleMethodResp(self, broker, codec, seq): code = codec.read_uint32() @@ -469,11 +478,13 @@ class Session: outArgs[arg.name] = self._decodeValue(codec, arg.type) result = MethodResult(code, text, outArgs) if synchronous: - broker.cv.acquire() - broker.syncResult = result - broker.syncInFlight = False - broker.cv.notify() - broker.cv.release() + try: + broker.cv.acquire() + broker.syncResult = result + broker.syncInFlight = False + broker.cv.notify() + finally: + broker.cv.release() else: if self.console: self.console.methodResponse(broker, seq, result) @@ -495,9 +506,11 @@ class Session: hash = codec.read_bin128() classKey = (pname, cname, hash) _class = SchemaClass(kind, classKey, codec) - self.cv.acquire() - self.packages[pname][(cname, hash)] = _class - self.cv.release() + try: + self.cv.acquire() + self.packages[pname][(cname, hash)] = _class + finally: + self.cv.release() broker._decOutstanding() if self.console != None: self.console.newClass(kind, classKey) @@ -507,26 +520,28 @@ class Session: cname = str(codec.read_str8()) hash = codec.read_bin128() classKey = (pname, cname, hash) - self.cv.acquire() - if pname not in self.packages: - self.cv.release() - return - if (cname, hash) not in self.packages[pname]: + try: + self.cv.acquire() + if pname not in self.packages: + return + if (cname, hash) not in self.packages[pname]: + return + schema = self.packages[pname][(cname, hash)] + finally: self.cv.release() - return - self.cv.release() - schema = self.packages[pname][(cname, hash)] + object = Object(self, broker, schema, codec, prop, stat) if pname == "org.apache.qpid.broker" and cname == "agent": broker._updateAgent(object) - self.cv.acquire() - if seq in self.syncSequenceList: - if object.getTimestamps()[2] == 0 and self._selectMatch(object): - self.getResult.append(object) + try: + self.cv.acquire() + if seq in self.syncSequenceList: + if object.getTimestamps()[2] == 0 and self._selectMatch(object): + self.getResult.append(object) + return + finally: self.cv.release() - return - self.cv.release() if self.console != None: if prop: @@ -536,10 +551,12 @@ class Session: def _handleError(self, error): self.error = error - self.cv.acquire() - self.syncSequenceList = [] - self.cv.notify() - self.cv.release() + try: + self.cv.acquire() + self.syncSequenceList = [] + self.cv.notify() + finally: + self.cv.release() def _selectMatch(self, object): """ Check the object against self.getSelect to check for a match """ @@ -993,24 +1010,27 @@ class Object(object): smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % (self._objectId.getBroker(), self._objectId.getBank())) if synchronous: - self._broker.cv.acquire() - self._broker.syncInFlight = True - self._broker.cv.release() + try: + self._broker.cv.acquire() + self._broker.syncInFlight = True + finally: + self._broker.cv.release() self._broker._send(smsg) return seq return None def _invoke(self, name, args, kwargs): if self._sendMethodRequest(name, args, kwargs, True): - self._broker.cv.acquire() - starttime = time() - while self._broker.syncInFlight and self._broker.error == None: - self._broker.cv.wait(self._broker.SYNC_TIME) - if time() - starttime > self._broker.SYNC_TIME: - self._broker.cv.release() - self._session.seqMgr._release(seq) - raise RuntimeError("Timed out waiting for method to respond") - self._broker.cv.release() + try: + self._broker.cv.acquire() + starttime = time() + while self._broker.syncInFlight and self._broker.error == None: + self._broker.cv.wait(self._broker.SYNC_TIME) + if time() - starttime > self._broker.SYNC_TIME: + self._session.seqMgr._release(seq) + raise RuntimeError("Timed out waiting for method to respond") + finally: + self._broker.cv.release() if self._broker.error != None: errorText = self._broker.error self._broker.error = None @@ -1208,36 +1228,40 @@ class Broker: raise Exception("Broker already disconnected") def _waitForStable(self): - self.cv.acquire() - if self.reqsOutstanding == 0: + try: + self.cv.acquire() + if self.reqsOutstanding == 0: + return + self.syncInFlight = True + starttime = time() + while self.reqsOutstanding != 0: + self.cv.wait(self.SYNC_TIME) + if time() - starttime > self.SYNC_TIME: + raise RuntimeError("Timed out waiting for broker to synchronize") + finally: self.cv.release() - return - self.syncInFlight = True - starttime = time() - while self.reqsOutstanding != 0: - self.cv.wait(self.SYNC_TIME) - if time() - starttime > self.SYNC_TIME: - self.cv.release() - raise RuntimeError("Timed out waiting for broker to synchronize") - self.cv.release() def _incOutstanding(self): - self.cv.acquire() - self.reqsOutstanding += 1 - self.cv.release() + try: + self.cv.acquire() + self.reqsOutstanding += 1 + finally: + self.cv.release() def _decOutstanding(self): - self.cv.acquire() - self.reqsOutstanding -= 1 - if self.reqsOutstanding == 0 and not self.topicBound: - self.topicBound = True - for key in self.session.bindingKeyList: - self.amqpSession.exchange_bind(exchange="qpid.management", - queue=self.topicName, binding_key=key) - if self.reqsOutstanding == 0 and self.syncInFlight: - self.syncInFlight = False - self.cv.notify() - self.cv.release() + try: + self.cv.acquire() + self.reqsOutstanding -= 1 + if self.reqsOutstanding == 0 and not self.topicBound: + self.topicBound = True + for key in self.session.bindingKeyList: + self.amqpSession.exchange_bind(exchange="qpid.management", + queue=self.topicName, binding_key=key) + if self.reqsOutstanding == 0 and self.syncInFlight: + self.syncInFlight = False + self.cv.notify() + finally: + self.cv.release() def _replyCb(self, msg): codec = Codec(self.conn.spec, msg.body) @@ -1259,10 +1283,12 @@ class Broker: def _exceptionCb(self, data): self.isConnected = False self.error = data - self.cv.acquire() - if self.syncInFlight: - self.cv.notify() - self.cv.release() + try: + self.cv.acquire() + if self.syncInFlight: + self.cv.notify() + finally: + self.cv.release() self.session._handleError(self.error) self.session._handleBrokerDisconnect(self) @@ -1326,7 +1352,7 @@ class Event: return self.arguments def getTimestamp(self): - return self.timerstamp + return self.timestamp def getName(self): return self.name @@ -1343,21 +1369,25 @@ class SequenceManager: def _reserve(self, data): """ Reserve a unique sequence number """ - self.lock.acquire() - result = self.sequence - self.sequence = self.sequence + 1 - self.pending[result] = data - self.lock.release() + try: + self.lock.acquire() + result = self.sequence + self.sequence = self.sequence + 1 + self.pending[result] = data + finally: + self.lock.release() return result def _release(self, seq): """ Release a reserved sequence number """ data = None - self.lock.acquire() - if seq in self.pending: - data = self.pending[seq] - del self.pending[seq] - self.lock.release() + try: + self.lock.acquire() + if seq in self.pending: + data = self.pending[seq] + del self.pending[seq] + finally: + self.lock.release() return data -- cgit v1.2.1 From 9a420ca9f7d542927503d044abefaa5004576596 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 27 Oct 2008 02:52:58 +0000 Subject: Put Url.match back in. It isn't unused. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@708075 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 7bfe5d1128..bfd201fcc3 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -106,6 +106,9 @@ class BrokerURL: def name(self): return self.host + ":" + str(self.port) + def match(self, host, port): + return socket.gethostbyname(self.host) == socket.gethostbyname(host) and self.port == port + class Session: """ An instance of the Session class represents a console session running -- cgit v1.2.1 From 3f2d66ae172f6bfd8b8a257e68ea425c01bbb9d9 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 28 Oct 2008 17:19:07 +0000 Subject: fixed a bug in int16 codec git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@708615 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 2 +- qpid/python/tests/codec010.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index dac023e2bd..c2ea7ed512 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -68,7 +68,7 @@ class Codec(Packer): def read_int16(self): return self.unpack("!h") def write_int16(self, n): - return self.unpack("!h", n) + self.pack("!h", n) def read_uint32(self): diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index 835966e103..bbe894b7b3 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -86,3 +86,7 @@ class CodecTest(TestCase): def testArrayNone(self): self.check("array", None) + + def testInt16(self): + self.check("int16", 3) + self.check("int16", -3) -- cgit v1.2.1 From 1897b4925c78fdf5b17dda487a7a0e7f8c9ecd47 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 28 Oct 2008 23:15:27 +0000 Subject: ssl support for the python client git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@708718 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 21 +++++++++++++++++++-- qpid/python/qpid/qmfconsole.py | 37 ++++++++++++++++++------------------ qpid/python/qpid/testlib.py | 43 ++++++++++++++++++++++++------------------ qpid/python/qpid/util.py | 41 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 39 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index ce27a74489..4c9c02822a 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -17,7 +17,7 @@ # under the License. # -import datatypes, session +import datatypes, session, socket from threading import Thread, Condition, RLock from util import wait, notify from assembler import Assembler, Segment @@ -44,10 +44,27 @@ def client(*args, **kwargs): def server(*args, **kwargs): return delegates.Server(*args, **kwargs) +class SSLWrapper: + + def __init__(self, ssl): + self.ssl = ssl + + def recv(self, n): + return self.ssl.read(n) + + def send(self, s): + return self.ssl.write(s) + +def sslwrap(sock): + if isinstance(sock, socket.SSLType): + return SSLWrapper(sock) + else: + return sock + class Connection(Assembler): def __init__(self, sock, spec=None, delegate=client, **args): - Assembler.__init__(self, sock) + Assembler.__init__(self, sslwrap(sock)) if spec == None: spec = load(default()) self.spec = spec diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index bfd201fcc3..55f2370a28 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -27,7 +27,7 @@ import re from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed from qpid.datatypes import uuid4, Message, RangedSet -from qpid.util import connect +from qpid.util import connect, ssl, URL from qpid.codec010 import StringCodec as Codec from threading import Lock, Condition from time import time, strftime, gmtime @@ -86,21 +86,17 @@ class Console: """ """ pass -class BrokerURL: +class BrokerURL(URL): def __init__(self, text): - rex = re.compile(r""" - # [ [ / ] @] [ : ] - ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) - match = rex.match(text) - if not match: raise ValueError("'%s' is not a valid broker url" % (text)) - user, password, host, port = match.groups() - - socket.gethostbyname(host) - self.host = host - if port: self.port = int(port) - else: self.port = 5672 - self.authName = user or "guest" - self.authPass = password or "guest" + URL.__init__(self, text) + socket.gethostbyname(self.host) + if self.port is None: + if self.scheme == URL.AMQPS: + self.port = 5671 + else: + self.port = 5672 + self.authName = self.user or "guest" + self.authPass = self.password or "guest" self.authMech = "PLAIN" def name(self): @@ -178,7 +174,8 @@ class Session: def addBroker(self, target="localhost"): """ Connect to a Qpid broker. Returns an object of type Broker. """ url = BrokerURL(target) - broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass) + broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass, + ssl = url.scheme == URL.AMQPS) if not broker.isConnected and not self.manageConnections: raise Exception(broker.error) @@ -1075,10 +1072,11 @@ class Broker: """ """ SYNC_TIME = 60 - def __init__(self, session, host, port, authMech, authUser, authPass): + def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): self.session = session self.host = host self.port = port + self.ssl = ssl self.authUser = authUser self.authPass = authPass self.agents = {} @@ -1129,7 +1127,10 @@ class Broker: def _tryToConnect(self): try: self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self.conn = Connection(connect(self.host, self.port), username=self.authUser, password=self.authPass) + sock = connect(self.host, self.port) + if self.ssl: + sock = ssl(sock) + self.conn = Connection(sock, username=self.authUser, password=self.authPass) self.conn.start() self.replyName = "reply-%s" % self.amqpSessionId self.amqpSession = self.conn.session(self.amqpSessionId) diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index b8f4c29ceb..53723148b7 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -32,7 +32,7 @@ from qpid.message import Message #0-10 support from qpid.connection import Connection from qpid.spec010 import load -from qpid.util import connect +from qpid.util import connect, ssl, URL def findmodules(root): """Find potential python modules under directory root""" @@ -70,8 +70,9 @@ Options: -s/--spec : URL of AMQP XML specification or one of these abbreviations: 0-8 - use the default 0-8 specification. 0-9 - use the default 0-9 specification. + 0-10-errata - use the 0-10 specification with qpid errata. -e/--errata : file containing amqp XML errata - -b/--broker [[/]@][:] : broker to connect to + -b/--broker [amqps://][[/]@][:] : broker to connect to -v/--verbose : verbose - lists tests as they are run. -d/--debug : enable debug logging. -i/--ignore : ignore the named test. @@ -82,15 +83,20 @@ Options: sys.exit(1) def setBroker(self, broker): - rex = re.compile(r""" - # [ [ / ] @] [ : ] - ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) - match = rex.match(broker) - if not match: self._die("'%s' is not a valid broker" % (broker)) - self.user, self.password, self.host, self.port = match.groups() - self.port = int(default(self.port, 5672)) - self.user = default(self.user, "guest") - self.password = default(self.password, "guest") + try: + self.url = URL(broker) + except ValueError: + self._die("'%s' is not a valid broker" % (broker)) + self.user = default(self.url.user, "guest") + self.password = default(self.url.password, "guest") + self.host = self.url.host + if self.url.scheme == URL.AMQPS: + self.ssl = True + default_port = 5671 + else: + self.ssl = False + default_port = 5672 + self.port = default(self.url.port, default_port) def ignoreFile(self, filename): f = file(filename) @@ -129,6 +135,7 @@ Options: if opt in ("-I", "--ignore-file"): self.ignoreFile(value) if opt in ("-S", "--skip-self-test"): self.skip_self_test = True if opt in ("-F", "--spec-folder"): TestRunner.SPEC_FOLDER = value + # Abbreviations for default settings. if (self.specfile == "0-10"): self.spec = load(self.get_spec_file("amqp.0-10.xml")) @@ -352,20 +359,20 @@ class TestBase010(unittest.TestCase): """ def setUp(self): - spec = testrunner.spec - self.conn = Connection(connect(testrunner.host, testrunner.port), spec, - username=testrunner.user, password=testrunner.password) - self.conn.start(timeout=10) + self.conn = self.connect() self.session = self.conn.session("test-session", timeout=10) self.qmf = None def startQmf(self): self.qmf = qpid.qmfconsole.Session() - self.qmf_broker = self.qmf.addBroker("%s:%d" % (testrunner.host, testrunner.port)) + self.qmf_broker = self.qmf.addBroker(str(testrunner.url)) def connect(self, host=None, port=None): - spec = testrunner.spec - conn = Connection(connect(host or testrunner.host, port or testrunner.port), spec) + sock = connect(host or testrunner.host, port or testrunner.port) + if testrunner.url.scheme == URL.AMQPS: + sock = ssl(sock) + conn = Connection(sock, testrunner.spec, username=testrunner.user, + password=testrunner.password) conn.start(timeout=10) return conn diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index 1140cbe5ef..1ca616f1f5 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -17,7 +17,9 @@ # under the License. # -import os, socket, time, textwrap +import os, socket, time, textwrap, re + +ssl = socket.ssl def connect(host, port): sock = socket.socket() @@ -76,3 +78,40 @@ def fill(text, indent, heading = None): init = sub w = textwrap.TextWrapper(initial_indent = init, subsequent_indent = sub) return w.fill(" ".join(text.split())) + +class URL: + + RE = re.compile(r""" + # [ :// ] [ [ / ] @] [ : ] + ^ (?: ([^:/@]+)://)? (?: ([^:/@]+) (?: / ([^:/@]+) )? @)? ([^@:/]+) (?: :([0-9]+))?$ +""", re.X) + + AMQPS = "amqps" + AMQP = "amqp" + + def __init__(self, s): + match = URL.RE.match(s) + if match is None: + raise ValueError(s) + self.scheme, self.user, self.password, self.host, port = match.groups() + if port is None: + self.port = None + else: + self.port = int(port) + + def __repr__(self): + return "URL(%r)" % str(self) + + def __str__(self): + s = "" + if self.scheme: + s += "%s://" % self.scheme + if self.user: + s += self.user + if self.password: + s += "/%s" % self.password + s += "@" + s += self.host + if self.port: + s += ":%s" % self.port + return s -- cgit v1.2.1 From eb22e389934952405ca0d3bc4d863150b8b90bce Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 31 Oct 2008 03:41:29 +0000 Subject: Fixed a problem with improper shutdown that occasionally caused unhandled exceptions git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@709337 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 1 + 1 file changed, 1 insertion(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 55f2370a28..f5c9af18cc 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -1227,6 +1227,7 @@ class Broker: if self.session.console != None: self.amqpSession.incoming("tdest").stop() self.amqpSession.close() + self.conn.close() self.isConnected = False else: raise Exception("Broker already disconnected") -- cgit v1.2.1 From a5049238140656ad5fbf2ada920b65b507b327d6 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 31 Oct 2008 18:56:24 +0000 Subject: Federation enhancements and bug fixes: qmfconsole.py - minor fixes, make sure object-dereference only queries one broker Bridge.cpp - Added channel-id to queue name to avoid collisions qpid-route - Added link-map feature for viewing the entire federated topology git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@709532 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 102 +++++++++++++++++++++++++++++++++++++++- qpid/python/qpid/qmfconsole.py | 12 +++-- 2 files changed, 110 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 7d6d3e333e..47eeef3ff2 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -29,6 +29,7 @@ def Usage (): print "Usage: qpid-route [OPTIONS] link add " print " qpid-route [OPTIONS] link del " print " qpid-route [OPTIONS] link list []" + print " qpid-route [OPTIONS] link map []" print print " qpid-route [OPTIONS] route add [tag] [exclude-list]" print " qpid-route [OPTIONS] route del " @@ -120,6 +121,80 @@ class RouteManager: print "%-16s%-8d %c %-18s%s" % \ (link.host, link.port, YN(link.durable), link.state, link.lastError) + def MapLinks(self): + qmf = self.qmf + print + print "Finding Linked Brokers:" + + brokerList = {} + brokerList[self.dest.name()] = self.broker + print " %s... Ok" % self.dest + + added = True + while added: + added = False + links = qmf.getObjects(_class="link") + for link in links: + url = qmfconsole.BrokerURL("%s:%d" % (link.host, link.port)) + if url.name() not in brokerList: + print " %s..." % url.name(), + try: + b = qmf.addBroker("%s:%d" % (link.host, link.port)) + brokerList[url.name()] = b + added = True + print "Ok" + except Exception, e: + print e + + print + print "Dynamic Routes:" + bridges = qmf.getObjects(_class="bridge", dynamic=True) + fedExchanges = [] + for bridge in bridges: + if bridge.src not in fedExchanges: + fedExchanges.append(bridge.src) + if len(fedExchanges) == 0: + print " none found" + else: + print + + for ex in fedExchanges: + print " Exchange %s:" % ex + pairs = [] + for bridge in bridges: + if bridge.src == ex: + link = bridge._linkRef_ + fromUrl = "%s:%s" % (link.host, link.port) + toUrl = bridge.getBroker().getUrl() + found = False + for pair in pairs: + if pair.matches(fromUrl, toUrl): + found = True + if not found: + pairs.append(RoutePair(fromUrl, toUrl)) + for pair in pairs: + print " %s" % pair + print + + print "Static Routes:" + bridges = qmf.getObjects(_class="bridge", dynamic=False) + if len(bridges) == 0: + print " none found" + else: + print + + for bridge in bridges: + link = bridge._linkRef_ + fromUrl = "%s:%s" % (link.host, link.port) + toUrl = bridge.getBroker().getUrl() + print " %s(%s) <= %s(%s) key=%s" % (toUrl, bridge.dest, fromUrl, bridge.src, bridge.key) + print + + for broker in brokerList: + if broker != self.dest.name(): + qmf.delBroker(brokerList[broker]) + + def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes, dynamic=False): self.src = qmfconsole.BrokerURL(srcBroker) if self.dest.match(self.src.host, self.src.port): @@ -240,6 +315,28 @@ class RouteManager: elif _verbose: print "Ok" +class RoutePair: + def __init__(self, fromUrl, toUrl): + self.fromUrl = fromUrl + self.toUrl = toUrl + self.bidir = False + + def __repr__(self): + if self.bidir: + delimit = "<=>" + else: + delimit = " =>" + return "%s %s %s" % (self.fromUrl, delimit, self.toUrl) + + def matches(self, fromUrl, toUrl): + if fromUrl == self.fromUrl and toUrl == self.toUrl: + return True + if toUrl == self.fromUrl and fromUrl == self.toUrl: + self.bidir = True + return True + return False + + def YN(val): if val == 1: return 'Y' @@ -290,7 +387,9 @@ try: Usage() rm.DelLink (cargs[3]) elif cmd == "list": - rm.ListLinks () + rm.ListLinks() + elif cmd == "map": + rm.MapLinks() elif group == "dynamic": if cmd == "add": @@ -330,6 +429,7 @@ try: rm.ClearAllRoutes () else: Usage () + except Exception,e: print "Failed:", e.args[0] sys.exit(1) diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index f5c9af18cc..3ceb41b7a6 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -511,6 +511,8 @@ class Session: self.packages[pname][(cname, hash)] = _class finally: self.cv.release() + + self.seqMgr._release(seq) broker._decOutstanding() if self.console != None: self.console.newClass(kind, classKey) @@ -759,7 +761,7 @@ class SchemaProperty: self.unit = None self.min = None self.max = None - self.maxlan = None + self.maxlen = None self.desc = None for key, value in map.items(): @@ -916,6 +918,10 @@ class Object(object): for statistic in schema.getStatistics(): self._statistics.append((statistic, self._session._decodeValue(codec, statistic.type))) + def getBroker(self): + """ Return the broker from which this object was sent """ + return self._broker + def getObjectId(self): """ Return the object identifier for this object """ return self._objectId @@ -972,7 +978,7 @@ class Object(object): if name == property.name: return value if name == "_" + property.name + "_" and property.type == 10: # Dereference references - deref = self._session.getObjects(_objectId=value) + deref = self._session.getObjects(_objectId=value, _broker=self._broker) if len(deref) != 1: return None else: @@ -1090,6 +1096,7 @@ class Broker: self.error = None self.brokerId = None self.isConnected = False + self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) self._tryToConnect() def isConnected(self): @@ -1126,7 +1133,6 @@ class Broker: def _tryToConnect(self): try: - self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) sock = connect(self.host, self.port) if self.ssl: sock = ssl(sock) -- cgit v1.2.1 From d99388aabaa11e8ab448918fc672efb1b9d267a9 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 4 Nov 2008 19:56:20 +0000 Subject: Fixed usage text for qpid-tool git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@711367 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-tool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-tool b/qpid/python/commands/qpid-tool index 60535c253b..14308f69fb 100755 --- a/qpid/python/commands/qpid-tool +++ b/qpid/python/commands/qpid-tool @@ -148,7 +148,7 @@ class Mcli (Cmd): self.dataObject.close () def Usage (): - print "Usage: qpid-tool []]" + print "Usage: qpid-tool [[/@][:]]" print sys.exit (1) -- cgit v1.2.1 From f5e991fc77b5e2a27d95785c4e08e4286828bd72 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 4 Nov 2008 20:28:56 +0000 Subject: Clarified broker/agent banks in method names. Added new accessor methods for bank values git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@711384 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 3ceb41b7a6..e173a4b864 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -671,7 +671,7 @@ class Session: self._encodeValue(sendCodec, argList[aIdx], arg.type) aIdx += 1 smsg = broker._message(sendCodec.encoded, "agent.%d.%d" % - (objectId.getBroker(), objectId.getBank())) + (objectId.getBrokerBank(), objectId.getAgentBank())) broker._send(smsg) return seq return None @@ -866,7 +866,7 @@ class ObjectId: def __repr__(self): return "%d-%d-%d-%d-%d" % (self.getFlags(), self.getSequence(), - self.getBroker(), self.getBank(), self.getObject()) + self.getBrokerBank(), self.getAgentBank(), self.getObject()) def index(self): return (self.first, self.second) @@ -877,10 +877,10 @@ class ObjectId: def getSequence(self): return (self.first & 0x0FFF000000000000) >> 48 - def getBroker(self): + def getBrokerBank(self): return (self.first & 0x0000FFFFF0000000) >> 28 - def getBank(self): + def getAgentBank(self): return self.first & 0x000000000FFFFFFF def getObject(self): @@ -1014,7 +1014,7 @@ class Object(object): self._session._encodeValue(sendCodec, args[aIdx], arg.type) aIdx += 1 smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % - (self._objectId.getBroker(), self._objectId.getBank())) + (self._objectId.getBrokerBank(), self._objectId.getAgentBank())) if synchronous: try: self._broker.cv.acquire() @@ -1109,6 +1109,9 @@ class Broker: """ Get broker's unique identifier (UUID) """ return self.brokerId + def getBrokerBank(self): + return 1 + def getSessionId(self): """ Get the identifier of the AMQP session to the broker """ return self.amqpSessionId @@ -1316,6 +1319,9 @@ class Agent: def getBroker(self): return self.broker + def getAgentBank(self): + return self.bank + class Event: """ """ def __init__(self, session, broker, codec): -- cgit v1.2.1 From abf152ac74b5e1dfb8ac3876eb2a74650191be35 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 4 Nov 2008 23:01:57 +0000 Subject: Added bank numbers to the routing key of a QMF heartbeat message. This is used by the console to identify which agent sent the indication. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@711458 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index e173a4b864..55b0a7fd94 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -489,10 +489,21 @@ class Session: if self.console: self.console.methodResponse(broker, seq, result) - def _handleHeartbeatInd(self, broker, codec, seq): + def _handleHeartbeatInd(self, broker, codec, seq, msg): + brokerBank = 1 + agentBank = 0 + dp = msg.get("delivery_properties") + if dp: + key = dp["routing_key"] + keyElements = key.split(".") + if len(keyElements) == 4: + brokerBank = int(keyElements[2]) + agentBank = int(keyElements[3]) + + agent = broker.getAgent(brokerBank, agentBank) timestamp = codec.read_uint64() if self.console != None: - self.console.heartbeat(None, timestamp) + self.console.heartbeat(agent, timestamp) def _handleEventInd(self, broker, codec, seq): if self.console != None: @@ -1086,7 +1097,7 @@ class Broker: self.authUser = authUser self.authPass = authPass self.agents = {} - self.agents[0] = Agent(self, "1.0", "BrokerAgent") + self.agents["1.0"] = Agent(self, "1.0", "BrokerAgent") self.topicBound = False self.cv = Condition() self.syncInFlight = False @@ -1112,6 +1123,12 @@ class Broker: def getBrokerBank(self): return 1 + def getAgent(self, brokerBank, agentBank): + bankKey = "%d.%d" % (brokerBank, agentBank) + if bankKey in self.agents: + return self.agents[bankKey] + return None + def getSessionId(self): """ Get the identifier of the AMQP session to the broker """ return self.amqpSessionId @@ -1287,7 +1304,7 @@ class Broker: elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) elif opcode == 'q': self.session._handleClassInd (self, codec, seq) elif opcode == 'm': self.session._handleMethodResp (self, codec, seq) - elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq) + elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq, msg) elif opcode == 'e': self.session._handleEventInd (self, codec, seq) elif opcode == 's': self.session._handleSchemaResp (self, codec, seq) elif opcode == 'c': self.session._handleContentInd (self, codec, seq, prop=True) -- cgit v1.2.1 From e4b55926dc53983f421e2c44b854b0d61e839b90 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 5 Nov 2008 17:16:43 +0000 Subject: Added support for push-routes and queue-based routes. Cleaned up the code a bit. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@711628 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 294 +++++++++++++++++++++++++--------------- 1 file changed, 182 insertions(+), 112 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 47eeef3ff2..e64889b2af 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -25,80 +25,81 @@ import socket import os from qpid import qmfconsole -def Usage (): - print "Usage: qpid-route [OPTIONS] link add " - print " qpid-route [OPTIONS] link del " - print " qpid-route [OPTIONS] link list []" - print " qpid-route [OPTIONS] link map []" +def Usage(): + print "Usage: qpid-route [OPTIONS] dynamic add [tag] [exclude-list]" + print " qpid-route [OPTIONS] dynamic del " print print " qpid-route [OPTIONS] route add [tag] [exclude-list]" print " qpid-route [OPTIONS] route del " + print " qpid-route [OPTIONS] queue add " + print " qpid-route [OPTIONS] queue del " print " qpid-route [OPTIONS] route list []" print " qpid-route [OPTIONS] route flush []" - print " qpid-route [OPTIONS] dynamic add [tag] [exclude-list]" - print " qpid-route [OPTIONS] dynamic del " + print " qpid-route [OPTIONS] route map []" + print + print " qpid-route [OPTIONS] link add " + print " qpid-route [OPTIONS] link del " + print " qpid-route [OPTIONS] link list []" print print "Options:" print " -v [ --verbose ] Verbose output" print " -q [ --quiet ] Quiet output, don't print duplicate warnings" print " -d [ --durable ] Added configuration shall be durable" print " -e [ --del-empty-link ] Delete link after deleting last route on the link" + print " -s [ --src-local ] Make connection to source broker (push route)" print " -t [ --transport ]" print " Specify transport to use for links, defaults to tcp" print print " dest-broker and src-broker are in the form: [username/password@] hostname | ip-address [:]" print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print - sys.exit (1) + sys.exit(1) -_verbose = False -_quiet = False -_durable = False -_dellink = False +_verbose = False +_quiet = False +_durable = False +_dellink = False +_srclocal = False _transport = "tcp" class RouteManager: - def __init__ (self, destBroker): - self.dest = qmfconsole.BrokerURL(destBroker) - self.src = None + def __init__(self, localBroker): + self.local = qmfconsole.BrokerURL(localBroker) + self.remote = None self.qmf = qmfconsole.Session() - self.broker = self.qmf.addBroker(destBroker) + self.broker = self.qmf.addBroker(localBroker) - def Disconnect (self): + def disconnect(self): self.qmf.delBroker(self.broker) - def getLink (self): + def getLink(self): links = self.qmf.getObjects(_class="link") for link in links: - if self.src.match(link.host, link.port): + if self.remote.match(link.host, link.port): return link return None - def AddLink (self, srcBroker): - self.src = qmfconsole.BrokerURL(srcBroker) - if self.dest.match(self.src.host, self.src.port): - print "Linking broker to itself is not permitted" - sys.exit(1) + def addLink(self, remoteBroker): + self.remote = qmfconsole.BrokerURL(remoteBroker) + if self.local.match(self.remote.host, self.remote.port): + raise Exception("Linking broker to itself is not permitted") brokers = self.qmf.getObjects(_class="broker") broker = brokers[0] link = self.getLink() - if link != None: - raise Exception("Link already exists") - - if self.src.authName == "anonymous": - mech = "ANONYMOUS" - else: - mech = "PLAIN" - res = broker.connect(self.src.host, self.src.port, _durable, - mech, self.src.authName, self.src.authPass, - _transport) - if _verbose: - print "Connect method returned:", res.status, res.text - link = self.getLink() + if link == None: + if self.remote.authName == "anonymous": + mech = "ANONYMOUS" + else: + mech = "PLAIN" + res = broker.connect(self.remote.host, self.remote.port, _durable, + mech, self.remote.authName, self.remote.authPass, + _transport) + if _verbose: + print "Connect method returned:", res.status, res.text - def DelLink (self, srcBroker): - self.src = qmfconsole.BrokerURL(srcBroker) + def delLink(self, remoteBroker): + self.remote = qmfconsole.BrokerURL(remoteBroker) brokers = self.qmf.getObjects(_class="broker") broker = brokers[0] link = self.getLink() @@ -109,7 +110,7 @@ class RouteManager: if _verbose: print "Close method returned:", res.status, res.text - def ListLinks (self): + def listLinks(self): links = self.qmf.getObjects(_class="link") if len(links) == 0: print "No Links Found" @@ -121,14 +122,14 @@ class RouteManager: print "%-16s%-8d %c %-18s%s" % \ (link.host, link.port, YN(link.durable), link.state, link.lastError) - def MapLinks(self): + def mapRoutes(self): qmf = self.qmf print print "Finding Linked Brokers:" brokerList = {} - brokerList[self.dest.name()] = self.broker - print " %s... Ok" % self.dest + brokerList[self.local.name()] = self.broker + print " %s... Ok" % self.local added = True while added: @@ -155,8 +156,7 @@ class RouteManager: fedExchanges.append(bridge.src) if len(fedExchanges) == 0: print " none found" - else: - print + print for ex in fedExchanges: print " Exchange %s:" % ex @@ -180,71 +180,124 @@ class RouteManager: bridges = qmf.getObjects(_class="bridge", dynamic=False) if len(bridges) == 0: print " none found" - else: - print + print for bridge in bridges: link = bridge._linkRef_ fromUrl = "%s:%s" % (link.host, link.port) toUrl = bridge.getBroker().getUrl() - print " %s(%s) <= %s(%s) key=%s" % (toUrl, bridge.dest, fromUrl, bridge.src, bridge.key) + leftType = "ex" + rightType = "ex" + if bridge.srcIsLocal: + arrow = "=>" + left = bridge.src + right = bridge.dest + if bridge.srcIsQueue: + leftType = "queue" + else: + arrow = "<=" + left = bridge.dest + right = bridge.src + if bridge.srcIsQueue: + rightType = "queue" + + if bridge.srcIsQueue: + print " %s(%s=%s) %s %s(%s=%s)" % \ + (toUrl, leftType, left, arrow, fromUrl, rightType, right) + else: + print " %s(%s=%s) %s %s(%s=%s) key=%s" % \ + (toUrl, leftType, left, arrow, fromUrl, rightType, right, bridge.key) print for broker in brokerList: - if broker != self.dest.name(): + if broker != self.local.name(): qmf.delBroker(brokerList[broker]) - def AddRoute (self, srcBroker, exchange, routingKey, tag, excludes, dynamic=False): - self.src = qmfconsole.BrokerURL(srcBroker) - if self.dest.match(self.src.host, self.src.port): - raise Exception("Linking broker to itself is not permitted") - - brokers = self.qmf.getObjects(_class="broker") - broker = brokers[0] + def addRoute(self, remoteBroker, exchange, routingKey, tag, excludes, dynamic=False): + if dynamic and _srclocal: + raise Exception("--src-local is not permitted on dynamic routes") + self.addLink(remoteBroker) link = self.getLink() if link == None: - if _verbose: - print "Inter-broker link not found, creating..." + raise Exception("Link failed to create") - if self.src.authName == "anonymous": - mech = "ANONYMOUS" - else: - mech = "PLAIN" - res = broker.connect(self.src.host, self.src.port, _durable, - mech, self.src.authName, self.src.authPass, - _transport) - if _verbose: - print "Connect method returned:", res.status, res.text - link = self.getLink() + bridges = self.qmf.getObjects(_class="bridge") + for bridge in bridges: + if bridge.linkRef == link.getObjectId() and \ + bridge.dest == exchange and bridge.key == routingKey and not bridge.srcIsQueue: + if not _quiet: + raise Exception("Duplicate Route - ignoring: %s(%s)" % (exchange, routingKey)) + sys.exit(0) + if _verbose: + print "Creating inter-broker binding..." + res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, _srclocal, dynamic) + if res.status != 0: + raise Exception(res.text) + if _verbose: + print "Bridge method returned:", res.status, res.text + + def addQueueRoute(self, remoteBroker, exchange, queue): + self.addLink(remoteBroker) + link = self.getLink() if link == None: - raise Exception("Protocol Error - Missing link ID") + raise Exception("Link failed to create") bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: if bridge.linkRef == link.getObjectId() and \ - bridge.dest == exchange and bridge.key == routingKey: + bridge.dest == exchange and bridge.src == queue and bridge.srcIsQueue: if not _quiet: - raise Exception("Duplicate Route - ignoring: %s(%s)" % (exchange, routingKey)) - sys.exit (0) + raise Exception("Duplicate Route - ignoring: %s(%s)" % (exchange, queue)) + sys.exit(0) if _verbose: print "Creating inter-broker binding..." - res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, False, dynamic) + res = link.bridge(_durable, queue, exchange, "", "", "", True, _srclocal, False) if res.status != 0: raise Exception(res.text) if _verbose: print "Bridge method returned:", res.status, res.text - def DelRoute (self, srcBroker, exchange, routingKey, dynamic=False): - self.src = qmfconsole.BrokerURL(srcBroker) + def delQueueRoute(self, remoteBroker, exchange, queue): + self.remote = qmfconsole.BrokerURL(remoteBroker) link = self.getLink() if link == None: if not _quiet: - raise Exception("No link found from %s to %s" % (self.src.name(), self.dest.name())) - sys.exit (0) + raise Exception("No link found from %s to %s" % (self.remote.name(), self.local.name())) + sys.exit(0) + + bridges = self.qmf.getObjects(_class="bridge") + for bridge in bridges: + if bridge.linkRef == link.getObjectId() and \ + bridge.dest == exchange and bridge.src == queue and bridge.srcIsQueue: + if _verbose: + print "Closing bridge..." + res = bridge.close() + if res.status != 0: + raise Exception("Error closing bridge: %d - %s" % (res.status, res.text)) + if len(bridges) == 1 and _dellink: + link = self.getLink() + if link == None: + sys.exit(0) + if _verbose: + print "Last bridge on link, closing link..." + res = link.close() + if res.status != 0: + raise Exception("Error closing link: %d - %s" % (res.status, res.text)) + sys.exit(0) + if not _quiet: + raise Exception("Route not found") + + def delRoute(self, remoteBroker, exchange, routingKey, dynamic=False): + self.remote = qmfconsole.BrokerURL(remoteBroker) + link = self.getLink() + if link == None: + if not _quiet: + raise Exception("No link found from %s to %s" % (self.remote.name(), self.local.name())) + sys.exit(0) bridges = self.qmf.getObjects(_class="bridge") for bridge in bridges: @@ -255,20 +308,20 @@ class RouteManager: res = bridge.close() if res.status != 0: raise Exception("Error closing bridge: %d - %s" % (res.status, res.text)) - if len (bridges) == 1 and _dellink: - link = self.getLink () + if len(bridges) == 1 and _dellink: + link = self.getLink() if link == None: - sys.exit (0) + sys.exit(0) if _verbose: print "Last bridge on link, closing link..." res = link.close() if res.status != 0: raise Exception("Error closing link: %d - %s" % (res.status, res.text)) - sys.exit (0) + sys.exit(0) if not _quiet: raise Exception("Route not found") - def ListRoutes (self): + def listRoutes(self): links = self.qmf.getObjects(_class="link") bridges = self.qmf.getObjects(_class="bridge") @@ -283,9 +336,9 @@ class RouteManager: keyText = "" else: keyText = bridge.key - print "%s %s:%d %s %s" % (self.dest.name(), myLink.host, myLink.port, bridge.dest, keyText) + print "%s %s:%d %s %s" % (self.local.name(), myLink.host, myLink.port, bridge.dest, keyText) - def ClearAllRoutes (self): + def clearAllRoutes(self): links = self.qmf.getObjects(_class="link") bridges = self.qmf.getObjects(_class="bridge") @@ -347,10 +400,10 @@ def YN(val): ## try: - longOpts = ("verbose", "quiet", "durable", "del-empty-link", "transport=") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "vqdet:", longOpts) + longOpts = ("verbose", "quiet", "durable", "del-empty-link", "src-local", "transport=") + (optlist, cargs) = getopt.gnu_getopt(sys.argv[1:], "vqdest:", longOpts) except: - Usage () + Usage() for opt in optlist: if opt[0] == "-v" or opt[0] == "--verbose": @@ -361,77 +414,94 @@ for opt in optlist: _durable = True if opt[0] == "-e" or opt[0] == "--del-empty-link": _dellink = True + if opt[0] == "-s" or opt[0] == "--src-local": + _srclocal = True if opt[0] == "-t" or opt[0] == "--transport": _transport = opt[1] -nargs = len (cargs) +nargs = len(cargs) if nargs < 2: - Usage () + Usage() if nargs == 2: - destBroker = "localhost" + localBroker = "localhost" else: - destBroker = cargs[2] + if _srclocal: + localBroker = cargs[3] + remoteBroker = cargs[2] + else: + localBroker = cargs[2] + if nargs > 3: + remoteBroker = cargs[3] group = cargs[0] cmd = cargs[1] try: - rm = RouteManager (destBroker) + rm = RouteManager(localBroker) if group == "link": if cmd == "add": if nargs != 4: Usage() - rm.AddLink (cargs[3]) + rm.addLink(remoteBroker) elif cmd == "del": if nargs != 4: Usage() - rm.DelLink (cargs[3]) + rm.delLink(remoteBroker) elif cmd == "list": - rm.ListLinks() - elif cmd == "map": - rm.MapLinks() + rm.listLinks() elif group == "dynamic": if cmd == "add": if nargs < 5 or nargs > 7: - Usage () + Usage() tag = "" excludes = "" if nargs > 5: tag = cargs[5] if nargs > 6: excludes = cargs[6] - rm.AddRoute (cargs[3], cargs[4], "", tag, excludes, dynamic=True) + rm.addRoute(remoteBroker, cargs[4], "", tag, excludes, dynamic=True) elif cmd == "del": if nargs != 5: - Usage () + Usage() else: - rm.DelRoute (cargs[3], cargs[4], "", dynamic=True) + rm.delRoute(remoteBroker, cargs[4], "", dynamic=True) elif group == "route": if cmd == "add": if nargs < 6 or nargs > 8: - Usage () + Usage() tag = "" excludes = "" if nargs > 6: tag = cargs[6] - if nargs > 7: excludes = cargs[7] - rm.AddRoute (cargs[3], cargs[4], cargs[5], tag, excludes, dynamic=False) + if nargs > 7: excludes = cargs[7] + rm.addRoute(remoteBroker, cargs[4], cargs[5], tag, excludes, dynamic=False) elif cmd == "del": if nargs != 6: - Usage () - else: - rm.DelRoute (cargs[3], cargs[4], cargs[5], dynamic=False) + Usage() + rm.delRoute(remoteBroker, cargs[4], cargs[5], dynamic=False) + elif cmd == "map": + rm.mapRoutes() else: - if cmd == "list": - rm.ListRoutes () + if cmd == "list": + rm.listRoutes() elif cmd == "flush": - rm.ClearAllRoutes () + rm.clearAllRoutes() else: - Usage () + Usage() + + elif group == "queue": + if nargs != 6: + Usage() + if cmd == "add": + rm.addQueueRoute(remoteBroker, exchange=cargs[4], queue=cargs[5]) + elif cmd == "del": + rm.delQueueRoute(remoteBroker, exchange=cargs[4], queue=cargs[5]) + else: + Usage() except Exception,e: print "Failed:", e.args[0] sys.exit(1) -rm.Disconnect () +rm.disconnect() -- cgit v1.2.1 From 67d0d5457591f51a5e9f80bfd4874c2ff6745a89 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 10 Nov 2008 15:19:29 +0000 Subject: added a timestamp class to qpid.datatypes and modified codec to use it for AMQP's datetime type; this fixes support of datetime within maps where formerly decoding and reencoding the same value would switch an entry in a map from an AMQP datetime to an AMQP uint64 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@712673 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 11 ++++++---- qpid/python/qpid/datatypes.py | 50 +++++++++++++++++++++++++++++++++++++++++- qpid/python/qpid/spec010.py | 6 +++-- qpid/python/tests/codec010.py | 36 +++++++++++++++++++++++------- qpid/python/tests/datatypes.py | 32 +++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 15 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index c2ea7ed512..f34025ef17 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -17,8 +17,9 @@ # under the License. # +import datetime from packer import Packer -from datatypes import serial, RangedSet, Struct +from datatypes import serial, timestamp, RangedSet, Struct class CodecException(Exception): pass @@ -103,9 +104,11 @@ class Codec(Packer): self.pack("!q", n) def read_datetime(self): - return self.read_uint64() - def write_datetime(self, n): - self.write_uint64(n) + return timestamp(self.read_uint64()) + def write_datetime(self, t): + if isinstance(t, datetime.datetime): + t = timestamp(t) + self.write_uint64(t) def read_double(self): return self.unpack("!d") diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 7150caded2..38fc163dd9 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -17,7 +17,7 @@ # under the License. # -import threading, struct +import threading, struct, datetime, time class Struct: @@ -296,3 +296,51 @@ class UUID: def __repr__(self): return "UUID(%r)" % str(self) + +class timestamp(float): + + def __new__(cls, obj=None): + if obj is None: + obj = time.time() + elif isinstance(obj, datetime.datetime): + obj = time.mktime(obj.timetuple()) + 1e-6 * obj.microsecond + return super(timestamp, cls).__new__(cls, obj) + + def datetime(self): + return datetime.datetime.fromtimestamp(self) + + def __add__(self, other): + if isinstance(other, datetime.timedelta): + return timestamp(self.datetime() + other) + else: + return timestamp(float(self) + other) + + def __sub__(self, other): + if isinstance(other, datetime.timedelta): + return timestamp(self.datetime() - other) + else: + return timestamp(float(self) - other) + + def __radd__(self, other): + if isinstance(other, datetime.timedelta): + return timestamp(self.datetime() + other) + else: + return timestamp(other + float(self)) + + def __rsub__(self, other): + if isinstance(other, datetime.timedelta): + return timestamp(self.datetime() - other) + else: + return timestamp(other - float(self)) + + def __neg__(self): + return timestamp(-float(self)) + + def __pos__(self): + return self + + def __abs__(self): + return timestamp(abs(float(self))) + + def __repr__(self): + return "timestamp(%r)" % float(self) diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 23966e6176..cbc85a5e8b 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -17,7 +17,7 @@ # under the License. # -import os, cPickle, datatypes +import os, cPickle, datatypes, datetime from codec010 import StringCodec from util import mtime, fill @@ -477,7 +477,9 @@ class Spec(Node): None.__class__: "void", list: "list", tuple: "list", - dict: "map" + dict: "map", + datatypes.timestamp: "datetime", + datetime.datetime: "datetime" } def __init__(self, major, minor, port, children): diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index bbe894b7b3..5f9f853744 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -17,22 +17,27 @@ # under the License. # +import time + from unittest import TestCase from qpid.spec010 import load from qpid.codec010 import StringCodec from qpid.testlib import testrunner +from qpid.datatypes import timestamp class CodecTest(TestCase): def setUp(self): self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) - def check(self, type, value): + def check(self, type, value, compare=True): t = self.spec[type] sc = StringCodec(self.spec) t.encode(sc, value) decoded = t.decode(sc) - assert decoded == value, "%s, %s" % (decoded, value) + if compare: + assert decoded == value, "%s, %s" % (decoded, value) + return decoded def testMapString(self): self.check("map", {"string": "this is a test"}) @@ -43,6 +48,15 @@ class CodecTest(TestCase): def testMapLong(self): self.check("map", {"long": 2**32}) + def testMapTimestamp(self): + decoded = self.check("map", {"timestamp": timestamp(0)}) + assert isinstance(decoded["timestamp"], timestamp) + + def testMapDatetime(self): + decoded = self.check("map", {"datetime": timestamp(0).datetime()}, compare=False) + assert isinstance(decoded["datetime"], timestamp) + assert decoded["datetime"] == 0.0 + def testMapNone(self): self.check("map", {"none": None}) @@ -53,12 +67,14 @@ class CodecTest(TestCase): self.check("map", {"list": [1, "two", 3.0, -4]}) def testMapAll(self): - self.check("map", {"string": "this is a test", - "int": 3, - "long": 2**32, - "none": None, - "map": {"string": "nested map"}, - "list": [1, "two", 3.0, -4]}) + decoded = self.check("map", {"string": "this is a test", + "int": 3, + "long": 2**32, + "timestamp": timestamp(0), + "none": None, + "map": {"string": "nested map"}, + "list": [1, "two", 3.0, -4]}) + assert isinstance(decoded["timestamp"], timestamp) def testMapEmpty(self): self.check("map", {}) @@ -90,3 +106,7 @@ class CodecTest(TestCase): def testInt16(self): self.check("int16", 3) self.check("int16", -3) + + def testDatetime(self): + self.check("datetime", timestamp(0)) + self.check("datetime", timestamp(long(time.time()))) diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index ef98e81da0..4b9e1bcc78 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -223,3 +223,35 @@ class MessageTest(TestCase): assert m.get("fragment_properties") is None assert m.get("message_properties") == self.mp assert m.get("delivery_properties") == self.dp + +class TimestampTest(TestCase): + + def check(self, expected, *values): + for v in values: + assert isinstance(v, timestamp) + assert v == expected + assert v == timestamp(expected) + + def testAdd(self): + self.check(4.0, + timestamp(2.0) + 2.0, + 2.0 + timestamp(2.0)) + + def testSub(self): + self.check(2.0, + timestamp(4.0) - 2.0, + 4.0 - timestamp(2.0)) + + def testNeg(self): + self.check(-4.0, -timestamp(4.0)) + + def testPos(self): + self.check(+4.0, +timestamp(4.0)) + + def testAbs(self): + self.check(4.0, abs(timestamp(-4.0))) + + def testConversion(self): + dt = timestamp(0).datetime() + t = timestamp(dt) + assert t == 0 -- cgit v1.2.1 From ced8b34442a004fc76a268754d4d23854c7c0af3 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 10 Nov 2008 15:41:42 +0000 Subject: fixed potential race condition in qpid.util.listen; added asserts to internal test cases to ensure that the test only proceeds when the server is bound git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@712679 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/util.py | 2 +- qpid/python/tests/assembler.py | 1 + qpid/python/tests/connection.py | 1 + qpid/python/tests/framer.py | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/util.py b/qpid/python/qpid/util.py index 1ca616f1f5..bb7f5090df 100644 --- a/qpid/python/qpid/util.py +++ b/qpid/python/qpid/util.py @@ -34,8 +34,8 @@ def listen(host, port, predicate = lambda: True, bound = lambda: None): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, port)) - bound() sock.listen(5) + bound() while predicate(): s, a = sock.accept() yield s diff --git a/qpid/python/tests/assembler.py b/qpid/python/tests/assembler.py index b76924e59d..f4e37084b6 100644 --- a/qpid/python/tests/assembler.py +++ b/qpid/python/tests/assembler.py @@ -47,6 +47,7 @@ class AssemblerTest(TestCase): self.server.start() started.wait(3) + assert started.isSet() def tearDown(self): self.running = False diff --git a/qpid/python/tests/connection.py b/qpid/python/tests/connection.py index 23e0c937fb..512fa62189 100644 --- a/qpid/python/tests/connection.py +++ b/qpid/python/tests/connection.py @@ -87,6 +87,7 @@ class ConnectionTest(TestCase): self.server.start() started.wait(3) + assert started.isSet() def tearDown(self): self.running = False diff --git a/qpid/python/tests/framer.py b/qpid/python/tests/framer.py index 05bb467bbe..e99166721c 100644 --- a/qpid/python/tests/framer.py +++ b/qpid/python/tests/framer.py @@ -46,6 +46,7 @@ class FramerTest(TestCase): self.server.start() started.wait(3) + assert started.isSet() def tearDown(self): self.running = False -- cgit v1.2.1 From 1f9851ac7d96bc916135ec0d4f78082dd0747082 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 10 Nov 2008 19:35:31 +0000 Subject: use long syntax for flow control literals, this will hopefully avoid future warnings on older versions of python git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@712726 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index f500ab112f..4a7ecbc28a 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -346,7 +346,7 @@ class Incoming(Queue): def start(self): self.session.message_set_flow_mode(self.destination, self.session.flow_mode.credit) for unit in self.session.credit_unit.values(): - self.session.message_flow(self.destination, unit, 0xFFFFFFFF) + self.session.message_flow(self.destination, unit, 0xFFFFFFFFL) def stop(self): self.session.message_cancel(self.destination) -- cgit v1.2.1 From 3d00ce05395f5196ae576762090db2a6f701fc42 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 11 Nov 2008 16:52:44 +0000 Subject: QPID-1448 - Management APIs and CLI tools are not unicode safe The management APIs (old and new) now use unicode strings as the default. The CLI utilities now use the preferred shell encoding to support multibyte characters in the command line and in managed objects. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@713079 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 10 +++- qpid/python/commands/qpid-route | 17 ++++-- qpid/python/qpid/disp.py | 6 +- qpid/python/qpid/management.py | 26 ++++----- qpid/python/qpid/managementdata.py | 7 ++- qpid/python/qpid/qmfconsole.py | 110 +++++++++++++++++++------------------ 6 files changed, 100 insertions(+), 76 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 6917d4c5c0..e2d3108a33 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -22,6 +22,7 @@ import os import getopt import sys +import locale from qpid import qmfconsole _recursive = False @@ -305,10 +306,16 @@ try: longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=", "last-value-queue", "optimistic-consume", "sequence", "ive") - (optlist, cargs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) + (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () +try: + encoding = locale.getpreferredencoding() + cargs = [a.decode(encoding) for a in encArgs] +except: + cargs = encArgs + for opt in optlist: if opt[0] == "-b" or opt[0] == "--bindings": _recursive = True @@ -381,6 +388,7 @@ try: Usage () except Exception,e: print "Failed:", e.message + raise sys.exit(1) bm.Disconnect() diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index e64889b2af..6fbd11b684 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -23,6 +23,7 @@ import getopt import sys import socket import os +import locale from qpid import qmfconsole def Usage(): @@ -116,11 +117,11 @@ class RouteManager: print "No Links Found" else: print - print "Host Port Durable State Last Error" - print "===================================================================" + print "Host Port Transport Durable State Last Error" + print "=============================================================================" for link in links: - print "%-16s%-8d %c %-18s%s" % \ - (link.host, link.port, YN(link.durable), link.state, link.lastError) + print "%-16s%-8d%-13s%c %-18s%s" % \ + (link.host, link.port, link.transport, YN(link.durable), link.state, link.lastError) def mapRoutes(self): qmf = self.qmf @@ -401,10 +402,16 @@ def YN(val): try: longOpts = ("verbose", "quiet", "durable", "del-empty-link", "src-local", "transport=") - (optlist, cargs) = getopt.gnu_getopt(sys.argv[1:], "vqdest:", longOpts) + (optlist, encArgs) = getopt.gnu_getopt(sys.argv[1:], "vqdest:", longOpts) except: Usage() +try: + encoding = locale.getpreferredencoding() + cargs = [a.decode(encoding) for a in encArgs] +except: + cargs = encArgs + for opt in optlist: if opt[0] == "-v" or opt[0] == "--verbose": _verbose = True diff --git a/qpid/python/qpid/disp.py b/qpid/python/qpid/disp.py index d697cd0136..e46cb33c60 100644 --- a/qpid/python/qpid/disp.py +++ b/qpid/python/qpid/disp.py @@ -40,7 +40,7 @@ class Display: for head in heads: width = len (head) for row in rows: - cellWidth = len (str (row[col])) + cellWidth = len (unicode (row[col])) if cellWidth > width: width = cellWidth colWidth.append (width + self.tableSpacing) @@ -60,9 +60,9 @@ class Display: line = self.tablePrefix col = 0 for width in colWidth: - line = line + str (row[col]) + line = line + unicode (row[col]) if col < len (heads) - 1: - for i in range (width - len (str (row[col]))): + for i in range (width - len (unicode (row[col]))): line = line + " " col = col + 1 print line diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 863e25e24c..e91eb48517 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -479,7 +479,7 @@ class managementClient: elif typecode == 5: data = codec.read_uint8 () elif typecode == 6: - data = str (codec.read_str8 ()) + data = codec.read_str8 () elif typecode == 7: data = codec.read_str16 () elif typecode == 8: # ABSTIME @@ -534,7 +534,7 @@ class managementClient: def handleMethodReply (self, ch, codec, sequence): status = codec.read_uint32 () - sText = str (codec.read_str16 ()) + sText = codec.read_str16 () data = self.seqMgr.release (sequence) if data == None: @@ -570,7 +570,7 @@ class managementClient: def handleCommandComplete (self, ch, codec, seq): code = codec.read_uint32 () - text = str (codec.read_str8 ()) + text = codec.read_str8 () data = (seq, code, text) context = self.seqMgr.release (seq) if context == "outstanding": @@ -597,7 +597,7 @@ class managementClient: ch.send ("qpid.management", smsg) def handlePackageInd (self, ch, codec): - pname = str (codec.read_str8 ()) + pname = codec.read_str8 () if pname not in self.packages: self.packages[pname] = {} @@ -614,8 +614,8 @@ class managementClient: kind = codec.read_uint8() if kind != 1: # This API doesn't handle new-style events return - pname = str (codec.read_str8()) - cname = str (codec.read_str8()) + pname = codec.read_str8() + cname = codec.read_str8() hash = codec.read_bin128() if pname not in self.packages: return @@ -642,10 +642,10 @@ class managementClient: return timestamp = codec.read_uint64() objId = objectId(codec) - packageName = str(codec.read_str8()) - className = str(codec.read_str8()) + packageName = codec.read_str8() + className = codec.read_str8() hash = codec.read_bin128() - name = str(codec.read_str8()) + name = codec.read_str8() classKey = (packageName, className, hash) if classKey not in self.schema: return; @@ -669,8 +669,8 @@ class managementClient: kind = codec.read_uint8() if kind != 1: # This API doesn't handle new-style events return - packageName = str (codec.read_str8 ()) - className = str (codec.read_str8 ()) + packageName = codec.read_str8 () + className = codec.read_str8 () hash = codec.read_bin128 () configCount = codec.read_uint16 () instCount = codec.read_uint16 () @@ -808,8 +808,8 @@ class managementClient: if cls == 'I' and self.instCb == None: return - packageName = str (codec.read_str8 ()) - className = str (codec.read_str8 ()) + packageName = codec.read_str8 () + className = codec.read_str8 () hash = codec.read_bin128 () classKey = (packageName, className, hash) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 5bceb48b7d..c3e66c29a1 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -29,6 +29,7 @@ import re import socket import struct import os +import locale from qpid.management import managementChannel, managementClient from threading import Lock from disp import Display @@ -727,7 +728,11 @@ class ManagementData: self.schemaTable (data) def do_call (self, data): - tokens = data.split () + encTokens = data.split () + try: + tokens = [a.decode(locale.getpreferredencoding()) for a in encArgs] + except: + tokens = encTokens if len (tokens) < 2: print "Not enough arguments supplied" return diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 55b0a7fd94..7d7aeee44f 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -26,7 +26,7 @@ import socket import re from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed -from qpid.datatypes import uuid4, Message, RangedSet +from qpid.datatypes import UUID, uuid4, Message, RangedSet from qpid.util import connect, ssl, URL from qpid.codec010 import StringCodec as Codec from threading import Lock, Condition @@ -384,7 +384,7 @@ class Session: pass def _handleBrokerResp(self, broker, codec, seq): - broker.brokerId = codec.read_uuid() + broker.brokerId = UUID(codec.read_uuid()) if self.console != None: self.console.brokerInfo(broker) @@ -397,7 +397,7 @@ class Session: broker._send(smsg) def _handlePackageInd(self, broker, codec, seq): - pname = str(codec.read_str8()) + pname = codec.read_str8() notify = False try: self.cv.acquire() @@ -420,7 +420,7 @@ class Session: def _handleCommandComplete(self, broker, codec, seq): code = codec.read_uint32() - text = str(codec.read_str8()) + text = codec.read_str8() context = self.seqMgr._release(seq) if context == self._CONTEXT_STARTUP: broker._decOutstanding() @@ -442,8 +442,8 @@ class Session: def _handleClassInd(self, broker, codec, seq): kind = codec.read_uint8() - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) + pname = codec.read_str8() + cname = codec.read_str8() hash = codec.read_bin128() unknown = False @@ -469,7 +469,7 @@ class Session: def _handleMethodResp(self, broker, codec, seq): code = codec.read_uint32() - text = str(codec.read_str16()) + text = codec.read_str16() outArgs = {} method, synchronous = self.seqMgr._release(seq) if code == 0: @@ -512,8 +512,8 @@ class Session: def _handleSchemaResp(self, broker, codec, seq): kind = codec.read_uint8() - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) + pname = codec.read_str8() + cname = codec.read_str8() hash = codec.read_bin128() classKey = (pname, cname, hash) _class = SchemaClass(kind, classKey, codec) @@ -529,8 +529,8 @@ class Session: self.console.newClass(kind, classKey) def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) + pname = codec.read_str8() + cname = codec.read_str8() hash = codec.read_bin128() classKey = (pname, cname, hash) try: @@ -585,7 +585,7 @@ class Session: elif typecode == 2: data = codec.read_uint16() # U16 elif typecode == 3: data = codec.read_uint32() # U32 elif typecode == 4: data = codec.read_uint64() # U64 - elif typecode == 6: data = str(codec.read_str8()) # SSTR + elif typecode == 6: data = codec.read_str8() # SSTR elif typecode == 7: data = codec.read_str16() # LSTR elif typecode == 8: data = codec.read_int64() # ABSTIME elif typecode == 9: data = codec.read_uint64() # DELTATIME @@ -593,7 +593,7 @@ class Session: elif typecode == 11: data = codec.read_uint8() != 0 # BOOL elif typecode == 12: data = codec.read_float() # FLOAT elif typecode == 13: data = codec.read_double() # DOUBLE - elif typecode == 14: data = codec.read_uuid() # UUID + elif typecode == 14: data = UUID(codec.read_uuid()) # UUID elif typecode == 15: data = codec.read_map() # FTABLE elif typecode == 16: data = codec.read_int8() # S8 elif typecode == 17: data = codec.read_int16() # S16 @@ -617,7 +617,7 @@ class Session: elif typecode == 11: codec.write_uint8 (int(value)) # BOOL elif typecode == 12: codec.write_float (float(value)) # FLOAT elif typecode == 13: codec.write_double (double(value)) # DOUBLE - elif typecode == 14: codec.write_uuid (value) # UUID + elif typecode == 14: codec.write_uuid (value.bytes) # UUID elif typecode == 15: codec.write_map (value) # FTABLE elif typecode == 16: codec.write_int8 (int(value)) # S8 elif typecode == 17: codec.write_int16 (int(value)) # S16 @@ -628,26 +628,26 @@ class Session: def _displayValue(self, value, typecode): """ """ - if typecode == 1: return str(value) - elif typecode == 2: return str(value) - elif typecode == 3: return str(value) - elif typecode == 4: return str(value) - elif typecode == 6: return str(value) - elif typecode == 7: return str(value) - elif typecode == 8: return strftime("%c", gmtime(value / 1000000000)) - elif typecode == 9: return str(value) - elif typecode == 10: return value.__repr__() + if typecode == 1: return unicode(value) + elif typecode == 2: return unicode(value) + elif typecode == 3: return unicode(value) + elif typecode == 4: return unicode(value) + elif typecode == 6: return value + elif typecode == 7: return value + elif typecode == 8: return unicode(strftime("%c", gmtime(value / 1000000000))) + elif typecode == 9: return unicode(value) + elif typecode == 10: return unicode(value.__repr__()) elif typecode == 11: - if value: return 'T' - else: return 'F' - elif typecode == 12: return str(value) - elif typecode == 13: return str(value) - elif typecode == 14: return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack("!LHHHHL", value) - elif typecode == 15: return value.__repr__() - elif typecode == 16: return str(value) - elif typecode == 17: return str(value) - elif typecode == 18: return str(value) - elif typecode == 19: return str(value) + if value: return u"T" + else: return u"F" + elif typecode == 12: return unicode(value) + elif typecode == 13: return unicode(value) + elif typecode == 14: return unicode(value.__repr__()) + elif typecode == 15: return unicode(value.__repr__()) + elif typecode == 16: return unicode(value) + elif typecode == 17: return unicode(value) + elif typecode == 18: return unicode(value) + elif typecode == 19: return unicode(value) else: raise ValueError ("Invalid type code: %d" % typecode) @@ -764,7 +764,7 @@ class SchemaProperty: """ """ def __init__(self, codec): map = codec.read_map() - self.name = str(map["name"]) + self.name = map["name"] self.type = map["type"] self.access = map["access"] self.index = map["index"] != 0 @@ -776,11 +776,11 @@ class SchemaProperty: self.desc = None for key, value in map.items(): - if key == "unit" : self.unit = str(value) + if key == "unit" : self.unit = value elif key == "min" : self.min = value elif key == "max" : self.max = value elif key == "maxlen" : self.maxlen = value - elif key == "desc" : self.desc = str(value) + elif key == "desc" : self.desc = value def __repr__(self): return self.name @@ -789,14 +789,14 @@ class SchemaStatistic: """ """ def __init__(self, codec): map = codec.read_map() - self.name = str(map["name"]) + self.name = map["name"] self.type = map["type"] self.unit = None self.desc = None for key, value in map.items(): - if key == "unit" : self.unit = str(value) - elif key == "desc" : self.desc = str(value) + if key == "unit" : self.unit = value + elif key == "desc" : self.desc = value def __repr__(self): return self.name @@ -805,10 +805,10 @@ class SchemaMethod: """ """ def __init__(self, codec): map = codec.read_map() - self.name = str(map["name"]) + self.name = map["name"] argCount = map["argCount"] if "desc" in map: - self.desc = str(map["desc"]) + self.desc = map["desc"] else: self.desc = None self.arguments = [] @@ -833,10 +833,10 @@ class SchemaArgument: """ """ def __init__(self, codec, methodArg): map = codec.read_map() - self.name = str(map["name"]) + self.name = map["name"] self.type = map["type"] if methodArg: - self.dir = str(map["dir"].upper()) + self.dir = map["dir"].upper() self.unit = None self.min = None self.max = None @@ -845,12 +845,12 @@ class SchemaArgument: self.default = None for key, value in map.items(): - if key == "unit" : self.unit = str(value) + if key == "unit" : self.unit = value elif key == "min" : self.min = value elif key == "max" : self.max = value elif key == "maxlen" : self.maxlen = value - elif key == "desc" : self.desc = str(value) - elif key == "default" : self.default = str(value) + elif key == "desc" : self.desc = value + elif key == "default" : self.default = value class ObjectId: """ Object that represents QMF object identifiers """ @@ -904,7 +904,6 @@ class ObjectId: codec.write_uint64(self.first) codec.write_uint64(self.second) - class Object(object): """ """ def __init__(self, session, broker, schema, codec, prop, stat): @@ -955,12 +954,16 @@ class Object(object): def getIndex(self): """ Return a string describing this object's primary key. """ - result = "" + result = u"" for property, value in self._properties: if property.index: - if result != "": - result += ":" - result += str(value) + if result != u"": + result += u":" + try: + valstr = unicode(value) + except: + valstr = u"" + result += valstr return result def getProperties(self): @@ -979,7 +982,7 @@ class Object(object): self.statistics = newer.getStatistics() def __repr__(self): - return self.getIndex() + return self.getIndex().encode("utf8") def __getattr__(self, name): for method in self._schema.getMethods(): @@ -1365,7 +1368,8 @@ class Event: out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] out += " broker=" + self.broker.getUrl() for arg in self.schema.arguments: - out += " " + arg.name + "=" + self.session._displayValue(self.arguments[arg.name], arg.type) + out += " " + arg.name + "=" + \ + self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8") return out def _sevName(self): -- cgit v1.2.1 From c8e02b87d416f44d2cfc6acc707ab2ef8d7ad813 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 11 Nov 2008 19:15:32 +0000 Subject: added codec tests for int64 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@713117 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests/codec010.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index 5f9f853744..1912eac591 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -47,6 +47,8 @@ class CodecTest(TestCase): def testMapLong(self): self.check("map", {"long": 2**32}) + self.check("map", {"long": 1 << 34}) + self.check("map", {"long": -(1 << 34)}) def testMapTimestamp(self): decoded = self.check("map", {"timestamp": timestamp(0)}) @@ -107,6 +109,12 @@ class CodecTest(TestCase): self.check("int16", 3) self.check("int16", -3) + def testInt64(self): + self.check("int64", 3) + self.check("int64", -3) + self.check("int64", 1<<34) + self.check("int64", -(1<<34)) + def testDatetime(self): self.check("datetime", timestamp(0)) self.check("datetime", timestamp(long(time.time()))) -- cgit v1.2.1 From 1bf6e3cb632d3ba0e2ea90a4124ff7e532f4aef8 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 12 Nov 2008 22:52:51 +0000 Subject: Removed optimistic-consume option for queues git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@713540 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index e2d3108a33..2a6f8fcf8b 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -35,7 +35,6 @@ _maxQueueSize = None _maxQueueCount = None _policyType = None _lvq = False -_optimisticConsume = False _msgSequence = False _ive = False @@ -46,7 +45,6 @@ MAX_QUEUE_COUNT = "qpid.max_count" POLICY_TYPE = "qpid.policy_type" CLUSTER_DURABLE = "qpid.persist_last_node" LVQ = "qpid.last_value_queue" -OPTIMISTIC_CONSUME = "qpid.optimistic_consume" MSG_SEQUENCE = "qpid.msg_sequence" IVE = "qpid.ive" @@ -76,7 +74,6 @@ def Usage (): print " --max-queue-count N Maximum in-memory queue size as a number of messages" print " --policy-type TYPE Action taken when queue limit is reached (reject, flow_to_disk, ring, ring_strict)" print " --last-value-queue Enable LVQ behavior on the queue" - print " --optimistic-consume Enable optimistic consume on the queue" print print "Add Exchange Options:" print " --durable Exchange is durable" @@ -191,7 +188,6 @@ class BrokerManager: if MAX_QUEUE_COUNT in args: print "--max-queue-count=%d" % args[MAX_QUEUE_COUNT], if POLICY_TYPE in args: print "--policy-type=%s" % args[POLICY_TYPE], if LVQ in args and args[LVQ] == 1: print "--last-value-queue", - if OPTIMISTIC_CONSUME in args and args[OPTIMISTIC_CONSUME] == 1: print "--optimistic-consume", print def QueueListRecurse (self, filter): @@ -248,8 +244,6 @@ class BrokerManager: declArgs[CLUSTER_DURABLE] = 1 if _lvq: declArgs[LVQ] = 1 - if _optimisticConsume: - declArgs[OPTIMISTIC_CONSUME] = 1 self.broker.getAmqpSession().queue_declare (queue=qname, durable=_durable, arguments=declArgs) @@ -305,7 +299,7 @@ def YN (bool): try: longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=", - "last-value-queue", "optimistic-consume", "sequence", "ive") + "last-value-queue", "sequence", "ive") (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -337,8 +331,6 @@ for opt in optlist: _policyType = opt[1] if opt[0] == "--last-value-queue": _lvq = True - if opt[0] == "--optimistic-consume": - _optimisticConsume = True if opt[0] == "--sequence": _msgSequence = True if opt[0] == "--ive": -- cgit v1.2.1 From b15caaa061091efcecfd1e8704e6f2c6bde4e03e Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 13 Nov 2008 04:15:15 +0000 Subject: Updated qmf-agent API to allow user to specify uid, password, mechanism, and protocol. Fixed qmf-console bug related to routing keys of object messages. Pass the binding key into the management agent to allow for selective broadcast of object data. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@713631 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 7d7aeee44f..06517d0aaf 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -224,7 +224,7 @@ class Session: raise Exception("userBindings option not set for Session") for broker in self.brokers: broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.%s" % packageName) + binding_key="console.obj.%s.#" % packageName) def bindClass(self, classKey): """ """ @@ -233,7 +233,7 @@ class Session: pname, cname, hash = classKey for broker in self.brokers: broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.%s.%s" % (pname, cname)) + binding_key="console.obj.%s.%s.#" % (pname, cname)) def getAgents(self, broker=None): """ Get a list of currently known agents """ @@ -374,7 +374,7 @@ class Session: if self.rcvEvents: keyList.append("console.event.#") if self.rcvHeartbeats: - keyList.append("console.heartbeat") + keyList.append("console.heartbeat.#") return keyList def _handleBrokerConnect(self, broker): @@ -692,13 +692,6 @@ class Package: def __init__(self, name): self.name = name -class ClassKey: - """ """ - def __init__(self, package, className, hash): - self.package = package - self.className = className - self.hash = hash - class SchemaClass: """ """ CLASS_KIND_TABLE = 1 -- cgit v1.2.1 From 72294c5cc09948632d5952fa16bd4eff0292d995 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 13 Nov 2008 21:54:22 +0000 Subject: Removed spurious exception re-raise git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@713836 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 1 - 1 file changed, 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 2a6f8fcf8b..4b4bdd00b8 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -380,7 +380,6 @@ try: Usage () except Exception,e: print "Failed:", e.message - raise sys.exit(1) bm.Disconnect() -- cgit v1.2.1 From ba538574e5a877503b51211ca389df6a45b95769 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 13 Nov 2008 22:07:30 +0000 Subject: Avoid unicode in schema labels, which causes problems when they're used as map keys. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@713839 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 47 +++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index 06517d0aaf..aa4b149601 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -397,7 +397,7 @@ class Session: broker._send(smsg) def _handlePackageInd(self, broker, codec, seq): - pname = codec.read_str8() + pname = str(codec.read_str8()) notify = False try: self.cv.acquire() @@ -442,8 +442,8 @@ class Session: def _handleClassInd(self, broker, codec, seq): kind = codec.read_uint8() - pname = codec.read_str8() - cname = codec.read_str8() + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) hash = codec.read_bin128() unknown = False @@ -512,8 +512,8 @@ class Session: def _handleSchemaResp(self, broker, codec, seq): kind = codec.read_uint8() - pname = codec.read_str8() - cname = codec.read_str8() + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) hash = codec.read_bin128() classKey = (pname, cname, hash) _class = SchemaClass(kind, classKey, codec) @@ -529,8 +529,8 @@ class Session: self.console.newClass(kind, classKey) def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): - pname = codec.read_str8() - cname = codec.read_str8() + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) hash = codec.read_bin128() classKey = (pname, cname, hash) try: @@ -757,9 +757,9 @@ class SchemaProperty: """ """ def __init__(self, codec): map = codec.read_map() - self.name = map["name"] + self.name = str(map["name"]) self.type = map["type"] - self.access = map["access"] + self.access = str(map["access"]) self.index = map["index"] != 0 self.optional = map["optional"] != 0 self.unit = None @@ -782,7 +782,7 @@ class SchemaStatistic: """ """ def __init__(self, codec): map = codec.read_map() - self.name = map["name"] + self.name = str(map["name"]) self.type = map["type"] self.unit = None self.desc = None @@ -798,7 +798,7 @@ class SchemaMethod: """ """ def __init__(self, codec): map = codec.read_map() - self.name = map["name"] + self.name = str(map["name"]) argCount = map["argCount"] if "desc" in map: self.desc = map["desc"] @@ -826,10 +826,10 @@ class SchemaArgument: """ """ def __init__(self, codec, methodArg): map = codec.read_map() - self.name = map["name"] + self.name = str(map["name"]) self.type = map["type"] if methodArg: - self.dir = map["dir"].upper() + self.dir = str(map["dir"]).upper() self.unit = None self.min = None self.max = None @@ -1141,6 +1141,17 @@ class Broker: """ """ return "%s:%d" % (self.host, self.port) + def getFullUrl(self, noAuthIfGuestDefault=True): + """ """ + ssl = "" + if self.ssl: + ssl = "s" + auth = "%s/%s@" % (self.authUser, self.authPass) + if self.authUser == "" or \ + (noAuthIfGuestDefault and self.authUser == "guest" and self.authPass == "guest"): + auth = "" + return "amqp%s://%s%s:%d" % (ssl, auth, self.host, self.port or 5672) + def __repr__(self): if self.isConnected: return "Broker connected at: %s" % self.getUrl() @@ -1340,8 +1351,8 @@ class Event: def __init__(self, session, broker, codec): self.session = session self.broker = broker - pname = codec.read_str8() - cname = codec.read_str8() + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) hash = codec.read_bin128() self.classKey = (pname, cname, hash) self.timestamp = codec.read_int64() @@ -1361,8 +1372,10 @@ class Event: out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] out += " broker=" + self.broker.getUrl() for arg in self.schema.arguments: - out += " " + arg.name + "=" + \ - self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8") + disp = self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8") + if " " in disp: + disp = "\"" + disp + "\"" + out += " " + arg.name + "=" + disp return out def _sevName(self): -- cgit v1.2.1 From e37656ebe6afe9611cf8ff0ec2e458117d4a129f Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Sat, 15 Nov 2008 14:10:10 +0000 Subject: updated version numbers and release notes for M4 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@714267 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/RELEASE_NOTES | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/RELEASE_NOTES b/qpid/python/RELEASE_NOTES index 0e75988ae9..e92e8c20b9 100644 --- a/qpid/python/RELEASE_NOTES +++ b/qpid/python/RELEASE_NOTES @@ -1,7 +1,7 @@ -Apache Incubator Qpid Python M3 Release Notes +Apache Incubator Qpid Python M4 Release Notes ------------------------------------------- -The Qpid M3 release of the python client contains support the for both +The Qpid M4 release of the python client contains support the for both 0-8 and 0-10 of the AMQP specification as well as support for the non-WIP portion of the 0-9 specification. You can access these specficiations from: -- cgit v1.2.1 From c2354b9106ca5738c3f31e60ded1025473aaa605 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 17 Nov 2008 12:35:05 +0000 Subject: Fix to error handling, without this I was seeing "AttributeError: Exception instance has no attribute 'message'" when e.g. the address specified did not match an available broker. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@718233 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 4b4bdd00b8..bd4aa613bc 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -379,7 +379,7 @@ try: else: Usage () except Exception,e: - print "Failed:", e.message + print "Failed:", e.args[0] sys.exit(1) bm.Disconnect() -- cgit v1.2.1 From 7ad3988e599da508eb782ca84d97033b33692ac4 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 20 Nov 2008 14:51:45 +0000 Subject: QPID-1476 - routing keys used for updates can't be used to discriminate by agent - Fixed routing keys in agents and binding keys in consoles - Added some additional debug output for ManagementAgentImpl - Minor cleanup in the connection close path for ManagementAgentImpl git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@719245 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/qmfconsole.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py index aa4b149601..bdd93e6f94 100644 --- a/qpid/python/qpid/qmfconsole.py +++ b/qpid/python/qpid/qmfconsole.py @@ -224,7 +224,7 @@ class Session: raise Exception("userBindings option not set for Session") for broker in self.brokers: broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.%s.#" % packageName) + binding_key="console.obj.*.*.%s.#" % packageName) def bindClass(self, classKey): """ """ @@ -233,7 +233,7 @@ class Session: pname, cname, hash = classKey for broker in self.brokers: broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.%s.%s.#" % (pname, cname)) + binding_key="console.obj.*.*.%s.%s.#" % (pname, cname)) def getAgents(self, broker=None): """ Get a list of currently known agents """ @@ -370,7 +370,7 @@ class Session: if self.rcvObjects and not self.userBindings: keyList.append("console.obj.#") else: - keyList.append("console.obj.org.apache.qpid.broker.agent") + keyList.append("console.obj.*.*.org.apache.qpid.broker.agent") if self.rcvEvents: keyList.append("console.event.#") if self.rcvHeartbeats: -- cgit v1.2.1 From 395b33c630a57baa4262cdac05828a33df268592 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 20 Nov 2008 20:28:17 +0000 Subject: QPID-1464 - Moved qmf console API out of python/qpid and into python/qmf git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@719359 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 4 +- qpid/python/commands/qpid-printevents | 2 +- qpid/python/commands/qpid-queue-stats | 2 +- qpid/python/commands/qpid-route | 16 +- qpid/python/qmf/__init__.py | 18 + qpid/python/qmf/console.py | 1472 +++++++++++++++++++++++++++++++++ qpid/python/qpid/management.py | 2 +- qpid/python/qpid/managementdata.py | 2 +- qpid/python/qpid/qmfconsole.py | 1472 --------------------------------- qpid/python/qpid/testlib.py | 4 +- 10 files changed, 1506 insertions(+), 1488 deletions(-) create mode 100644 qpid/python/qmf/__init__.py create mode 100644 qpid/python/qmf/console.py delete mode 100644 qpid/python/qpid/qmfconsole.py (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index bd4aa613bc..6bfa6939f1 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -23,7 +23,7 @@ import os import getopt import sys import locale -from qpid import qmfconsole +from qmf.console import Session _recursive = False _host = "localhost" @@ -93,7 +93,7 @@ class BrokerManager: def SetBroker (self, brokerUrl): self.url = brokerUrl - self.qmf = qmfconsole.Session() + self.qmf = Session() self.broker = self.qmf.addBroker(brokerUrl) def Disconnect(self): diff --git a/qpid/python/commands/qpid-printevents b/qpid/python/commands/qpid-printevents index 6efd472221..f27eec04f1 100755 --- a/qpid/python/commands/qpid-printevents +++ b/qpid/python/commands/qpid-printevents @@ -24,7 +24,7 @@ import optparse import sys import socket import time -from qpid.qmfconsole import Console, Session +from qmf.console import Console, Session class EventConsole(Console): def event(self, broker, event): diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index c29cab3568..5aa32f9b38 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -26,7 +26,7 @@ import re import socket import qpid from threading import Condition -from qpid.qmfconsole import Session, Console +from qmf.console import Session, Console from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed from qpid.util import connect diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 6fbd11b684..5733c62a27 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -24,7 +24,7 @@ import sys import socket import os import locale -from qpid import qmfconsole +from qmf.console import Session, BrokerURL def Usage(): print "Usage: qpid-route [OPTIONS] dynamic add [tag] [exclude-list]" @@ -65,9 +65,9 @@ _transport = "tcp" class RouteManager: def __init__(self, localBroker): - self.local = qmfconsole.BrokerURL(localBroker) + self.local = BrokerURL(localBroker) self.remote = None - self.qmf = qmfconsole.Session() + self.qmf = Session() self.broker = self.qmf.addBroker(localBroker) def disconnect(self): @@ -81,7 +81,7 @@ class RouteManager: return None def addLink(self, remoteBroker): - self.remote = qmfconsole.BrokerURL(remoteBroker) + self.remote = BrokerURL(remoteBroker) if self.local.match(self.remote.host, self.remote.port): raise Exception("Linking broker to itself is not permitted") @@ -100,7 +100,7 @@ class RouteManager: print "Connect method returned:", res.status, res.text def delLink(self, remoteBroker): - self.remote = qmfconsole.BrokerURL(remoteBroker) + self.remote = BrokerURL(remoteBroker) brokers = self.qmf.getObjects(_class="broker") broker = brokers[0] link = self.getLink() @@ -137,7 +137,7 @@ class RouteManager: added = False links = qmf.getObjects(_class="link") for link in links: - url = qmfconsole.BrokerURL("%s:%d" % (link.host, link.port)) + url = BrokerURL("%s:%d" % (link.host, link.port)) if url.name() not in brokerList: print " %s..." % url.name(), try: @@ -263,7 +263,7 @@ class RouteManager: print "Bridge method returned:", res.status, res.text def delQueueRoute(self, remoteBroker, exchange, queue): - self.remote = qmfconsole.BrokerURL(remoteBroker) + self.remote = BrokerURL(remoteBroker) link = self.getLink() if link == None: if not _quiet: @@ -293,7 +293,7 @@ class RouteManager: raise Exception("Route not found") def delRoute(self, remoteBroker, exchange, routingKey, dynamic=False): - self.remote = qmfconsole.BrokerURL(remoteBroker) + self.remote = BrokerURL(remoteBroker) link = self.getLink() if link == None: if not _quiet: diff --git a/qpid/python/qmf/__init__.py b/qpid/python/qmf/__init__.py new file mode 100644 index 0000000000..31d5a2ef58 --- /dev/null +++ b/qpid/python/qmf/__init__.py @@ -0,0 +1,18 @@ +# +# 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. +# diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py new file mode 100644 index 0000000000..bdd93e6f94 --- /dev/null +++ b/qpid/python/qmf/console.py @@ -0,0 +1,1472 @@ +# +# 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. +# + +""" Console API for Qpid Management Framework """ + +import os +import qpid +import struct +import socket +import re +from qpid.peer import Closed +from qpid.connection import Connection, ConnectionFailed +from qpid.datatypes import UUID, uuid4, Message, RangedSet +from qpid.util import connect, ssl, URL +from qpid.codec010 import StringCodec as Codec +from threading import Lock, Condition +from time import time, strftime, gmtime +from cStringIO import StringIO + +class Console: + """ To access the asynchronous operations, a class must be derived from + Console with overrides of any combination of the available methods. """ + + def brokerConnected(self, broker): + """ Invoked when a connection is established to a broker """ + pass + + def brokerDisconnected(self, broker): + """ Invoked when the connection to a broker is lost """ + pass + + def newPackage(self, name): + """ Invoked when a QMF package is discovered. """ + pass + + def newClass(self, kind, classKey): + """ Invoked when a new class is discovered. Session.getSchema can be + used to obtain details about the class.""" + pass + + def newAgent(self, agent): + """ Invoked when a QMF agent is discovered. """ + pass + + def delAgent(self, agent): + """ Invoked when a QMF agent disconects. """ + pass + + def objectProps(self, broker, record): + """ Invoked when an object is updated. """ + pass + + def objectStats(self, broker, record): + """ Invoked when an object is updated. """ + pass + + def event(self, broker, event): + """ Invoked when an event is raised. """ + pass + + def heartbeat(self, agent, timestamp): + """ """ + pass + + def brokerInfo(self, broker): + """ """ + pass + + def methodResponse(self, broker, seq, response): + """ """ + pass + +class BrokerURL(URL): + def __init__(self, text): + URL.__init__(self, text) + socket.gethostbyname(self.host) + if self.port is None: + if self.scheme == URL.AMQPS: + self.port = 5671 + else: + self.port = 5672 + self.authName = self.user or "guest" + self.authPass = self.password or "guest" + self.authMech = "PLAIN" + + def name(self): + return self.host + ":" + str(self.port) + + def match(self, host, port): + return socket.gethostbyname(self.host) == socket.gethostbyname(host) and self.port == port + +class Session: + """ + An instance of the Session class represents a console session running + against one or more QMF brokers. A single instance of Session is needed + to interact with the management framework as a console. + """ + _CONTEXT_SYNC = 1 + _CONTEXT_STARTUP = 2 + _CONTEXT_MULTIGET = 3 + + GET_WAIT_TIME = 60 + + def __init__(self, console=None, rcvObjects=True, rcvEvents=True, rcvHeartbeats=True, + manageConnections=False, userBindings=False): + """ + Initialize a session. If the console argument is provided, the + more advanced asynchronous features are available. If console is + defaulted, the session will operate in a simpler, synchronous manner. + + The rcvObjects, rcvEvents, and rcvHeartbeats arguments are meaningful only if 'console' + is provided. They control whether object updates, events, and agent-heartbeats are + subscribed to. If the console is not interested in receiving one or more of the above, + setting the argument to False will reduce tha bandwidth used by the API. + + If manageConnections is set to True, the Session object will manage connections to + the brokers. This means that if a broker is unreachable, it will retry until a connection + can be established. If a connection is lost, the Session will attempt to reconnect. + + If manageConnections is set to False, the user is responsible for handing failures. In + this case, an unreachable broker will cause addBroker to raise an exception. + + If userBindings is set to False (the default) and rcvObjects is True, the console will + receive data for all object classes. If userBindings is set to True, the user must select + which classes the console shall receive by invoking the bindPackage or bindClass methods. + This allows the console to be configured to receive only information that is relavant to + a particular application. If rcvObjects id False, userBindings has no meaning. + """ + self.console = console + self.brokers = [] + self.packages = {} + self.seqMgr = SequenceManager() + self.cv = Condition() + self.syncSequenceList = [] + self.getResult = [] + self.getSelect = [] + self.error = None + self.rcvObjects = rcvObjects + self.rcvEvents = rcvEvents + self.rcvHeartbeats = rcvHeartbeats + self.userBindings = userBindings + if self.console == None: + self.rcvObjects = False + self.rcvEvents = False + self.rcvHeartbeats = False + self.bindingKeyList = self._bindingKeys() + self.manageConnections = manageConnections + + if self.userBindings and not self.rcvObjects: + raise Exception("userBindings can't be set unless rcvObjects is set and a console is provided") + + if manageConnections: + raise Exception("manageConnections - not yet implemented") + + def __repr__(self): + return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) + + def addBroker(self, target="localhost"): + """ Connect to a Qpid broker. Returns an object of type Broker. """ + url = BrokerURL(target) + broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass, + ssl = url.scheme == URL.AMQPS) + if not broker.isConnected and not self.manageConnections: + raise Exception(broker.error) + + self.brokers.append(broker) + self.getObjects(broker=broker, _class="agent") + return broker + + def delBroker(self, broker): + """ Disconnect from a broker. The 'broker' argument is the object + returned from the addBroker call """ + broker._shutdown() + self.brokers.remove(broker) + del broker + + def getPackages(self): + """ Get the list of known QMF packages """ + for broker in self.brokers: + broker._waitForStable() + list = [] + for package in self.packages: + list.append(package) + return list + + def getClasses(self, packageName): + """ Get the list of known classes within a QMF package """ + for broker in self.brokers: + broker._waitForStable() + list = [] + if packageName in self.packages: + for cname, hash in self.packages[packageName]: + list.append((packageName, cname, hash)) + return list + + def getSchema(self, classKey): + """ Get the schema for a QMF class """ + for broker in self.brokers: + broker._waitForStable() + pname, cname, hash = classKey + if pname in self.packages: + if (cname, hash) in self.packages[pname]: + return self.packages[pname][(cname, hash)] + + def bindPackage(self, packageName): + """ """ + if not self.userBindings or not self.rcvObjects: + raise Exception("userBindings option not set for Session") + for broker in self.brokers: + broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, + binding_key="console.obj.*.*.%s.#" % packageName) + + def bindClass(self, classKey): + """ """ + if not self.userBindings or not self.rcvObjects: + raise Exception("userBindings option not set for Session") + pname, cname, hash = classKey + for broker in self.brokers: + broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, + binding_key="console.obj.*.*.%s.%s.#" % (pname, cname)) + + def getAgents(self, broker=None): + """ Get a list of currently known agents """ + brokerList = [] + if broker == None: + for b in self.brokers: + brokerList.append(b) + else: + brokerList.append(broker) + + for b in brokerList: + b._waitForStable() + agentList = [] + for b in brokerList: + for a in b.getAgents(): + agentList.append(a) + return agentList + + def getObjects(self, **kwargs): + """ Get a list of objects from QMF agents. + All arguments are passed by name(keyword). + + The class for queried objects may be specified in one of the following ways: + + _schema = - supply a schema object returned from getSchema. + _key = - supply a classKey from the list returned by getClasses. + _class = - supply a class name as a string. If the class name exists + in multiple packages, a _package argument may also be supplied. + _objectId = - get the object referenced by the object-id + + If objects should be obtained from only one agent, use the following argument. + Otherwise, the query will go to all agents. + + _agent = - supply an agent from the list returned by getAgents. + + If the get query is to be restricted to one broker (as opposed to all connected brokers), + add the following argument: + + _broker = - supply a broker as returned by addBroker. + + If additional arguments are supplied, they are used as property selectors. For example, + if the argument name="test" is supplied, only objects whose "name" property is "test" + will be returned in the result. + """ + if "_broker" in kwargs: + brokerList = [] + brokerList.append(kwargs["_broker"]) + else: + brokerList = self.brokers + for broker in brokerList: + broker._waitForStable() + + agentList = [] + if "_agent" in kwargs: + agent = kwargs["_agent"] + if agent.broker not in brokerList: + raise Exception("Supplied agent is not accessible through the supplied broker") + agentList.append(agent) + else: + for broker in brokerList: + for agent in broker.getAgents(): + agentList.append(agent) + + cname = None + if "_schema" in kwargs: pname, cname, hash = kwargs["_schema"].getKey() + elif "_key" in kwargs: pname, cname, hash = kwargs["_key"] + elif "_class" in kwargs: + pname, cname, hash = None, kwargs["_class"], None + if "_package" in kwargs: + pname = kwargs["_package"] + if cname == None and "_objectId" not in kwargs: + raise Exception("No class supplied, use '_schema', '_key', '_class', or '_objectId' argument") + + map = {} + self.getSelect = [] + if "_objectId" in kwargs: + map["_objectid"] = kwargs["_objectId"].__repr__() + else: + map["_class"] = cname + if pname != None: map["_package"] = pname + if hash != None: map["_hash"] = hash + for item in kwargs: + if item[0] != '_': + self.getSelect.append((item, kwargs[item])) + + self.getResult = [] + for agent in agentList: + broker = agent.broker + sendCodec = Codec(broker.conn.spec) + try: + self.cv.acquire() + seq = self.seqMgr._reserve(self._CONTEXT_MULTIGET) + self.syncSequenceList.append(seq) + finally: + self.cv.release() + broker._setHeader(sendCodec, 'G', seq) + sendCodec.write_map(map) + smsg = broker._message(sendCodec.encoded, "agent.%s" % agent.bank) + broker._send(smsg) + + starttime = time() + timeout = False + try: + self.cv.acquire() + while len(self.syncSequenceList) > 0 and self.error == None: + self.cv.wait(self.GET_WAIT_TIME) + if time() - starttime > self.GET_WAIT_TIME: + for pendingSeq in self.syncSequenceList: + self.seqMgr._release(pendingSeq) + self.syncSequenceList = [] + timeout = True + finally: + self.cv.release() + + if self.error: + errorText = self.error + self.error = None + raise Exception(errorText) + + if len(self.getResult) == 0 and timeout: + raise RuntimeError("No agent responded within timeout period") + return self.getResult + + def setEventFilter(self, **kwargs): + """ """ + pass + + def _bindingKeys(self): + keyList = [] + keyList.append("schema.#") + if self.rcvObjects and self.rcvEvents and self.rcvHeartbeats and not self.userBindings: + keyList.append("console.#") + else: + if self.rcvObjects and not self.userBindings: + keyList.append("console.obj.#") + else: + keyList.append("console.obj.*.*.org.apache.qpid.broker.agent") + if self.rcvEvents: + keyList.append("console.event.#") + if self.rcvHeartbeats: + keyList.append("console.heartbeat.#") + return keyList + + def _handleBrokerConnect(self, broker): + pass + + def _handleBrokerDisconnect(self, broker): + pass + + def _handleBrokerResp(self, broker, codec, seq): + broker.brokerId = UUID(codec.read_uuid()) + if self.console != None: + self.console.brokerInfo(broker) + + # Send a package request + # (effectively inc and dec outstanding by not doing anything) + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) + broker._setHeader(sendCodec, 'P', seq) + smsg = broker._message(sendCodec.encoded) + broker._send(smsg) + + def _handlePackageInd(self, broker, codec, seq): + pname = str(codec.read_str8()) + notify = False + try: + self.cv.acquire() + if pname not in self.packages: + self.packages[pname] = {} + notify = True + finally: + self.cv.release() + if notify and self.console != None: + self.console.newPackage(pname) + + # Send a class request + broker._incOutstanding() + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) + broker._setHeader(sendCodec, 'Q', seq) + sendCodec.write_str8(pname) + smsg = broker._message(sendCodec.encoded) + broker._send(smsg) + + def _handleCommandComplete(self, broker, codec, seq): + code = codec.read_uint32() + text = codec.read_str8() + context = self.seqMgr._release(seq) + if context == self._CONTEXT_STARTUP: + broker._decOutstanding() + elif context == self._CONTEXT_SYNC and seq == broker.syncSequence: + try: + broker.cv.acquire() + broker.syncInFlight = False + broker.cv.notify() + finally: + broker.cv.release() + elif context == self._CONTEXT_MULTIGET and seq in self.syncSequenceList: + try: + self.cv.acquire() + self.syncSequenceList.remove(seq) + if len(self.syncSequenceList) == 0: + self.cv.notify() + finally: + self.cv.release() + + def _handleClassInd(self, broker, codec, seq): + kind = codec.read_uint8() + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + unknown = False + + try: + self.cv.acquire() + if pname in self.packages: + if (cname, hash) not in self.packages[pname]: + unknown = True + finally: + self.cv.release() + + if unknown: + # Send a schema request for the unknown class + broker._incOutstanding() + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) + broker._setHeader(sendCodec, 'S', seq) + sendCodec.write_str8(pname) + sendCodec.write_str8(cname) + sendCodec.write_bin128(hash) + smsg = broker._message(sendCodec.encoded) + broker._send(smsg) + + def _handleMethodResp(self, broker, codec, seq): + code = codec.read_uint32() + text = codec.read_str16() + outArgs = {} + method, synchronous = self.seqMgr._release(seq) + if code == 0: + for arg in method.arguments: + if arg.dir.find("O") != -1: + outArgs[arg.name] = self._decodeValue(codec, arg.type) + result = MethodResult(code, text, outArgs) + if synchronous: + try: + broker.cv.acquire() + broker.syncResult = result + broker.syncInFlight = False + broker.cv.notify() + finally: + broker.cv.release() + else: + if self.console: + self.console.methodResponse(broker, seq, result) + + def _handleHeartbeatInd(self, broker, codec, seq, msg): + brokerBank = 1 + agentBank = 0 + dp = msg.get("delivery_properties") + if dp: + key = dp["routing_key"] + keyElements = key.split(".") + if len(keyElements) == 4: + brokerBank = int(keyElements[2]) + agentBank = int(keyElements[3]) + + agent = broker.getAgent(brokerBank, agentBank) + timestamp = codec.read_uint64() + if self.console != None: + self.console.heartbeat(agent, timestamp) + + def _handleEventInd(self, broker, codec, seq): + if self.console != None: + event = Event(self, broker, codec) + self.console.event(broker, event) + + def _handleSchemaResp(self, broker, codec, seq): + kind = codec.read_uint8() + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + classKey = (pname, cname, hash) + _class = SchemaClass(kind, classKey, codec) + try: + self.cv.acquire() + self.packages[pname][(cname, hash)] = _class + finally: + self.cv.release() + + self.seqMgr._release(seq) + broker._decOutstanding() + if self.console != None: + self.console.newClass(kind, classKey) + + def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + classKey = (pname, cname, hash) + try: + self.cv.acquire() + if pname not in self.packages: + return + if (cname, hash) not in self.packages[pname]: + return + schema = self.packages[pname][(cname, hash)] + finally: + self.cv.release() + + object = Object(self, broker, schema, codec, prop, stat) + if pname == "org.apache.qpid.broker" and cname == "agent": + broker._updateAgent(object) + + try: + self.cv.acquire() + if seq in self.syncSequenceList: + if object.getTimestamps()[2] == 0 and self._selectMatch(object): + self.getResult.append(object) + return + finally: + self.cv.release() + + if self.console != None: + if prop: + self.console.objectProps(broker, object) + if stat: + self.console.objectStats(broker, object) + + def _handleError(self, error): + self.error = error + try: + self.cv.acquire() + self.syncSequenceList = [] + self.cv.notify() + finally: + self.cv.release() + + def _selectMatch(self, object): + """ Check the object against self.getSelect to check for a match """ + for key, value in self.getSelect: + for prop, propval in object.getProperties(): + if key == prop.name and value != propval: + return False + return True + + def _decodeValue(self, codec, typecode): + """ Decode, from the codec, a value based on its typecode. """ + if typecode == 1: data = codec.read_uint8() # U8 + elif typecode == 2: data = codec.read_uint16() # U16 + elif typecode == 3: data = codec.read_uint32() # U32 + elif typecode == 4: data = codec.read_uint64() # U64 + elif typecode == 6: data = codec.read_str8() # SSTR + elif typecode == 7: data = codec.read_str16() # LSTR + elif typecode == 8: data = codec.read_int64() # ABSTIME + elif typecode == 9: data = codec.read_uint64() # DELTATIME + elif typecode == 10: data = ObjectId(codec) # REF + elif typecode == 11: data = codec.read_uint8() != 0 # BOOL + elif typecode == 12: data = codec.read_float() # FLOAT + elif typecode == 13: data = codec.read_double() # DOUBLE + elif typecode == 14: data = UUID(codec.read_uuid()) # UUID + elif typecode == 15: data = codec.read_map() # FTABLE + elif typecode == 16: data = codec.read_int8() # S8 + elif typecode == 17: data = codec.read_int16() # S16 + elif typecode == 18: data = codec.read_int32() # S32 + elif typecode == 19: data = codec.read_int64() # S63 + else: + raise ValueError("Invalid type code: %d" % typecode) + return data + + def _encodeValue(self, codec, value, typecode): + """ Encode, into the codec, a value based on its typecode. """ + if typecode == 1: codec.write_uint8 (int(value)) # U8 + elif typecode == 2: codec.write_uint16 (int(value)) # U16 + elif typecode == 3: codec.write_uint32 (long(value)) # U32 + elif typecode == 4: codec.write_uint64 (long(value)) # U64 + elif typecode == 6: codec.write_str8 (value) # SSTR + elif typecode == 7: codec.write_str16 (value) # LSTR + elif typecode == 8: codec.write_int64 (long(value)) # ABSTIME + elif typecode == 9: codec.write_uint64 (long(value)) # DELTATIME + elif typecode == 10: value.encode (codec) # REF + elif typecode == 11: codec.write_uint8 (int(value)) # BOOL + elif typecode == 12: codec.write_float (float(value)) # FLOAT + elif typecode == 13: codec.write_double (double(value)) # DOUBLE + elif typecode == 14: codec.write_uuid (value.bytes) # UUID + elif typecode == 15: codec.write_map (value) # FTABLE + elif typecode == 16: codec.write_int8 (int(value)) # S8 + elif typecode == 17: codec.write_int16 (int(value)) # S16 + elif typecode == 18: codec.write_int32 (int(value)) # S32 + elif typecode == 19: codec.write_int64 (int(value)) # S64 + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def _displayValue(self, value, typecode): + """ """ + if typecode == 1: return unicode(value) + elif typecode == 2: return unicode(value) + elif typecode == 3: return unicode(value) + elif typecode == 4: return unicode(value) + elif typecode == 6: return value + elif typecode == 7: return value + elif typecode == 8: return unicode(strftime("%c", gmtime(value / 1000000000))) + elif typecode == 9: return unicode(value) + elif typecode == 10: return unicode(value.__repr__()) + elif typecode == 11: + if value: return u"T" + else: return u"F" + elif typecode == 12: return unicode(value) + elif typecode == 13: return unicode(value) + elif typecode == 14: return unicode(value.__repr__()) + elif typecode == 15: return unicode(value.__repr__()) + elif typecode == 16: return unicode(value) + elif typecode == 17: return unicode(value) + elif typecode == 18: return unicode(value) + elif typecode == 19: return unicode(value) + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def _sendMethodRequest(self, broker, schemaKey, objectId, name, argList): + """ This function can be used to send a method request to an object given only the + broker, schemaKey, and objectId. This is an uncommon usage pattern as methods are + normally invoked on the object itself. + """ + schema = self.getSchema(schemaKey) + for method in schema.getMethods(): + if name == method.name: + aIdx = 0 + sendCodec = Codec(broker.conn.spec) + seq = self.seqMgr._reserve((method, False)) + broker._setHeader(sendCodec, 'M', seq) + objectId.encode(sendCodec) + pname, cname, hash = schemaKey + sendCodec.write_str8(pname) + sendCodec.write_str8(cname) + sendCodec.write_bin128(hash) + sendCodec.write_str8(name) + + count = 0 + for arg in method.arguments: + if arg.dir.find("I") != -1: + count += 1 + if count != len(argList): + raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(argList))) + + for arg in method.arguments: + if arg.dir.find("I") != -1: + self._encodeValue(sendCodec, argList[aIdx], arg.type) + aIdx += 1 + smsg = broker._message(sendCodec.encoded, "agent.%d.%d" % + (objectId.getBrokerBank(), objectId.getAgentBank())) + broker._send(smsg) + return seq + return None + +class Package: + """ """ + def __init__(self, name): + self.name = name + +class SchemaClass: + """ """ + CLASS_KIND_TABLE = 1 + CLASS_KIND_EVENT = 2 + + def __init__(self, kind, key, codec): + self.kind = kind + self.classKey = key + self.properties = [] + self.statistics = [] + self.methods = [] + self.arguments = [] + + if self.kind == self.CLASS_KIND_TABLE: + propCount = codec.read_uint16() + statCount = codec.read_uint16() + methodCount = codec.read_uint16() + for idx in range(propCount): + self.properties.append(SchemaProperty(codec)) + for idx in range(statCount): + self.statistics.append(SchemaStatistic(codec)) + for idx in range(methodCount): + self.methods.append(SchemaMethod(codec)) + + elif self.kind == self.CLASS_KIND_EVENT: + argCount = codec.read_uint16() + for idx in range(argCount): + self.arguments.append(SchemaArgument(codec, methodArg=False)) + + def __repr__(self): + pname, cname, hash = self.classKey + if self.kind == self.CLASS_KIND_TABLE: + kindStr = "Table" + elif self.kind == self.CLASS_KIND_EVENT: + kindStr = "Event" + else: + kindStr = "Unsupported" + result = "%s Class: %s:%s " % (kindStr, pname, cname) + result += "(%08x-%04x-%04x-%04x-%04x%08x)" % struct.unpack ("!LHHHHL", hash) + return result + + def getKey(self): + """ Return the class-key for this class. """ + return self.classKey + + def getProperties(self): + """ Return the list of properties for the class. """ + return self.properties + + def getStatistics(self): + """ Return the list of statistics for the class. """ + return self.statistics + + def getMethods(self): + """ Return the list of methods for the class. """ + return self.methods + + def getArguments(self): + """ Return the list of events for the class. """ + return self.arguments + +class SchemaProperty: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + self.type = map["type"] + self.access = str(map["access"]) + self.index = map["index"] != 0 + self.optional = map["optional"] != 0 + self.unit = None + self.min = None + self.max = None + self.maxlen = None + self.desc = None + + for key, value in map.items(): + if key == "unit" : self.unit = value + elif key == "min" : self.min = value + elif key == "max" : self.max = value + elif key == "maxlen" : self.maxlen = value + elif key == "desc" : self.desc = value + + def __repr__(self): + return self.name + +class SchemaStatistic: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + self.type = map["type"] + self.unit = None + self.desc = None + + for key, value in map.items(): + if key == "unit" : self.unit = value + elif key == "desc" : self.desc = value + + def __repr__(self): + return self.name + +class SchemaMethod: + """ """ + def __init__(self, codec): + map = codec.read_map() + self.name = str(map["name"]) + argCount = map["argCount"] + if "desc" in map: + self.desc = map["desc"] + else: + self.desc = None + self.arguments = [] + + for idx in range(argCount): + self.arguments.append(SchemaArgument(codec, methodArg=True)) + + def __repr__(self): + result = self.name + "(" + first = True + for arg in self.arguments: + if arg.dir.find("I") != -1: + if first: + first = False + else: + result += ", " + result += arg.name + result += ")" + return result + +class SchemaArgument: + """ """ + def __init__(self, codec, methodArg): + map = codec.read_map() + self.name = str(map["name"]) + self.type = map["type"] + if methodArg: + self.dir = str(map["dir"]).upper() + self.unit = None + self.min = None + self.max = None + self.maxlen = None + self.desc = None + self.default = None + + for key, value in map.items(): + if key == "unit" : self.unit = value + elif key == "min" : self.min = value + elif key == "max" : self.max = value + elif key == "maxlen" : self.maxlen = value + elif key == "desc" : self.desc = value + elif key == "default" : self.default = value + +class ObjectId: + """ Object that represents QMF object identifiers """ + def __init__(self, codec, first=0, second=0): + if codec: + self.first = codec.read_uint64() + self.second = codec.read_uint64() + else: + self.first = first + self.second = second + + def __cmp__(self, other): + if other == None or not isinstance(other, ObjectId) : + return 1 + if self.first < other.first: + return -1 + if self.first > other.first: + return 1 + if self.second < other.second: + return -1 + if self.second > other.second: + return 1 + return 0 + + def __repr__(self): + return "%d-%d-%d-%d-%d" % (self.getFlags(), self.getSequence(), + self.getBrokerBank(), self.getAgentBank(), self.getObject()) + + def index(self): + return (self.first, self.second) + + def getFlags(self): + return (self.first & 0xF000000000000000) >> 60 + + def getSequence(self): + return (self.first & 0x0FFF000000000000) >> 48 + + def getBrokerBank(self): + return (self.first & 0x0000FFFFF0000000) >> 28 + + def getAgentBank(self): + return self.first & 0x000000000FFFFFFF + + def getObject(self): + return self.second + + def isDurable(self): + return self.getSequence() == 0 + + def encode(self, codec): + codec.write_uint64(self.first) + codec.write_uint64(self.second) + +class Object(object): + """ """ + def __init__(self, session, broker, schema, codec, prop, stat): + """ """ + self._session = session + self._broker = broker + self._schema = schema + self._currentTime = codec.read_uint64() + self._createTime = codec.read_uint64() + self._deleteTime = codec.read_uint64() + self._objectId = ObjectId(codec) + self._properties = [] + self._statistics = [] + if prop: + notPresent = self._parsePresenceMasks(codec, schema) + for property in schema.getProperties(): + if property.name in notPresent: + self._properties.append((property, None)) + else: + self._properties.append((property, self._session._decodeValue(codec, property.type))) + if stat: + for statistic in schema.getStatistics(): + self._statistics.append((statistic, self._session._decodeValue(codec, statistic.type))) + + def getBroker(self): + """ Return the broker from which this object was sent """ + return self._broker + + def getObjectId(self): + """ Return the object identifier for this object """ + return self._objectId + + def getClassKey(self): + """ Return the class-key that references the schema describing this object. """ + return self._schema.getKey() + + def getSchema(self): + """ Return the schema that describes this object. """ + return self._schema + + def getMethods(self): + """ Return a list of methods available for this object. """ + return self._schema.getMethods() + + def getTimestamps(self): + """ Return the current, creation, and deletion times for this object. """ + return self._currentTime, self._createTime, self._deleteTime + + def getIndex(self): + """ Return a string describing this object's primary key. """ + result = u"" + for property, value in self._properties: + if property.index: + if result != u"": + result += u":" + try: + valstr = unicode(value) + except: + valstr = u"" + result += valstr + return result + + def getProperties(self): + return self._properties + + def getStatistics(self): + return self._statistics + + def mergeUpdate(self, newer): + """ Replace properties and/or statistics with a newly received update """ + if self._objectId != newer._objectId: + raise Exception("Objects with different object-ids") + if len(newer.getProperties()) > 0: + self.properties = newer.getProperties() + if len(newer.getStatistics()) > 0: + self.statistics = newer.getStatistics() + + def __repr__(self): + return self.getIndex().encode("utf8") + + def __getattr__(self, name): + for method in self._schema.getMethods(): + if name == method.name: + return lambda *args, **kwargs : self._invoke(name, args, kwargs) + for property, value in self._properties: + if name == property.name: + return value + if name == "_" + property.name + "_" and property.type == 10: # Dereference references + deref = self._session.getObjects(_objectId=value, _broker=self._broker) + if len(deref) != 1: + return None + else: + return deref[0] + for statistic, value in self._statistics: + if name == statistic.name: + return value + raise Exception("Type Object has no attribute '%s'" % name) + + def _sendMethodRequest(self, name, args, kwargs, synchronous=False): + for method in self._schema.getMethods(): + if name == method.name: + aIdx = 0 + sendCodec = Codec(self._broker.conn.spec) + seq = self._session.seqMgr._reserve((method, synchronous)) + self._broker._setHeader(sendCodec, 'M', seq) + self._objectId.encode(sendCodec) + pname, cname, hash = self._schema.getKey() + sendCodec.write_str8(pname) + sendCodec.write_str8(cname) + sendCodec.write_bin128(hash) + sendCodec.write_str8(name) + + count = 0 + for arg in method.arguments: + if arg.dir.find("I") != -1: + count += 1 + if count != len(args): + raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(args))) + + for arg in method.arguments: + if arg.dir.find("I") != -1: + self._session._encodeValue(sendCodec, args[aIdx], arg.type) + aIdx += 1 + smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % + (self._objectId.getBrokerBank(), self._objectId.getAgentBank())) + if synchronous: + try: + self._broker.cv.acquire() + self._broker.syncInFlight = True + finally: + self._broker.cv.release() + self._broker._send(smsg) + return seq + return None + + def _invoke(self, name, args, kwargs): + if self._sendMethodRequest(name, args, kwargs, True): + try: + self._broker.cv.acquire() + starttime = time() + while self._broker.syncInFlight and self._broker.error == None: + self._broker.cv.wait(self._broker.SYNC_TIME) + if time() - starttime > self._broker.SYNC_TIME: + self._session.seqMgr._release(seq) + raise RuntimeError("Timed out waiting for method to respond") + finally: + self._broker.cv.release() + if self._broker.error != None: + errorText = self._broker.error + self._broker.error = None + raise Exception(errorText) + return self._broker.syncResult + raise Exception("Invalid Method (software defect) [%s]" % name) + + def _parsePresenceMasks(self, codec, schema): + excludeList = [] + bit = 0 + for property in schema.getProperties(): + if property.optional: + if bit == 0: + mask = codec.read_uint8() + bit = 1 + if (mask & bit) == 0: + excludeList.append(property.name) + bit *= 2 + if bit == 256: + bit = 0 + return excludeList + +class MethodResult(object): + """ """ + def __init__(self, status, text, outArgs): + """ """ + self.status = status + self.text = text + self.outArgs = outArgs + + def __getattr__(self, name): + if name in self.outArgs: + return self.outArgs[name] + + def __repr__(self): + return "%s (%d) - %s" % (self.text, self.status, self.outArgs) + +class Broker: + """ """ + SYNC_TIME = 60 + + def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): + self.session = session + self.host = host + self.port = port + self.ssl = ssl + self.authUser = authUser + self.authPass = authPass + self.agents = {} + self.agents["1.0"] = Agent(self, "1.0", "BrokerAgent") + self.topicBound = False + self.cv = Condition() + self.syncInFlight = False + self.syncRequest = 0 + self.syncResult = None + self.reqsOutstanding = 1 + self.error = None + self.brokerId = None + self.isConnected = False + self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) + self._tryToConnect() + + def isConnected(self): + return self.isConnected + + def getError(self): + return self.error + + def getBrokerId(self): + """ Get broker's unique identifier (UUID) """ + return self.brokerId + + def getBrokerBank(self): + return 1 + + def getAgent(self, brokerBank, agentBank): + bankKey = "%d.%d" % (brokerBank, agentBank) + if bankKey in self.agents: + return self.agents[bankKey] + return None + + def getSessionId(self): + """ Get the identifier of the AMQP session to the broker """ + return self.amqpSessionId + + def getAgents(self): + """ Get the list of agents reachable via this broker """ + return self.agents.values() + + def getAmqpSession(self): + """ Get the AMQP session object for this connected broker. """ + return self.amqpSession + + def getUrl(self): + """ """ + return "%s:%d" % (self.host, self.port) + + def getFullUrl(self, noAuthIfGuestDefault=True): + """ """ + ssl = "" + if self.ssl: + ssl = "s" + auth = "%s/%s@" % (self.authUser, self.authPass) + if self.authUser == "" or \ + (noAuthIfGuestDefault and self.authUser == "guest" and self.authPass == "guest"): + auth = "" + return "amqp%s://%s%s:%d" % (ssl, auth, self.host, self.port or 5672) + + def __repr__(self): + if self.isConnected: + return "Broker connected at: %s" % self.getUrl() + else: + return "Disconnected Broker" + + def _tryToConnect(self): + try: + sock = connect(self.host, self.port) + if self.ssl: + sock = ssl(sock) + self.conn = Connection(sock, username=self.authUser, password=self.authPass) + self.conn.start() + self.replyName = "reply-%s" % self.amqpSessionId + self.amqpSession = self.conn.session(self.amqpSessionId) + self.amqpSession.auto_sync = True + self.amqpSession.queue_declare(queue=self.replyName, exclusive=True, auto_delete=True) + self.amqpSession.exchange_bind(exchange="amq.direct", + queue=self.replyName, binding_key=self.replyName) + self.amqpSession.message_subscribe(queue=self.replyName, destination="rdest", + accept_mode=self.amqpSession.accept_mode.none, + acquire_mode=self.amqpSession.acquire_mode.pre_acquired) + self.amqpSession.incoming("rdest").listen(self._replyCb, self._exceptionCb) + self.amqpSession.message_set_flow_mode(destination="rdest", flow_mode=1) + self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="rdest", unit=1, value=0xFFFFFFFF) + + self.topicName = "topic-%s" % self.amqpSessionId + self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) + self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest", + accept_mode=self.amqpSession.accept_mode.none, + acquire_mode=self.amqpSession.acquire_mode.pre_acquired) + self.amqpSession.incoming("tdest").listen(self._replyCb) + self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) + self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) + + self.isConnected = True + self.session._handleBrokerConnect(self) + + codec = Codec(self.conn.spec) + self._setHeader(codec, 'B') + msg = self._message(codec.encoded) + self._send(msg) + + except socket.error, e: + self.error = "Socket Error %s - %s" % (e[0], e[1]) + except Closed, e: + self.error = "Connect Failed %d - %s" % (e[0], e[1]) + except ConnectionFailed, e: + self.error = "Connect Failed %d - %s" % (e[0], e[1]) + + def _updateAgent(self, obj): + bankKey = "%d.%d" % (obj.brokerBank, obj.agentBank) + if obj._deleteTime == 0: + if bankKey not in self.agents: + agent = Agent(self, bankKey, obj.label) + self.agents[bankKey] = agent + if self.session.console != None: + self.session.console.newAgent(agent) + else: + agent = self.agents.pop(bankKey, None) + if agent != None and self.session.console != None: + self.session.console.delAgent(agent) + + def _setHeader(self, codec, opcode, seq=0): + """ Compose the header of a management message. """ + codec.write_uint8(ord('A')) + codec.write_uint8(ord('M')) + codec.write_uint8(ord('2')) + codec.write_uint8(ord(opcode)) + codec.write_uint32(seq) + + def _checkHeader(self, codec): + """ Check the header of a management message and extract the opcode and class. """ + try: + octet = chr(codec.read_uint8()) + if octet != 'A': + return None, None + octet = chr(codec.read_uint8()) + if octet != 'M': + return None, None + octet = chr(codec.read_uint8()) + if octet != '2': + return None, None + opcode = chr(codec.read_uint8()) + seq = codec.read_uint32() + return opcode, seq + except: + return None, None + + def _message (self, body, routing_key="broker"): + dp = self.amqpSession.delivery_properties() + dp.routing_key = routing_key + mp = self.amqpSession.message_properties() + mp.content_type = "x-application/qmf" + mp.reply_to = self.amqpSession.reply_to("amq.direct", self.replyName) + return Message(dp, mp, body) + + def _send(self, msg, dest="qpid.management"): + self.amqpSession.message_transfer(destination=dest, message=msg) + + def _shutdown(self): + if self.isConnected: + self.amqpSession.incoming("rdest").stop() + if self.session.console != None: + self.amqpSession.incoming("tdest").stop() + self.amqpSession.close() + self.conn.close() + self.isConnected = False + else: + raise Exception("Broker already disconnected") + + def _waitForStable(self): + try: + self.cv.acquire() + if self.reqsOutstanding == 0: + return + self.syncInFlight = True + starttime = time() + while self.reqsOutstanding != 0: + self.cv.wait(self.SYNC_TIME) + if time() - starttime > self.SYNC_TIME: + raise RuntimeError("Timed out waiting for broker to synchronize") + finally: + self.cv.release() + + def _incOutstanding(self): + try: + self.cv.acquire() + self.reqsOutstanding += 1 + finally: + self.cv.release() + + def _decOutstanding(self): + try: + self.cv.acquire() + self.reqsOutstanding -= 1 + if self.reqsOutstanding == 0 and not self.topicBound: + self.topicBound = True + for key in self.session.bindingKeyList: + self.amqpSession.exchange_bind(exchange="qpid.management", + queue=self.topicName, binding_key=key) + if self.reqsOutstanding == 0 and self.syncInFlight: + self.syncInFlight = False + self.cv.notify() + finally: + self.cv.release() + + def _replyCb(self, msg): + codec = Codec(self.conn.spec, msg.body) + while True: + opcode, seq = self._checkHeader(codec) + if opcode == None: return + if opcode == 'b': self.session._handleBrokerResp (self, codec, seq) + elif opcode == 'p': self.session._handlePackageInd (self, codec, seq) + elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) + elif opcode == 'q': self.session._handleClassInd (self, codec, seq) + elif opcode == 'm': self.session._handleMethodResp (self, codec, seq) + elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq, msg) + elif opcode == 'e': self.session._handleEventInd (self, codec, seq) + elif opcode == 's': self.session._handleSchemaResp (self, codec, seq) + elif opcode == 'c': self.session._handleContentInd (self, codec, seq, prop=True) + elif opcode == 'i': self.session._handleContentInd (self, codec, seq, stat=True) + elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) + + def _exceptionCb(self, data): + self.isConnected = False + self.error = data + try: + self.cv.acquire() + if self.syncInFlight: + self.cv.notify() + finally: + self.cv.release() + self.session._handleError(self.error) + self.session._handleBrokerDisconnect(self) + +class Agent: + """ """ + def __init__(self, broker, bank, label): + self.broker = broker + self.bank = bank + self.label = label + + def __repr__(self): + return "Agent at bank %s (%s)" % (self.bank, self.label) + + def getBroker(self): + return self.broker + + def getAgentBank(self): + return self.bank + +class Event: + """ """ + def __init__(self, session, broker, codec): + self.session = session + self.broker = broker + pname = str(codec.read_str8()) + cname = str(codec.read_str8()) + hash = codec.read_bin128() + self.classKey = (pname, cname, hash) + self.timestamp = codec.read_int64() + self.severity = codec.read_uint8() + self.schema = None + if pname in session.packages: + if (cname, hash) in session.packages[pname]: + self.schema = session.packages[pname][(cname, hash)] + self.arguments = {} + for arg in self.schema.arguments: + self.arguments[arg.name] = session._decodeValue(codec, arg.type) + + def __repr__(self): + if self.schema == None: + return "" + out = strftime("%c", gmtime(self.timestamp / 1000000000)) + out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] + out += " broker=" + self.broker.getUrl() + for arg in self.schema.arguments: + disp = self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8") + if " " in disp: + disp = "\"" + disp + "\"" + out += " " + arg.name + "=" + disp + return out + + def _sevName(self): + if self.severity == 0 : return "EMER " + if self.severity == 1 : return "ALERT" + if self.severity == 2 : return "CRIT " + if self.severity == 3 : return "ERROR" + if self.severity == 4 : return "WARN " + if self.severity == 5 : return "NOTIC" + if self.severity == 6 : return "INFO " + if self.severity == 7 : return "DEBUG" + return "INV-%d" % self.severity + + def getClassKey(self): + return self.classKey + + def getArguments(self): + return self.arguments + + def getTimestamp(self): + return self.timestamp + + def getName(self): + return self.name + + def getSchema(self): + return self.schema + +class SequenceManager: + """ Manage sequence numbers for asynchronous method calls """ + def __init__(self): + self.lock = Lock() + self.sequence = 0 + self.pending = {} + + def _reserve(self, data): + """ Reserve a unique sequence number """ + try: + self.lock.acquire() + result = self.sequence + self.sequence = self.sequence + 1 + self.pending[result] = data + finally: + self.lock.release() + return result + + def _release(self, seq): + """ Release a reserved sequence number """ + data = None + try: + self.lock.acquire() + if seq in self.pending: + data = self.pending[seq] + del self.pending[seq] + finally: + self.lock.release() + return data + + +class DebugConsole(Console): + """ """ + def brokerConnected(self, broker): + print "brokerConnected:", broker + + def brokerDisconnected(self, broker): + print "brokerDisconnected:", broker + + def newPackage(self, name): + print "newPackage:", name + + def newClass(self, kind, classKey): + print "newClass:", kind, classKey + + def newAgent(self, agent): + print "newAgent:", agent + + def delAgent(self, agent): + print "delAgent:", agent + + def objectProps(self, broker, record): + print "objectProps:", record.getClassKey() + + def objectStats(self, broker, record): + print "objectStats:", record.getClassKey() + + def event(self, broker, event): + print "event:", event + + def heartbeat(self, agent, timestamp): + print "heartbeat:", agent + + def brokerInfo(self, broker): + print "brokerInfo:", broker + diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index e91eb48517..4a87d2d533 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -18,7 +18,7 @@ # ############################################################################### -## This file is being obsoleted by qpid/qmfconsole.py +## This file is being obsoleted by qmf/console.py ############################################################################### """ diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index c3e66c29a1..46c746c0f9 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -21,7 +21,7 @@ ############################################################################### -## This file is being obsoleted by qpid/qmfconsole.py +## This file is being obsoleted by qmf/console.py ############################################################################### import qpid diff --git a/qpid/python/qpid/qmfconsole.py b/qpid/python/qpid/qmfconsole.py deleted file mode 100644 index bdd93e6f94..0000000000 --- a/qpid/python/qpid/qmfconsole.py +++ /dev/null @@ -1,1472 +0,0 @@ -# -# 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. -# - -""" Console API for Qpid Management Framework """ - -import os -import qpid -import struct -import socket -import re -from qpid.peer import Closed -from qpid.connection import Connection, ConnectionFailed -from qpid.datatypes import UUID, uuid4, Message, RangedSet -from qpid.util import connect, ssl, URL -from qpid.codec010 import StringCodec as Codec -from threading import Lock, Condition -from time import time, strftime, gmtime -from cStringIO import StringIO - -class Console: - """ To access the asynchronous operations, a class must be derived from - Console with overrides of any combination of the available methods. """ - - def brokerConnected(self, broker): - """ Invoked when a connection is established to a broker """ - pass - - def brokerDisconnected(self, broker): - """ Invoked when the connection to a broker is lost """ - pass - - def newPackage(self, name): - """ Invoked when a QMF package is discovered. """ - pass - - def newClass(self, kind, classKey): - """ Invoked when a new class is discovered. Session.getSchema can be - used to obtain details about the class.""" - pass - - def newAgent(self, agent): - """ Invoked when a QMF agent is discovered. """ - pass - - def delAgent(self, agent): - """ Invoked when a QMF agent disconects. """ - pass - - def objectProps(self, broker, record): - """ Invoked when an object is updated. """ - pass - - def objectStats(self, broker, record): - """ Invoked when an object is updated. """ - pass - - def event(self, broker, event): - """ Invoked when an event is raised. """ - pass - - def heartbeat(self, agent, timestamp): - """ """ - pass - - def brokerInfo(self, broker): - """ """ - pass - - def methodResponse(self, broker, seq, response): - """ """ - pass - -class BrokerURL(URL): - def __init__(self, text): - URL.__init__(self, text) - socket.gethostbyname(self.host) - if self.port is None: - if self.scheme == URL.AMQPS: - self.port = 5671 - else: - self.port = 5672 - self.authName = self.user or "guest" - self.authPass = self.password or "guest" - self.authMech = "PLAIN" - - def name(self): - return self.host + ":" + str(self.port) - - def match(self, host, port): - return socket.gethostbyname(self.host) == socket.gethostbyname(host) and self.port == port - -class Session: - """ - An instance of the Session class represents a console session running - against one or more QMF brokers. A single instance of Session is needed - to interact with the management framework as a console. - """ - _CONTEXT_SYNC = 1 - _CONTEXT_STARTUP = 2 - _CONTEXT_MULTIGET = 3 - - GET_WAIT_TIME = 60 - - def __init__(self, console=None, rcvObjects=True, rcvEvents=True, rcvHeartbeats=True, - manageConnections=False, userBindings=False): - """ - Initialize a session. If the console argument is provided, the - more advanced asynchronous features are available. If console is - defaulted, the session will operate in a simpler, synchronous manner. - - The rcvObjects, rcvEvents, and rcvHeartbeats arguments are meaningful only if 'console' - is provided. They control whether object updates, events, and agent-heartbeats are - subscribed to. If the console is not interested in receiving one or more of the above, - setting the argument to False will reduce tha bandwidth used by the API. - - If manageConnections is set to True, the Session object will manage connections to - the brokers. This means that if a broker is unreachable, it will retry until a connection - can be established. If a connection is lost, the Session will attempt to reconnect. - - If manageConnections is set to False, the user is responsible for handing failures. In - this case, an unreachable broker will cause addBroker to raise an exception. - - If userBindings is set to False (the default) and rcvObjects is True, the console will - receive data for all object classes. If userBindings is set to True, the user must select - which classes the console shall receive by invoking the bindPackage or bindClass methods. - This allows the console to be configured to receive only information that is relavant to - a particular application. If rcvObjects id False, userBindings has no meaning. - """ - self.console = console - self.brokers = [] - self.packages = {} - self.seqMgr = SequenceManager() - self.cv = Condition() - self.syncSequenceList = [] - self.getResult = [] - self.getSelect = [] - self.error = None - self.rcvObjects = rcvObjects - self.rcvEvents = rcvEvents - self.rcvHeartbeats = rcvHeartbeats - self.userBindings = userBindings - if self.console == None: - self.rcvObjects = False - self.rcvEvents = False - self.rcvHeartbeats = False - self.bindingKeyList = self._bindingKeys() - self.manageConnections = manageConnections - - if self.userBindings and not self.rcvObjects: - raise Exception("userBindings can't be set unless rcvObjects is set and a console is provided") - - if manageConnections: - raise Exception("manageConnections - not yet implemented") - - def __repr__(self): - return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) - - def addBroker(self, target="localhost"): - """ Connect to a Qpid broker. Returns an object of type Broker. """ - url = BrokerURL(target) - broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass, - ssl = url.scheme == URL.AMQPS) - if not broker.isConnected and not self.manageConnections: - raise Exception(broker.error) - - self.brokers.append(broker) - self.getObjects(broker=broker, _class="agent") - return broker - - def delBroker(self, broker): - """ Disconnect from a broker. The 'broker' argument is the object - returned from the addBroker call """ - broker._shutdown() - self.brokers.remove(broker) - del broker - - def getPackages(self): - """ Get the list of known QMF packages """ - for broker in self.brokers: - broker._waitForStable() - list = [] - for package in self.packages: - list.append(package) - return list - - def getClasses(self, packageName): - """ Get the list of known classes within a QMF package """ - for broker in self.brokers: - broker._waitForStable() - list = [] - if packageName in self.packages: - for cname, hash in self.packages[packageName]: - list.append((packageName, cname, hash)) - return list - - def getSchema(self, classKey): - """ Get the schema for a QMF class """ - for broker in self.brokers: - broker._waitForStable() - pname, cname, hash = classKey - if pname in self.packages: - if (cname, hash) in self.packages[pname]: - return self.packages[pname][(cname, hash)] - - def bindPackage(self, packageName): - """ """ - if not self.userBindings or not self.rcvObjects: - raise Exception("userBindings option not set for Session") - for broker in self.brokers: - broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.*.*.%s.#" % packageName) - - def bindClass(self, classKey): - """ """ - if not self.userBindings or not self.rcvObjects: - raise Exception("userBindings option not set for Session") - pname, cname, hash = classKey - for broker in self.brokers: - broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.*.*.%s.%s.#" % (pname, cname)) - - def getAgents(self, broker=None): - """ Get a list of currently known agents """ - brokerList = [] - if broker == None: - for b in self.brokers: - brokerList.append(b) - else: - brokerList.append(broker) - - for b in brokerList: - b._waitForStable() - agentList = [] - for b in brokerList: - for a in b.getAgents(): - agentList.append(a) - return agentList - - def getObjects(self, **kwargs): - """ Get a list of objects from QMF agents. - All arguments are passed by name(keyword). - - The class for queried objects may be specified in one of the following ways: - - _schema = - supply a schema object returned from getSchema. - _key = - supply a classKey from the list returned by getClasses. - _class = - supply a class name as a string. If the class name exists - in multiple packages, a _package argument may also be supplied. - _objectId = - get the object referenced by the object-id - - If objects should be obtained from only one agent, use the following argument. - Otherwise, the query will go to all agents. - - _agent = - supply an agent from the list returned by getAgents. - - If the get query is to be restricted to one broker (as opposed to all connected brokers), - add the following argument: - - _broker = - supply a broker as returned by addBroker. - - If additional arguments are supplied, they are used as property selectors. For example, - if the argument name="test" is supplied, only objects whose "name" property is "test" - will be returned in the result. - """ - if "_broker" in kwargs: - brokerList = [] - brokerList.append(kwargs["_broker"]) - else: - brokerList = self.brokers - for broker in brokerList: - broker._waitForStable() - - agentList = [] - if "_agent" in kwargs: - agent = kwargs["_agent"] - if agent.broker not in brokerList: - raise Exception("Supplied agent is not accessible through the supplied broker") - agentList.append(agent) - else: - for broker in brokerList: - for agent in broker.getAgents(): - agentList.append(agent) - - cname = None - if "_schema" in kwargs: pname, cname, hash = kwargs["_schema"].getKey() - elif "_key" in kwargs: pname, cname, hash = kwargs["_key"] - elif "_class" in kwargs: - pname, cname, hash = None, kwargs["_class"], None - if "_package" in kwargs: - pname = kwargs["_package"] - if cname == None and "_objectId" not in kwargs: - raise Exception("No class supplied, use '_schema', '_key', '_class', or '_objectId' argument") - - map = {} - self.getSelect = [] - if "_objectId" in kwargs: - map["_objectid"] = kwargs["_objectId"].__repr__() - else: - map["_class"] = cname - if pname != None: map["_package"] = pname - if hash != None: map["_hash"] = hash - for item in kwargs: - if item[0] != '_': - self.getSelect.append((item, kwargs[item])) - - self.getResult = [] - for agent in agentList: - broker = agent.broker - sendCodec = Codec(broker.conn.spec) - try: - self.cv.acquire() - seq = self.seqMgr._reserve(self._CONTEXT_MULTIGET) - self.syncSequenceList.append(seq) - finally: - self.cv.release() - broker._setHeader(sendCodec, 'G', seq) - sendCodec.write_map(map) - smsg = broker._message(sendCodec.encoded, "agent.%s" % agent.bank) - broker._send(smsg) - - starttime = time() - timeout = False - try: - self.cv.acquire() - while len(self.syncSequenceList) > 0 and self.error == None: - self.cv.wait(self.GET_WAIT_TIME) - if time() - starttime > self.GET_WAIT_TIME: - for pendingSeq in self.syncSequenceList: - self.seqMgr._release(pendingSeq) - self.syncSequenceList = [] - timeout = True - finally: - self.cv.release() - - if self.error: - errorText = self.error - self.error = None - raise Exception(errorText) - - if len(self.getResult) == 0 and timeout: - raise RuntimeError("No agent responded within timeout period") - return self.getResult - - def setEventFilter(self, **kwargs): - """ """ - pass - - def _bindingKeys(self): - keyList = [] - keyList.append("schema.#") - if self.rcvObjects and self.rcvEvents and self.rcvHeartbeats and not self.userBindings: - keyList.append("console.#") - else: - if self.rcvObjects and not self.userBindings: - keyList.append("console.obj.#") - else: - keyList.append("console.obj.*.*.org.apache.qpid.broker.agent") - if self.rcvEvents: - keyList.append("console.event.#") - if self.rcvHeartbeats: - keyList.append("console.heartbeat.#") - return keyList - - def _handleBrokerConnect(self, broker): - pass - - def _handleBrokerDisconnect(self, broker): - pass - - def _handleBrokerResp(self, broker, codec, seq): - broker.brokerId = UUID(codec.read_uuid()) - if self.console != None: - self.console.brokerInfo(broker) - - # Send a package request - # (effectively inc and dec outstanding by not doing anything) - sendCodec = Codec(broker.conn.spec) - seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) - broker._setHeader(sendCodec, 'P', seq) - smsg = broker._message(sendCodec.encoded) - broker._send(smsg) - - def _handlePackageInd(self, broker, codec, seq): - pname = str(codec.read_str8()) - notify = False - try: - self.cv.acquire() - if pname not in self.packages: - self.packages[pname] = {} - notify = True - finally: - self.cv.release() - if notify and self.console != None: - self.console.newPackage(pname) - - # Send a class request - broker._incOutstanding() - sendCodec = Codec(broker.conn.spec) - seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) - broker._setHeader(sendCodec, 'Q', seq) - sendCodec.write_str8(pname) - smsg = broker._message(sendCodec.encoded) - broker._send(smsg) - - def _handleCommandComplete(self, broker, codec, seq): - code = codec.read_uint32() - text = codec.read_str8() - context = self.seqMgr._release(seq) - if context == self._CONTEXT_STARTUP: - broker._decOutstanding() - elif context == self._CONTEXT_SYNC and seq == broker.syncSequence: - try: - broker.cv.acquire() - broker.syncInFlight = False - broker.cv.notify() - finally: - broker.cv.release() - elif context == self._CONTEXT_MULTIGET and seq in self.syncSequenceList: - try: - self.cv.acquire() - self.syncSequenceList.remove(seq) - if len(self.syncSequenceList) == 0: - self.cv.notify() - finally: - self.cv.release() - - def _handleClassInd(self, broker, codec, seq): - kind = codec.read_uint8() - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - unknown = False - - try: - self.cv.acquire() - if pname in self.packages: - if (cname, hash) not in self.packages[pname]: - unknown = True - finally: - self.cv.release() - - if unknown: - # Send a schema request for the unknown class - broker._incOutstanding() - sendCodec = Codec(broker.conn.spec) - seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) - broker._setHeader(sendCodec, 'S', seq) - sendCodec.write_str8(pname) - sendCodec.write_str8(cname) - sendCodec.write_bin128(hash) - smsg = broker._message(sendCodec.encoded) - broker._send(smsg) - - def _handleMethodResp(self, broker, codec, seq): - code = codec.read_uint32() - text = codec.read_str16() - outArgs = {} - method, synchronous = self.seqMgr._release(seq) - if code == 0: - for arg in method.arguments: - if arg.dir.find("O") != -1: - outArgs[arg.name] = self._decodeValue(codec, arg.type) - result = MethodResult(code, text, outArgs) - if synchronous: - try: - broker.cv.acquire() - broker.syncResult = result - broker.syncInFlight = False - broker.cv.notify() - finally: - broker.cv.release() - else: - if self.console: - self.console.methodResponse(broker, seq, result) - - def _handleHeartbeatInd(self, broker, codec, seq, msg): - brokerBank = 1 - agentBank = 0 - dp = msg.get("delivery_properties") - if dp: - key = dp["routing_key"] - keyElements = key.split(".") - if len(keyElements) == 4: - brokerBank = int(keyElements[2]) - agentBank = int(keyElements[3]) - - agent = broker.getAgent(brokerBank, agentBank) - timestamp = codec.read_uint64() - if self.console != None: - self.console.heartbeat(agent, timestamp) - - def _handleEventInd(self, broker, codec, seq): - if self.console != None: - event = Event(self, broker, codec) - self.console.event(broker, event) - - def _handleSchemaResp(self, broker, codec, seq): - kind = codec.read_uint8() - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - classKey = (pname, cname, hash) - _class = SchemaClass(kind, classKey, codec) - try: - self.cv.acquire() - self.packages[pname][(cname, hash)] = _class - finally: - self.cv.release() - - self.seqMgr._release(seq) - broker._decOutstanding() - if self.console != None: - self.console.newClass(kind, classKey) - - def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - classKey = (pname, cname, hash) - try: - self.cv.acquire() - if pname not in self.packages: - return - if (cname, hash) not in self.packages[pname]: - return - schema = self.packages[pname][(cname, hash)] - finally: - self.cv.release() - - object = Object(self, broker, schema, codec, prop, stat) - if pname == "org.apache.qpid.broker" and cname == "agent": - broker._updateAgent(object) - - try: - self.cv.acquire() - if seq in self.syncSequenceList: - if object.getTimestamps()[2] == 0 and self._selectMatch(object): - self.getResult.append(object) - return - finally: - self.cv.release() - - if self.console != None: - if prop: - self.console.objectProps(broker, object) - if stat: - self.console.objectStats(broker, object) - - def _handleError(self, error): - self.error = error - try: - self.cv.acquire() - self.syncSequenceList = [] - self.cv.notify() - finally: - self.cv.release() - - def _selectMatch(self, object): - """ Check the object against self.getSelect to check for a match """ - for key, value in self.getSelect: - for prop, propval in object.getProperties(): - if key == prop.name and value != propval: - return False - return True - - def _decodeValue(self, codec, typecode): - """ Decode, from the codec, a value based on its typecode. """ - if typecode == 1: data = codec.read_uint8() # U8 - elif typecode == 2: data = codec.read_uint16() # U16 - elif typecode == 3: data = codec.read_uint32() # U32 - elif typecode == 4: data = codec.read_uint64() # U64 - elif typecode == 6: data = codec.read_str8() # SSTR - elif typecode == 7: data = codec.read_str16() # LSTR - elif typecode == 8: data = codec.read_int64() # ABSTIME - elif typecode == 9: data = codec.read_uint64() # DELTATIME - elif typecode == 10: data = ObjectId(codec) # REF - elif typecode == 11: data = codec.read_uint8() != 0 # BOOL - elif typecode == 12: data = codec.read_float() # FLOAT - elif typecode == 13: data = codec.read_double() # DOUBLE - elif typecode == 14: data = UUID(codec.read_uuid()) # UUID - elif typecode == 15: data = codec.read_map() # FTABLE - elif typecode == 16: data = codec.read_int8() # S8 - elif typecode == 17: data = codec.read_int16() # S16 - elif typecode == 18: data = codec.read_int32() # S32 - elif typecode == 19: data = codec.read_int64() # S63 - else: - raise ValueError("Invalid type code: %d" % typecode) - return data - - def _encodeValue(self, codec, value, typecode): - """ Encode, into the codec, a value based on its typecode. """ - if typecode == 1: codec.write_uint8 (int(value)) # U8 - elif typecode == 2: codec.write_uint16 (int(value)) # U16 - elif typecode == 3: codec.write_uint32 (long(value)) # U32 - elif typecode == 4: codec.write_uint64 (long(value)) # U64 - elif typecode == 6: codec.write_str8 (value) # SSTR - elif typecode == 7: codec.write_str16 (value) # LSTR - elif typecode == 8: codec.write_int64 (long(value)) # ABSTIME - elif typecode == 9: codec.write_uint64 (long(value)) # DELTATIME - elif typecode == 10: value.encode (codec) # REF - elif typecode == 11: codec.write_uint8 (int(value)) # BOOL - elif typecode == 12: codec.write_float (float(value)) # FLOAT - elif typecode == 13: codec.write_double (double(value)) # DOUBLE - elif typecode == 14: codec.write_uuid (value.bytes) # UUID - elif typecode == 15: codec.write_map (value) # FTABLE - elif typecode == 16: codec.write_int8 (int(value)) # S8 - elif typecode == 17: codec.write_int16 (int(value)) # S16 - elif typecode == 18: codec.write_int32 (int(value)) # S32 - elif typecode == 19: codec.write_int64 (int(value)) # S64 - else: - raise ValueError ("Invalid type code: %d" % typecode) - - def _displayValue(self, value, typecode): - """ """ - if typecode == 1: return unicode(value) - elif typecode == 2: return unicode(value) - elif typecode == 3: return unicode(value) - elif typecode == 4: return unicode(value) - elif typecode == 6: return value - elif typecode == 7: return value - elif typecode == 8: return unicode(strftime("%c", gmtime(value / 1000000000))) - elif typecode == 9: return unicode(value) - elif typecode == 10: return unicode(value.__repr__()) - elif typecode == 11: - if value: return u"T" - else: return u"F" - elif typecode == 12: return unicode(value) - elif typecode == 13: return unicode(value) - elif typecode == 14: return unicode(value.__repr__()) - elif typecode == 15: return unicode(value.__repr__()) - elif typecode == 16: return unicode(value) - elif typecode == 17: return unicode(value) - elif typecode == 18: return unicode(value) - elif typecode == 19: return unicode(value) - else: - raise ValueError ("Invalid type code: %d" % typecode) - - def _sendMethodRequest(self, broker, schemaKey, objectId, name, argList): - """ This function can be used to send a method request to an object given only the - broker, schemaKey, and objectId. This is an uncommon usage pattern as methods are - normally invoked on the object itself. - """ - schema = self.getSchema(schemaKey) - for method in schema.getMethods(): - if name == method.name: - aIdx = 0 - sendCodec = Codec(broker.conn.spec) - seq = self.seqMgr._reserve((method, False)) - broker._setHeader(sendCodec, 'M', seq) - objectId.encode(sendCodec) - pname, cname, hash = schemaKey - sendCodec.write_str8(pname) - sendCodec.write_str8(cname) - sendCodec.write_bin128(hash) - sendCodec.write_str8(name) - - count = 0 - for arg in method.arguments: - if arg.dir.find("I") != -1: - count += 1 - if count != len(argList): - raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(argList))) - - for arg in method.arguments: - if arg.dir.find("I") != -1: - self._encodeValue(sendCodec, argList[aIdx], arg.type) - aIdx += 1 - smsg = broker._message(sendCodec.encoded, "agent.%d.%d" % - (objectId.getBrokerBank(), objectId.getAgentBank())) - broker._send(smsg) - return seq - return None - -class Package: - """ """ - def __init__(self, name): - self.name = name - -class SchemaClass: - """ """ - CLASS_KIND_TABLE = 1 - CLASS_KIND_EVENT = 2 - - def __init__(self, kind, key, codec): - self.kind = kind - self.classKey = key - self.properties = [] - self.statistics = [] - self.methods = [] - self.arguments = [] - - if self.kind == self.CLASS_KIND_TABLE: - propCount = codec.read_uint16() - statCount = codec.read_uint16() - methodCount = codec.read_uint16() - for idx in range(propCount): - self.properties.append(SchemaProperty(codec)) - for idx in range(statCount): - self.statistics.append(SchemaStatistic(codec)) - for idx in range(methodCount): - self.methods.append(SchemaMethod(codec)) - - elif self.kind == self.CLASS_KIND_EVENT: - argCount = codec.read_uint16() - for idx in range(argCount): - self.arguments.append(SchemaArgument(codec, methodArg=False)) - - def __repr__(self): - pname, cname, hash = self.classKey - if self.kind == self.CLASS_KIND_TABLE: - kindStr = "Table" - elif self.kind == self.CLASS_KIND_EVENT: - kindStr = "Event" - else: - kindStr = "Unsupported" - result = "%s Class: %s:%s " % (kindStr, pname, cname) - result += "(%08x-%04x-%04x-%04x-%04x%08x)" % struct.unpack ("!LHHHHL", hash) - return result - - def getKey(self): - """ Return the class-key for this class. """ - return self.classKey - - def getProperties(self): - """ Return the list of properties for the class. """ - return self.properties - - def getStatistics(self): - """ Return the list of statistics for the class. """ - return self.statistics - - def getMethods(self): - """ Return the list of methods for the class. """ - return self.methods - - def getArguments(self): - """ Return the list of events for the class. """ - return self.arguments - -class SchemaProperty: - """ """ - def __init__(self, codec): - map = codec.read_map() - self.name = str(map["name"]) - self.type = map["type"] - self.access = str(map["access"]) - self.index = map["index"] != 0 - self.optional = map["optional"] != 0 - self.unit = None - self.min = None - self.max = None - self.maxlen = None - self.desc = None - - for key, value in map.items(): - if key == "unit" : self.unit = value - elif key == "min" : self.min = value - elif key == "max" : self.max = value - elif key == "maxlen" : self.maxlen = value - elif key == "desc" : self.desc = value - - def __repr__(self): - return self.name - -class SchemaStatistic: - """ """ - def __init__(self, codec): - map = codec.read_map() - self.name = str(map["name"]) - self.type = map["type"] - self.unit = None - self.desc = None - - for key, value in map.items(): - if key == "unit" : self.unit = value - elif key == "desc" : self.desc = value - - def __repr__(self): - return self.name - -class SchemaMethod: - """ """ - def __init__(self, codec): - map = codec.read_map() - self.name = str(map["name"]) - argCount = map["argCount"] - if "desc" in map: - self.desc = map["desc"] - else: - self.desc = None - self.arguments = [] - - for idx in range(argCount): - self.arguments.append(SchemaArgument(codec, methodArg=True)) - - def __repr__(self): - result = self.name + "(" - first = True - for arg in self.arguments: - if arg.dir.find("I") != -1: - if first: - first = False - else: - result += ", " - result += arg.name - result += ")" - return result - -class SchemaArgument: - """ """ - def __init__(self, codec, methodArg): - map = codec.read_map() - self.name = str(map["name"]) - self.type = map["type"] - if methodArg: - self.dir = str(map["dir"]).upper() - self.unit = None - self.min = None - self.max = None - self.maxlen = None - self.desc = None - self.default = None - - for key, value in map.items(): - if key == "unit" : self.unit = value - elif key == "min" : self.min = value - elif key == "max" : self.max = value - elif key == "maxlen" : self.maxlen = value - elif key == "desc" : self.desc = value - elif key == "default" : self.default = value - -class ObjectId: - """ Object that represents QMF object identifiers """ - def __init__(self, codec, first=0, second=0): - if codec: - self.first = codec.read_uint64() - self.second = codec.read_uint64() - else: - self.first = first - self.second = second - - def __cmp__(self, other): - if other == None or not isinstance(other, ObjectId) : - return 1 - if self.first < other.first: - return -1 - if self.first > other.first: - return 1 - if self.second < other.second: - return -1 - if self.second > other.second: - return 1 - return 0 - - def __repr__(self): - return "%d-%d-%d-%d-%d" % (self.getFlags(), self.getSequence(), - self.getBrokerBank(), self.getAgentBank(), self.getObject()) - - def index(self): - return (self.first, self.second) - - def getFlags(self): - return (self.first & 0xF000000000000000) >> 60 - - def getSequence(self): - return (self.first & 0x0FFF000000000000) >> 48 - - def getBrokerBank(self): - return (self.first & 0x0000FFFFF0000000) >> 28 - - def getAgentBank(self): - return self.first & 0x000000000FFFFFFF - - def getObject(self): - return self.second - - def isDurable(self): - return self.getSequence() == 0 - - def encode(self, codec): - codec.write_uint64(self.first) - codec.write_uint64(self.second) - -class Object(object): - """ """ - def __init__(self, session, broker, schema, codec, prop, stat): - """ """ - self._session = session - self._broker = broker - self._schema = schema - self._currentTime = codec.read_uint64() - self._createTime = codec.read_uint64() - self._deleteTime = codec.read_uint64() - self._objectId = ObjectId(codec) - self._properties = [] - self._statistics = [] - if prop: - notPresent = self._parsePresenceMasks(codec, schema) - for property in schema.getProperties(): - if property.name in notPresent: - self._properties.append((property, None)) - else: - self._properties.append((property, self._session._decodeValue(codec, property.type))) - if stat: - for statistic in schema.getStatistics(): - self._statistics.append((statistic, self._session._decodeValue(codec, statistic.type))) - - def getBroker(self): - """ Return the broker from which this object was sent """ - return self._broker - - def getObjectId(self): - """ Return the object identifier for this object """ - return self._objectId - - def getClassKey(self): - """ Return the class-key that references the schema describing this object. """ - return self._schema.getKey() - - def getSchema(self): - """ Return the schema that describes this object. """ - return self._schema - - def getMethods(self): - """ Return a list of methods available for this object. """ - return self._schema.getMethods() - - def getTimestamps(self): - """ Return the current, creation, and deletion times for this object. """ - return self._currentTime, self._createTime, self._deleteTime - - def getIndex(self): - """ Return a string describing this object's primary key. """ - result = u"" - for property, value in self._properties: - if property.index: - if result != u"": - result += u":" - try: - valstr = unicode(value) - except: - valstr = u"" - result += valstr - return result - - def getProperties(self): - return self._properties - - def getStatistics(self): - return self._statistics - - def mergeUpdate(self, newer): - """ Replace properties and/or statistics with a newly received update """ - if self._objectId != newer._objectId: - raise Exception("Objects with different object-ids") - if len(newer.getProperties()) > 0: - self.properties = newer.getProperties() - if len(newer.getStatistics()) > 0: - self.statistics = newer.getStatistics() - - def __repr__(self): - return self.getIndex().encode("utf8") - - def __getattr__(self, name): - for method in self._schema.getMethods(): - if name == method.name: - return lambda *args, **kwargs : self._invoke(name, args, kwargs) - for property, value in self._properties: - if name == property.name: - return value - if name == "_" + property.name + "_" and property.type == 10: # Dereference references - deref = self._session.getObjects(_objectId=value, _broker=self._broker) - if len(deref) != 1: - return None - else: - return deref[0] - for statistic, value in self._statistics: - if name == statistic.name: - return value - raise Exception("Type Object has no attribute '%s'" % name) - - def _sendMethodRequest(self, name, args, kwargs, synchronous=False): - for method in self._schema.getMethods(): - if name == method.name: - aIdx = 0 - sendCodec = Codec(self._broker.conn.spec) - seq = self._session.seqMgr._reserve((method, synchronous)) - self._broker._setHeader(sendCodec, 'M', seq) - self._objectId.encode(sendCodec) - pname, cname, hash = self._schema.getKey() - sendCodec.write_str8(pname) - sendCodec.write_str8(cname) - sendCodec.write_bin128(hash) - sendCodec.write_str8(name) - - count = 0 - for arg in method.arguments: - if arg.dir.find("I") != -1: - count += 1 - if count != len(args): - raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(args))) - - for arg in method.arguments: - if arg.dir.find("I") != -1: - self._session._encodeValue(sendCodec, args[aIdx], arg.type) - aIdx += 1 - smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % - (self._objectId.getBrokerBank(), self._objectId.getAgentBank())) - if synchronous: - try: - self._broker.cv.acquire() - self._broker.syncInFlight = True - finally: - self._broker.cv.release() - self._broker._send(smsg) - return seq - return None - - def _invoke(self, name, args, kwargs): - if self._sendMethodRequest(name, args, kwargs, True): - try: - self._broker.cv.acquire() - starttime = time() - while self._broker.syncInFlight and self._broker.error == None: - self._broker.cv.wait(self._broker.SYNC_TIME) - if time() - starttime > self._broker.SYNC_TIME: - self._session.seqMgr._release(seq) - raise RuntimeError("Timed out waiting for method to respond") - finally: - self._broker.cv.release() - if self._broker.error != None: - errorText = self._broker.error - self._broker.error = None - raise Exception(errorText) - return self._broker.syncResult - raise Exception("Invalid Method (software defect) [%s]" % name) - - def _parsePresenceMasks(self, codec, schema): - excludeList = [] - bit = 0 - for property in schema.getProperties(): - if property.optional: - if bit == 0: - mask = codec.read_uint8() - bit = 1 - if (mask & bit) == 0: - excludeList.append(property.name) - bit *= 2 - if bit == 256: - bit = 0 - return excludeList - -class MethodResult(object): - """ """ - def __init__(self, status, text, outArgs): - """ """ - self.status = status - self.text = text - self.outArgs = outArgs - - def __getattr__(self, name): - if name in self.outArgs: - return self.outArgs[name] - - def __repr__(self): - return "%s (%d) - %s" % (self.text, self.status, self.outArgs) - -class Broker: - """ """ - SYNC_TIME = 60 - - def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): - self.session = session - self.host = host - self.port = port - self.ssl = ssl - self.authUser = authUser - self.authPass = authPass - self.agents = {} - self.agents["1.0"] = Agent(self, "1.0", "BrokerAgent") - self.topicBound = False - self.cv = Condition() - self.syncInFlight = False - self.syncRequest = 0 - self.syncResult = None - self.reqsOutstanding = 1 - self.error = None - self.brokerId = None - self.isConnected = False - self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self._tryToConnect() - - def isConnected(self): - return self.isConnected - - def getError(self): - return self.error - - def getBrokerId(self): - """ Get broker's unique identifier (UUID) """ - return self.brokerId - - def getBrokerBank(self): - return 1 - - def getAgent(self, brokerBank, agentBank): - bankKey = "%d.%d" % (brokerBank, agentBank) - if bankKey in self.agents: - return self.agents[bankKey] - return None - - def getSessionId(self): - """ Get the identifier of the AMQP session to the broker """ - return self.amqpSessionId - - def getAgents(self): - """ Get the list of agents reachable via this broker """ - return self.agents.values() - - def getAmqpSession(self): - """ Get the AMQP session object for this connected broker. """ - return self.amqpSession - - def getUrl(self): - """ """ - return "%s:%d" % (self.host, self.port) - - def getFullUrl(self, noAuthIfGuestDefault=True): - """ """ - ssl = "" - if self.ssl: - ssl = "s" - auth = "%s/%s@" % (self.authUser, self.authPass) - if self.authUser == "" or \ - (noAuthIfGuestDefault and self.authUser == "guest" and self.authPass == "guest"): - auth = "" - return "amqp%s://%s%s:%d" % (ssl, auth, self.host, self.port or 5672) - - def __repr__(self): - if self.isConnected: - return "Broker connected at: %s" % self.getUrl() - else: - return "Disconnected Broker" - - def _tryToConnect(self): - try: - sock = connect(self.host, self.port) - if self.ssl: - sock = ssl(sock) - self.conn = Connection(sock, username=self.authUser, password=self.authPass) - self.conn.start() - self.replyName = "reply-%s" % self.amqpSessionId - self.amqpSession = self.conn.session(self.amqpSessionId) - self.amqpSession.auto_sync = True - self.amqpSession.queue_declare(queue=self.replyName, exclusive=True, auto_delete=True) - self.amqpSession.exchange_bind(exchange="amq.direct", - queue=self.replyName, binding_key=self.replyName) - self.amqpSession.message_subscribe(queue=self.replyName, destination="rdest", - accept_mode=self.amqpSession.accept_mode.none, - acquire_mode=self.amqpSession.acquire_mode.pre_acquired) - self.amqpSession.incoming("rdest").listen(self._replyCb, self._exceptionCb) - self.amqpSession.message_set_flow_mode(destination="rdest", flow_mode=1) - self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFF) - self.amqpSession.message_flow(destination="rdest", unit=1, value=0xFFFFFFFF) - - self.topicName = "topic-%s" % self.amqpSessionId - self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) - self.amqpSession.message_subscribe(queue=self.topicName, destination="tdest", - accept_mode=self.amqpSession.accept_mode.none, - acquire_mode=self.amqpSession.acquire_mode.pre_acquired) - self.amqpSession.incoming("tdest").listen(self._replyCb) - self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) - self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) - self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) - - self.isConnected = True - self.session._handleBrokerConnect(self) - - codec = Codec(self.conn.spec) - self._setHeader(codec, 'B') - msg = self._message(codec.encoded) - self._send(msg) - - except socket.error, e: - self.error = "Socket Error %s - %s" % (e[0], e[1]) - except Closed, e: - self.error = "Connect Failed %d - %s" % (e[0], e[1]) - except ConnectionFailed, e: - self.error = "Connect Failed %d - %s" % (e[0], e[1]) - - def _updateAgent(self, obj): - bankKey = "%d.%d" % (obj.brokerBank, obj.agentBank) - if obj._deleteTime == 0: - if bankKey not in self.agents: - agent = Agent(self, bankKey, obj.label) - self.agents[bankKey] = agent - if self.session.console != None: - self.session.console.newAgent(agent) - else: - agent = self.agents.pop(bankKey, None) - if agent != None and self.session.console != None: - self.session.console.delAgent(agent) - - def _setHeader(self, codec, opcode, seq=0): - """ Compose the header of a management message. """ - codec.write_uint8(ord('A')) - codec.write_uint8(ord('M')) - codec.write_uint8(ord('2')) - codec.write_uint8(ord(opcode)) - codec.write_uint32(seq) - - def _checkHeader(self, codec): - """ Check the header of a management message and extract the opcode and class. """ - try: - octet = chr(codec.read_uint8()) - if octet != 'A': - return None, None - octet = chr(codec.read_uint8()) - if octet != 'M': - return None, None - octet = chr(codec.read_uint8()) - if octet != '2': - return None, None - opcode = chr(codec.read_uint8()) - seq = codec.read_uint32() - return opcode, seq - except: - return None, None - - def _message (self, body, routing_key="broker"): - dp = self.amqpSession.delivery_properties() - dp.routing_key = routing_key - mp = self.amqpSession.message_properties() - mp.content_type = "x-application/qmf" - mp.reply_to = self.amqpSession.reply_to("amq.direct", self.replyName) - return Message(dp, mp, body) - - def _send(self, msg, dest="qpid.management"): - self.amqpSession.message_transfer(destination=dest, message=msg) - - def _shutdown(self): - if self.isConnected: - self.amqpSession.incoming("rdest").stop() - if self.session.console != None: - self.amqpSession.incoming("tdest").stop() - self.amqpSession.close() - self.conn.close() - self.isConnected = False - else: - raise Exception("Broker already disconnected") - - def _waitForStable(self): - try: - self.cv.acquire() - if self.reqsOutstanding == 0: - return - self.syncInFlight = True - starttime = time() - while self.reqsOutstanding != 0: - self.cv.wait(self.SYNC_TIME) - if time() - starttime > self.SYNC_TIME: - raise RuntimeError("Timed out waiting for broker to synchronize") - finally: - self.cv.release() - - def _incOutstanding(self): - try: - self.cv.acquire() - self.reqsOutstanding += 1 - finally: - self.cv.release() - - def _decOutstanding(self): - try: - self.cv.acquire() - self.reqsOutstanding -= 1 - if self.reqsOutstanding == 0 and not self.topicBound: - self.topicBound = True - for key in self.session.bindingKeyList: - self.amqpSession.exchange_bind(exchange="qpid.management", - queue=self.topicName, binding_key=key) - if self.reqsOutstanding == 0 and self.syncInFlight: - self.syncInFlight = False - self.cv.notify() - finally: - self.cv.release() - - def _replyCb(self, msg): - codec = Codec(self.conn.spec, msg.body) - while True: - opcode, seq = self._checkHeader(codec) - if opcode == None: return - if opcode == 'b': self.session._handleBrokerResp (self, codec, seq) - elif opcode == 'p': self.session._handlePackageInd (self, codec, seq) - elif opcode == 'z': self.session._handleCommandComplete (self, codec, seq) - elif opcode == 'q': self.session._handleClassInd (self, codec, seq) - elif opcode == 'm': self.session._handleMethodResp (self, codec, seq) - elif opcode == 'h': self.session._handleHeartbeatInd (self, codec, seq, msg) - elif opcode == 'e': self.session._handleEventInd (self, codec, seq) - elif opcode == 's': self.session._handleSchemaResp (self, codec, seq) - elif opcode == 'c': self.session._handleContentInd (self, codec, seq, prop=True) - elif opcode == 'i': self.session._handleContentInd (self, codec, seq, stat=True) - elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) - - def _exceptionCb(self, data): - self.isConnected = False - self.error = data - try: - self.cv.acquire() - if self.syncInFlight: - self.cv.notify() - finally: - self.cv.release() - self.session._handleError(self.error) - self.session._handleBrokerDisconnect(self) - -class Agent: - """ """ - def __init__(self, broker, bank, label): - self.broker = broker - self.bank = bank - self.label = label - - def __repr__(self): - return "Agent at bank %s (%s)" % (self.bank, self.label) - - def getBroker(self): - return self.broker - - def getAgentBank(self): - return self.bank - -class Event: - """ """ - def __init__(self, session, broker, codec): - self.session = session - self.broker = broker - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - self.classKey = (pname, cname, hash) - self.timestamp = codec.read_int64() - self.severity = codec.read_uint8() - self.schema = None - if pname in session.packages: - if (cname, hash) in session.packages[pname]: - self.schema = session.packages[pname][(cname, hash)] - self.arguments = {} - for arg in self.schema.arguments: - self.arguments[arg.name] = session._decodeValue(codec, arg.type) - - def __repr__(self): - if self.schema == None: - return "" - out = strftime("%c", gmtime(self.timestamp / 1000000000)) - out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] - out += " broker=" + self.broker.getUrl() - for arg in self.schema.arguments: - disp = self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8") - if " " in disp: - disp = "\"" + disp + "\"" - out += " " + arg.name + "=" + disp - return out - - def _sevName(self): - if self.severity == 0 : return "EMER " - if self.severity == 1 : return "ALERT" - if self.severity == 2 : return "CRIT " - if self.severity == 3 : return "ERROR" - if self.severity == 4 : return "WARN " - if self.severity == 5 : return "NOTIC" - if self.severity == 6 : return "INFO " - if self.severity == 7 : return "DEBUG" - return "INV-%d" % self.severity - - def getClassKey(self): - return self.classKey - - def getArguments(self): - return self.arguments - - def getTimestamp(self): - return self.timestamp - - def getName(self): - return self.name - - def getSchema(self): - return self.schema - -class SequenceManager: - """ Manage sequence numbers for asynchronous method calls """ - def __init__(self): - self.lock = Lock() - self.sequence = 0 - self.pending = {} - - def _reserve(self, data): - """ Reserve a unique sequence number """ - try: - self.lock.acquire() - result = self.sequence - self.sequence = self.sequence + 1 - self.pending[result] = data - finally: - self.lock.release() - return result - - def _release(self, seq): - """ Release a reserved sequence number """ - data = None - try: - self.lock.acquire() - if seq in self.pending: - data = self.pending[seq] - del self.pending[seq] - finally: - self.lock.release() - return data - - -class DebugConsole(Console): - """ """ - def brokerConnected(self, broker): - print "brokerConnected:", broker - - def brokerDisconnected(self, broker): - print "brokerDisconnected:", broker - - def newPackage(self, name): - print "newPackage:", name - - def newClass(self, kind, classKey): - print "newClass:", kind, classKey - - def newAgent(self, agent): - print "newAgent:", agent - - def delAgent(self, agent): - print "delAgent:", agent - - def objectProps(self, broker, record): - print "objectProps:", record.getClassKey() - - def objectStats(self, broker, record): - print "objectStats:", record.getClassKey() - - def event(self, broker, event): - print "event:", event - - def heartbeat(self, agent, timestamp): - print "heartbeat:", agent - - def brokerInfo(self, broker): - print "brokerInfo:", broker - diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 53723148b7..18ef030ec0 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -22,7 +22,7 @@ # import sys, re, unittest, os, random, logging, traceback -import qpid.client, qpid.spec, qpid.qmfconsole +import qpid.client, qpid.spec, qmf.console import Queue from fnmatch import fnmatch from getopt import getopt, GetoptError @@ -364,7 +364,7 @@ class TestBase010(unittest.TestCase): self.qmf = None def startQmf(self): - self.qmf = qpid.qmfconsole.Session() + self.qmf = qmf.console.Session() self.qmf_broker = self.qmf.addBroker(str(testrunner.url)) def connect(self, host=None, port=None): -- cgit v1.2.1 From ed6ad2636dfa46133f7202dea30313ba96b6fcb8 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 21 Nov 2008 13:53:53 +0000 Subject: code cleanup on qmf console API Formalized schema-class as an object with an all-string __repr__ Added additional testing for the console API git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@719580 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 143 +++++++++++++++++++++-------------- qpid/python/tests_0-10/management.py | 23 +++++- 2 files changed, 105 insertions(+), 61 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index bdd93e6f94..22c499e40a 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -171,11 +171,11 @@ class Session: def __repr__(self): return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) - def addBroker(self, target="localhost"): + def addBroker(self, target="localhost", initialTopicCredits=0xFFFFFFFF): """ Connect to a Qpid broker. Returns an object of type Broker. """ url = BrokerURL(target) broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass, - ssl = url.scheme == URL.AMQPS) + ssl = url.scheme == URL.AMQPS, topicCredits=initialTopicCredits) if not broker.isConnected and not self.manageConnections: raise Exception(broker.error) @@ -205,18 +205,19 @@ class Session: broker._waitForStable() list = [] if packageName in self.packages: - for cname, hash in self.packages[packageName]: - list.append((packageName, cname, hash)) + for pkey in self.packages[packageName]: + list.append(self.packages[packageName][pkey].getKey()) return list def getSchema(self, classKey): """ Get the schema for a QMF class """ for broker in self.brokers: broker._waitForStable() - pname, cname, hash = classKey + pname = classKey.getPackageName() + pkey = classKey.getPackageKey() if pname in self.packages: - if (cname, hash) in self.packages[pname]: - return self.packages[pname][(cname, hash)] + if pkey in self.packages[pname]: + return self.packages[pname][pkey] def bindPackage(self, packageName): """ """ @@ -230,7 +231,8 @@ class Session: """ """ if not self.userBindings or not self.rcvObjects: raise Exception("userBindings option not set for Session") - pname, cname, hash = classKey + pname = classKey.getPackageName() + cname = classKey.getClassName() for broker in self.brokers: broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, binding_key="console.obj.*.*.%s.%s.#" % (pname, cname)) @@ -297,14 +299,17 @@ class Session: for agent in broker.getAgents(): agentList.append(agent) + pname = None cname = None - if "_schema" in kwargs: pname, cname, hash = kwargs["_schema"].getKey() - elif "_key" in kwargs: pname, cname, hash = kwargs["_key"] + hash = None + classKey = None + if "_schema" in kwargs: classKey = kwargs["_schema"].getKey() + elif "_key" in kwargs: classKey = kwargs["_key"] elif "_class" in kwargs: - pname, cname, hash = None, kwargs["_class"], None + cname = kwargs["_class"] if "_package" in kwargs: pname = kwargs["_package"] - if cname == None and "_objectId" not in kwargs: + if cname == None and classKey == None and "_objectId" not in kwargs: raise Exception("No class supplied, use '_schema', '_key', '_class', or '_objectId' argument") map = {} @@ -312,6 +317,10 @@ class Session: if "_objectId" in kwargs: map["_objectid"] = kwargs["_objectId"].__repr__() else: + if cname == None: + cname = classKey.getClassName() + pname = classKey.getPackageName() + hash = classKey.getHash() map["_class"] = cname if pname != None: map["_package"] = pname if hash != None: map["_hash"] = hash @@ -442,15 +451,13 @@ class Session: def _handleClassInd(self, broker, codec, seq): kind = codec.read_uint8() - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() + classKey = ClassKey(codec) unknown = False try: self.cv.acquire() - if pname in self.packages: - if (cname, hash) not in self.packages[pname]: + if classKey.getPackageName() in self.packages: + if classKey.getPackageKey() not in self.packages[classKey.getPackageName()]: unknown = True finally: self.cv.release() @@ -461,9 +468,7 @@ class Session: sendCodec = Codec(broker.conn.spec) seq = self.seqMgr._reserve(self._CONTEXT_STARTUP) broker._setHeader(sendCodec, 'S', seq) - sendCodec.write_str8(pname) - sendCodec.write_str8(cname) - sendCodec.write_bin128(hash) + classKey.encode(sendCodec) smsg = broker._message(sendCodec.encoded) broker._send(smsg) @@ -512,14 +517,11 @@ class Session: def _handleSchemaResp(self, broker, codec, seq): kind = codec.read_uint8() - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - classKey = (pname, cname, hash) + classKey = ClassKey(codec) _class = SchemaClass(kind, classKey, codec) try: self.cv.acquire() - self.packages[pname][(cname, hash)] = _class + self.packages[classKey.getPackageName()][classKey.getPackageKey()] = _class finally: self.cv.release() @@ -529,22 +531,21 @@ class Session: self.console.newClass(kind, classKey) def _handleContentInd(self, broker, codec, seq, prop=False, stat=False): - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - classKey = (pname, cname, hash) + classKey = ClassKey(codec) try: self.cv.acquire() + pname = classKey.getPackageName() if pname not in self.packages: return - if (cname, hash) not in self.packages[pname]: + pkey = classKey.getPackageKey() + if pkey not in self.packages[pname]: return - schema = self.packages[pname][(cname, hash)] + schema = self.packages[pname][pkey] finally: self.cv.release() object = Object(self, broker, schema, codec, prop, stat) - if pname == "org.apache.qpid.broker" and cname == "agent": + if pname == "org.apache.qpid.broker" and classKey.getClassName() == "agent": broker._updateAgent(object) try: @@ -664,10 +665,7 @@ class Session: seq = self.seqMgr._reserve((method, False)) broker._setHeader(sendCodec, 'M', seq) objectId.encode(sendCodec) - pname, cname, hash = schemaKey - sendCodec.write_str8(pname) - sendCodec.write_str8(cname) - sendCodec.write_bin128(hash) + schemaKey.encode(sendCodec) sendCodec.write_str8(name) count = 0 @@ -692,6 +690,36 @@ class Package: def __init__(self, name): self.name = name +class ClassKey: + """ """ + def __init__(self, codec): + self.pname = str(codec.read_str8()) + self.cname = str(codec.read_str8()) + self.hash = codec.read_bin128() + + def encode(self, codec): + codec.write_str8(self.pname) + codec.write_str8(self.cname) + codec.write_bin128(self.hash) + + def getPackageName(self): + return self.pname + + def getClassName(self): + return self.cname + + def getHash(self): + return self.hash + + def getHashString(self): + return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", self.hash) + + def getPackageKey(self): + return (self.cname, self.hash) + + def __repr__(self): + return self.pname + ":" + self.cname + "(" + self.getHashString() + ")" + class SchemaClass: """ """ CLASS_KIND_TABLE = 1 @@ -722,15 +750,13 @@ class SchemaClass: self.arguments.append(SchemaArgument(codec, methodArg=False)) def __repr__(self): - pname, cname, hash = self.classKey if self.kind == self.CLASS_KIND_TABLE: kindStr = "Table" elif self.kind == self.CLASS_KIND_EVENT: kindStr = "Event" else: kindStr = "Unsupported" - result = "%s Class: %s:%s " % (kindStr, pname, cname) - result += "(%08x-%04x-%04x-%04x-%04x%08x)" % struct.unpack ("!LHHHHL", hash) + result = "%s Class: %s " % (kindStr, self.classKey.__repr__()) return result def getKey(self): @@ -1003,10 +1029,7 @@ class Object(object): seq = self._session.seqMgr._reserve((method, synchronous)) self._broker._setHeader(sendCodec, 'M', seq) self._objectId.encode(sendCodec) - pname, cname, hash = self._schema.getKey() - sendCodec.write_str8(pname) - sendCodec.write_str8(cname) - sendCodec.write_bin128(hash) + self._schema.getKey().encode(sendCodec) sendCodec.write_str8(name) count = 0 @@ -1085,14 +1108,16 @@ class Broker: """ """ SYNC_TIME = 60 - def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): + def __init__(self, session, host, port, authMech, authUser, authPass, + ssl=False, topicCredits=0xFFFFFFFF): self.session = session - self.host = host - self.port = port + self.host = host + self.port = port self.ssl = ssl self.authUser = authUser self.authPass = authPass - self.agents = {} + self.topicCredits = topicCredits + self.agents = {} self.agents["1.0"] = Agent(self, "1.0", "BrokerAgent") self.topicBound = False self.cv = Condition() @@ -1100,8 +1125,8 @@ class Broker: self.syncRequest = 0 self.syncResult = None self.reqsOutstanding = 1 - self.error = None - self.brokerId = None + self.error = None + self.brokerId = None self.isConnected = False self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) self._tryToConnect() @@ -1152,6 +1177,9 @@ class Broker: auth = "" return "amqp%s://%s%s:%d" % (ssl, auth, self.host, self.port or 5672) + def replenishCredits(self, credits): + self.amqpSession.message_flow(destination="tdest", unit=0, value=credits) + def __repr__(self): if self.isConnected: return "Broker connected at: %s" % self.getUrl() @@ -1185,8 +1213,8 @@ class Broker: accept_mode=self.amqpSession.accept_mode.none, acquire_mode=self.amqpSession.acquire_mode.pre_acquired) self.amqpSession.incoming("tdest").listen(self._replyCb) - self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) - self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) + self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=0) + self.amqpSession.message_flow(destination="tdest", unit=0, value=self.topicCredits) self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) self.isConnected = True @@ -1351,16 +1379,15 @@ class Event: def __init__(self, session, broker, codec): self.session = session self.broker = broker - pname = str(codec.read_str8()) - cname = str(codec.read_str8()) - hash = codec.read_bin128() - self.classKey = (pname, cname, hash) + self.classKey = ClassKey(codec) self.timestamp = codec.read_int64() self.severity = codec.read_uint8() self.schema = None + pname = self.classKey.getPackageName() + pkey = self.classKey.getPackageKey() if pname in session.packages: - if (cname, hash) in session.packages[pname]: - self.schema = session.packages[pname][(cname, hash)] + if pkey in session.packages[pname]: + self.schema = session.packages[pname][pkey] self.arguments = {} for arg in self.schema.arguments: self.arguments[arg.name] = session._decodeValue(codec, arg.type) @@ -1369,7 +1396,7 @@ class Event: if self.schema == None: return "" out = strftime("%c", gmtime(self.timestamp / 1000000000)) - out += " " + self._sevName() + " " + self.classKey[0] + ":" + self.classKey[1] + out += " " + self._sevName() + " " + self.classKey.getPackageName() + ":" + self.classKey.getClassName() out += " broker=" + self.broker.getUrl() for arg in self.schema.arguments: disp = self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8") diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index a47982173b..0632d85da4 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -71,10 +71,27 @@ class ManagementTest (TestBase010): self.assertEqual (res.sequence, seq) self.assertEqual (res.body, body) - def test_system_object (self): + def test_get_objects(self): self.startQmf() - systems = self.qmf.getObjects(_class="system") - self.assertEqual (len (systems), 1) + + # get the package list, verify that the qpid broker package is there + packages = self.qmf.getPackages() + assert 'org.apache.qpid.broker' in packages + + # get the schema class keys for the broker, verify the broker table and link-down event + keys = self.qmf.getClasses('org.apache.qpid.broker') + broker = None + linkDown = None + for key in keys: + if key.getClassName() == "broker": broker = key + if key.getClassName() == "brokerLinkDown" : linkDown = key + assert broker + assert linkDown + + brokerObjs = self.qmf.getObjects(_class="broker") + assert len(brokerObjs) == 1 + brokerObjs = self.qmf.getObjects(_key=broker) + assert len(brokerObjs) == 1 def test_self_session_id (self): self.startQmf() -- cgit v1.2.1 From dd41ba8fdfecb5e60e41b714945a7ef852d7b876 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 21 Nov 2008 15:36:05 +0000 Subject: Add ability to construct a ClassKey from it's __repr__ string. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@719597 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 22c499e40a..4e74cac4ee 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -691,11 +691,28 @@ class Package: self.name = name class ClassKey: - """ """ - def __init__(self, codec): - self.pname = str(codec.read_str8()) - self.cname = str(codec.read_str8()) - self.hash = codec.read_bin128() + """ A ClassKey uniquely identifies a class from the schema. """ + def __init__(self, constructor): + if type(constructor) == str: + # construct from __repr__ string + try: + self.pname, cls = constructor.split(":") + self.cname, hsh = cls.split("(") + hsh = hsh.strip(")") + hexValues = hsh.split("-") + h0 = int(hexValues[0], 16) + h1 = int(hexValues[1], 16) + h2 = int(hexValues[2], 16) + h3 = int(hexValues[3], 16) + self.hash = struct.pack("!LLLL", h0, h1, h2, h3) + except: + raise Exception("Invalid ClassKey format") + else: + # construct from codec + codec = constructor + self.pname = str(codec.read_str8()) + self.cname = str(codec.read_str8()) + self.hash = codec.read_bin128() def encode(self, codec): codec.write_str8(self.pname) @@ -712,7 +729,7 @@ class ClassKey: return self.hash def getHashString(self): - return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", self.hash) + return "%08x-%08x-%08x-%08x" % struct.unpack ("!LLLL", self.hash) def getPackageKey(self): return (self.cname, self.hash) -- cgit v1.2.1 From a00e575e242f6c5c9673948efc905226f5a0bfd2 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 21 Nov 2008 20:17:22 +0000 Subject: Fixed several problems related to qmf update timestamps: - Timestamps were set at update send time regardless of whether the object's contents were actually changed. Now timestamps are set at the time of the change. - Agent heartbeat messages are now being sent after periodic updates, not before. Cleaned up the Agent object in qmf.console. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@719699 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 4e74cac4ee..8ccbefaf47 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -340,7 +340,7 @@ class Session: self.cv.release() broker._setHeader(sendCodec, 'G', seq) sendCodec.write_map(map) - smsg = broker._message(sendCodec.encoded, "agent.%s" % agent.bank) + smsg = broker._message(sendCodec.encoded, "agent.%d.%d" % (agent.brokerBank, agent.agentBank)) broker._send(smsg) starttime = time() @@ -507,7 +507,7 @@ class Session: agent = broker.getAgent(brokerBank, agentBank) timestamp = codec.read_uint64() - if self.console != None: + if self.console != None and agent != None: self.console.heartbeat(agent, timestamp) def _handleEventInd(self, broker, codec, seq): @@ -1135,7 +1135,7 @@ class Broker: self.authPass = authPass self.topicCredits = topicCredits self.agents = {} - self.agents["1.0"] = Agent(self, "1.0", "BrokerAgent") + self.agents[(1,0)] = Agent(self, 0, "BrokerAgent") self.topicBound = False self.cv = Condition() self.syncInFlight = False @@ -1162,7 +1162,7 @@ class Broker: return 1 def getAgent(self, brokerBank, agentBank): - bankKey = "%d.%d" % (brokerBank, agentBank) + bankKey = (brokerBank, agentBank) if bankKey in self.agents: return self.agents[bankKey] return None @@ -1250,10 +1250,10 @@ class Broker: self.error = "Connect Failed %d - %s" % (e[0], e[1]) def _updateAgent(self, obj): - bankKey = "%d.%d" % (obj.brokerBank, obj.agentBank) + bankKey = (obj.brokerBank, obj.agentBank) if obj._deleteTime == 0: if bankKey not in self.agents: - agent = Agent(self, bankKey, obj.label) + agent = Agent(self, obj.agentBank, obj.label) self.agents[bankKey] = agent if self.session.console != None: self.session.console.newAgent(agent) @@ -1377,19 +1377,23 @@ class Broker: class Agent: """ """ - def __init__(self, broker, bank, label): + def __init__(self, broker, agentBank, label): self.broker = broker - self.bank = bank - self.label = label + self.brokerBank = broker.getBrokerBank() + self.agentBank = agentBank + self.label = label def __repr__(self): - return "Agent at bank %s (%s)" % (self.bank, self.label) + return "Agent at bank %d.%d (%s)" % (self.brokerBank, self.agentBank, self.label) def getBroker(self): return self.broker + def getBrokerBank(self): + return self.brokerBank + def getAgentBank(self): - return self.bank + return self.agentBank class Event: """ """ -- cgit v1.2.1 From f5bc6d047f2ea17048704c6e353078e9d2b27978 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 24 Nov 2008 14:07:57 +0000 Subject: Removed hooks for message credits. This will be implemented correctly later. The problem with exposing message credits to the API client is that the client doesn't know how many AMQP messages have been received, only how many QMF messages. Since remote agents batch QMF messages into AMQP messages, message credits can't be handled at the QMF API level. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@720197 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 8ccbefaf47..cdc3ae3406 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -171,11 +171,11 @@ class Session: def __repr__(self): return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) - def addBroker(self, target="localhost", initialTopicCredits=0xFFFFFFFF): + def addBroker(self, target="localhost"): """ Connect to a Qpid broker. Returns an object of type Broker. """ url = BrokerURL(target) broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass, - ssl = url.scheme == URL.AMQPS, topicCredits=initialTopicCredits) + ssl = url.scheme == URL.AMQPS) if not broker.isConnected and not self.manageConnections: raise Exception(broker.error) @@ -1125,15 +1125,13 @@ class Broker: """ """ SYNC_TIME = 60 - def __init__(self, session, host, port, authMech, authUser, authPass, - ssl=False, topicCredits=0xFFFFFFFF): + def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): self.session = session self.host = host self.port = port self.ssl = ssl self.authUser = authUser self.authPass = authPass - self.topicCredits = topicCredits self.agents = {} self.agents[(1,0)] = Agent(self, 0, "BrokerAgent") self.topicBound = False @@ -1194,9 +1192,6 @@ class Broker: auth = "" return "amqp%s://%s%s:%d" % (ssl, auth, self.host, self.port or 5672) - def replenishCredits(self, credits): - self.amqpSession.message_flow(destination="tdest", unit=0, value=credits) - def __repr__(self): if self.isConnected: return "Broker connected at: %s" % self.getUrl() @@ -1230,8 +1225,8 @@ class Broker: accept_mode=self.amqpSession.accept_mode.none, acquire_mode=self.amqpSession.acquire_mode.pre_acquired) self.amqpSession.incoming("tdest").listen(self._replyCb) - self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=0) - self.amqpSession.message_flow(destination="tdest", unit=0, value=self.topicCredits) + self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) + self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) self.isConnected = True -- cgit v1.2.1 From eec5bffa802b66904bba3b8c7af868ef318c0fdb Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 24 Nov 2008 14:36:52 +0000 Subject: Added __hash__ and __eq__ methods to ObjectId. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@720204 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index cdc3ae3406..17d179b4d6 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -940,6 +940,12 @@ class ObjectId: codec.write_uint64(self.first) codec.write_uint64(self.second) + def __hash__(self): + return (self.first, self.second).__hash__() + + def __eq__(self, other): + return (self.first, self.second).__eq__(other) + class Object(object): """ """ def __init__(self, session, broker, schema, codec, prop, stat): -- cgit v1.2.1 From 11c01cd3449b4b41f4d1de1558c6c66956ab450a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 24 Nov 2008 19:49:57 +0000 Subject: QPID-1483 - Connection handling in the Python QMF console API git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@720275 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 164 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 126 insertions(+), 38 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 17d179b4d6..58cc46ae6f 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -29,7 +29,7 @@ from qpid.connection import Connection, ConnectionFailed from qpid.datatypes import UUID, uuid4, Message, RangedSet from qpid.util import connect, ssl, URL from qpid.codec010 import StringCodec as Codec -from threading import Lock, Condition +from threading import Lock, Condition, Thread from time import time, strftime, gmtime from cStringIO import StringIO @@ -165,22 +165,18 @@ class Session: if self.userBindings and not self.rcvObjects: raise Exception("userBindings can't be set unless rcvObjects is set and a console is provided") - if manageConnections: - raise Exception("manageConnections - not yet implemented") - def __repr__(self): - return "QMF Console Session Manager (brokers connected: %d)" % len(self.brokers) + return "QMF Console Session Manager (brokers: %d)" % len(self.brokers) def addBroker(self, target="localhost"): """ Connect to a Qpid broker. Returns an object of type Broker. """ url = BrokerURL(target) broker = Broker(self, url.host, url.port, url.authMech, url.authName, url.authPass, ssl = url.scheme == URL.AMQPS) - if not broker.isConnected and not self.manageConnections: - raise Exception(broker.error) self.brokers.append(broker) - self.getObjects(broker=broker, _class="agent") + if not self.manageConnections: + self.getObjects(broker=broker, _class="agent") return broker def delBroker(self, broker): @@ -220,22 +216,32 @@ class Session: return self.packages[pname][pkey] def bindPackage(self, packageName): - """ """ + """ Request object updates for all table classes within a package. """ if not self.userBindings or not self.rcvObjects: raise Exception("userBindings option not set for Session") + key = "console.obj.*.*.%s.#" % packageName + self.bindingKeyList.append(key) for broker in self.brokers: - broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.*.*.%s.#" % packageName) + if broker.isConnected(): + broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, + binding_key=key) - def bindClass(self, classKey): - """ """ + def bindClass(self, pname, cname): + """ Request object updates for a particular table class by package and class name. """ if not self.userBindings or not self.rcvObjects: raise Exception("userBindings option not set for Session") + key = "console.obj.*.*.%s.%s.#" % (pname, cname) + self.bindingKeyList.append(key) + for broker in self.brokers: + if broker.isConnected(): + broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, + binding_key=key) + + def bindClassKey(self, classKey): + """ Request object updates for a particular table class by class key. """ pname = classKey.getPackageName() cname = classKey.getClassName() - for broker in self.brokers: - broker.amqpSession.exchange_bind(exchange="qpid.management", queue=broker.topicName, - binding_key="console.obj.*.*.%s.%s.#" % (pname, cname)) + self.bindClass(pname, cname) def getAgents(self, broker=None): """ Get a list of currently known agents """ @@ -293,11 +299,16 @@ class Session: agent = kwargs["_agent"] if agent.broker not in brokerList: raise Exception("Supplied agent is not accessible through the supplied broker") - agentList.append(agent) + if agent.broker.isConnected(): + agentList.append(agent) else: for broker in brokerList: for agent in broker.getAgents(): - agentList.append(agent) + if agent.broker.isConnected(): + agentList.append(agent) + + if len(agentList) == 0: + return [] pname = None cname = None @@ -387,10 +398,12 @@ class Session: return keyList def _handleBrokerConnect(self, broker): - pass + if self.console: + self.console.brokerConnected(broker) def _handleBrokerDisconnect(self, broker): - pass + if self.console: + self.console.brokerDisconnected(broker) def _handleBrokerResp(self, broker, codec, seq): broker.brokerId = UUID(codec.read_uuid()) @@ -1127,8 +1140,63 @@ class MethodResult(object): def __repr__(self): return "%s (%d) - %s" % (self.text, self.status, self.outArgs) +class ManagedConnection(Thread): + """ Thread class for managing a connection. """ + DELAY_MIN = 1 + DELAY_MAX = 128 + DELAY_FACTOR = 2 + + def __init__(self, broker): + Thread.__init__(self) + self.broker = broker + self.cv = Condition() + self.canceled = False + + def stop(self): + """ Tell this thread to stop running and return. """ + try: + self.cv.acquire() + self.canceled = True + self.cv.notify() + finally: + self.cv.release() + + def disconnected(self): + """ Notify the thread that the connection was lost. """ + try: + self.cv.acquire() + self.cv.notify() + finally: + self.cv.release() + + def run(self): + """ Main body of the running thread. """ + delay = self.DELAY_MIN + while True: + try: + self.broker._tryToConnect() + try: + self.cv.acquire() + while (not self.canceled) and self.broker.connected: + self.cv.wait() + if self.canceled: + return + delay = self.DELAY_MIN + except: + self.cv.release() + except: + if delay < self.DELAY_MAX: + delay *= self.DELAY_FACTOR + try: + self.cv.acquire() + self.cv.wait(delay) + if self.canceled: + return + finally: + self.cv.release() + class Broker: - """ """ + """ This object represents a connection (or potential connection) to a QMF broker. """ SYNC_TIME = 60 def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): @@ -1138,24 +1206,24 @@ class Broker: self.ssl = ssl self.authUser = authUser self.authPass = authPass - self.agents = {} - self.agents[(1,0)] = Agent(self, 0, "BrokerAgent") - self.topicBound = False self.cv = Condition() - self.syncInFlight = False - self.syncRequest = 0 - self.syncResult = None - self.reqsOutstanding = 1 self.error = None self.brokerId = None - self.isConnected = False + self.connected = False self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) - self._tryToConnect() + if self.session.manageConnections: + self.thread = ManagedConnection(self) + self.thread.start() + else: + self.thread = None + self._tryToConnect() def isConnected(self): - return self.isConnected + """ Return True if there is an active connection to the broker. """ + return self.connected def getError(self): + """ Return the last error message seen while trying to connect to the broker. """ return self.error def getBrokerId(self): @@ -1163,9 +1231,13 @@ class Broker: return self.brokerId def getBrokerBank(self): + """ Return the broker-bank value. This is the value that the broker assigns to + objects within its control. This value appears as a field in the ObjectId + of objects created by agents controlled by this broker. """ return 1 def getAgent(self, brokerBank, agentBank): + """ Return the agent object associated with a particular broker and agent bank value.""" bankKey = (brokerBank, agentBank) if bankKey in self.agents: return self.agents[bankKey] @@ -1199,13 +1271,21 @@ class Broker: return "amqp%s://%s%s:%d" % (ssl, auth, self.host, self.port or 5672) def __repr__(self): - if self.isConnected: + if self.connected: return "Broker connected at: %s" % self.getUrl() else: return "Disconnected Broker" def _tryToConnect(self): try: + self.agents = {} + self.agents[(1,0)] = Agent(self, 0, "BrokerAgent") + self.topicBound = False + self.syncInFlight = False + self.syncRequest = 0 + self.syncResult = None + self.reqsOutstanding = 1 + sock = connect(self.host, self.port) if self.ssl: sock = ssl(sock) @@ -1235,7 +1315,7 @@ class Broker: self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) - self.isConnected = True + self.connected = True self.session._handleBrokerConnect(self) codec = Codec(self.conn.spec) @@ -1245,10 +1325,13 @@ class Broker: except socket.error, e: self.error = "Socket Error %s - %s" % (e[0], e[1]) + raise Exception(self.error) except Closed, e: self.error = "Connect Failed %d - %s" % (e[0], e[1]) + raise Exception(self.error) except ConnectionFailed, e: self.error = "Connect Failed %d - %s" % (e[0], e[1]) + raise Exception(self.error) def _updateAgent(self, obj): bankKey = (obj.brokerBank, obj.agentBank) @@ -1301,19 +1384,22 @@ class Broker: self.amqpSession.message_transfer(destination=dest, message=msg) def _shutdown(self): - if self.isConnected: + if self.thread: + self.thread.stop() + self.thread.join() + if self.connected: self.amqpSession.incoming("rdest").stop() if self.session.console != None: self.amqpSession.incoming("tdest").stop() self.amqpSession.close() self.conn.close() - self.isConnected = False - else: - raise Exception("Broker already disconnected") + self.connected = False def _waitForStable(self): try: self.cv.acquire() + if not self.connected: + return if self.reqsOutstanding == 0: return self.syncInFlight = True @@ -1365,7 +1451,7 @@ class Broker: elif opcode == 'g': self.session._handleContentInd (self, codec, seq, prop=True, stat=True) def _exceptionCb(self, data): - self.isConnected = False + self.connected = False self.error = data try: self.cv.acquire() @@ -1375,6 +1461,8 @@ class Broker: self.cv.release() self.session._handleError(self.error) self.session._handleBrokerDisconnect(self) + if self.thread: + self.thread.disconnected() class Agent: """ """ -- cgit v1.2.1 From 7478d5667ad6fc060854abbec5583e32aac83f8d Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 24 Nov 2008 19:51:03 +0000 Subject: QPID-1484 - qpid-printevents now handles connection loss/reconnect git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@720276 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-printevents | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-printevents b/qpid/python/commands/qpid-printevents index f27eec04f1..0c1b618a1f 100755 --- a/qpid/python/commands/qpid-printevents +++ b/qpid/python/commands/qpid-printevents @@ -23,13 +23,20 @@ import os import optparse import sys import socket -import time +from time import time, strftime, gmtime, sleep from qmf.console import Console, Session class EventConsole(Console): def event(self, broker, event): print event + def brokerConnected(self, broker): + print strftime("%c", gmtime(time())), "NOTIC qpid-printevents:brokerConnected broker=%s" % broker.getUrl() + + def brokerDisconnected(self, broker): + print strftime("%c", gmtime(time())), "NOTIC qpid-printevents:brokerDisconnected broker=%s" % broker.getUrl() + + ## ## Main Program ## @@ -48,14 +55,17 @@ ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost arguments.append("localhost") console = EventConsole() - session = Session(console, rcvObjects=False, rcvHeartbeats=False) + session = Session(console, rcvObjects=False, rcvHeartbeats=False, manageConnections=True) + brokers = [] for host in arguments: - session.addBroker(host) + brokers.append(session.addBroker(host)) try: while (True): - time.sleep(10) + sleep(10) except KeyboardInterrupt: + for broker in brokers: + session.delBroker(broker) print sys.exit(0) -- cgit v1.2.1 From de7f1a46af35818b5c96e6c87386606b94ad02f6 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 1 Dec 2008 16:38:54 +0000 Subject: Fixed a performance regression caused by the management code. The current/last-change timestamp is now set on management objects when they are published, not when they actually change. Also, the timestamp is updated only if modifications were made to the object in the last publish interval. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722120 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 58cc46ae6f..9f5f621ba0 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -558,7 +558,7 @@ class Session: self.cv.release() object = Object(self, broker, schema, codec, prop, stat) - if pname == "org.apache.qpid.broker" and classKey.getClassName() == "agent": + if pname == "org.apache.qpid.broker" and classKey.getClassName() == "agent" and prop: broker._updateAgent(object) try: -- cgit v1.2.1 From b3646fe3a1a64cc0c0f920c52677519bbd0fae92 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Mon, 1 Dec 2008 17:01:41 +0000 Subject: removed incubator notice git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722128 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/DISCLAIMER | 5 ----- qpid/python/RELEASE_NOTES | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 qpid/python/DISCLAIMER (limited to 'qpid/python') diff --git a/qpid/python/DISCLAIMER b/qpid/python/DISCLAIMER deleted file mode 100644 index c321113c9e..0000000000 --- a/qpid/python/DISCLAIMER +++ /dev/null @@ -1,5 +0,0 @@ -Apache Qpid is an effort undergoing incubation at the Apache Software Foundation (ASF), sponsored by the Apache Incubator PMC. - -Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. - -While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF. diff --git a/qpid/python/RELEASE_NOTES b/qpid/python/RELEASE_NOTES index e92e8c20b9..96f92a2d90 100644 --- a/qpid/python/RELEASE_NOTES +++ b/qpid/python/RELEASE_NOTES @@ -1,5 +1,5 @@ -Apache Incubator Qpid Python M4 Release Notes -------------------------------------------- +Apache Python M4 Release Notes +------------------------------ The Qpid M4 release of the python client contains support the for both 0-8 and 0-10 of the AMQP specification as well as support for the -- cgit v1.2.1 From 5d7cb01ea4c2fc6a275dfa30122c78019290add5 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 1 Dec 2008 18:39:29 +0000 Subject: Fixed regression caused by a change to the qmf.console API. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722167 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-queue-stats | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 5aa32f9b38..3c727fddfd 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -29,7 +29,6 @@ from threading import Condition from qmf.console import Session, Console from qpid.peer import Closed from qpid.connection import Connection, ConnectionFailed -from qpid.util import connect from time import sleep class BrokerManager(Console): @@ -54,7 +53,7 @@ class BrokerManager(Console): self.filter = filter def objectProps(self, broker, record): - className = record.getClassKey()[1] + className = record.getClassKey().getClassName() if className != "queue": return @@ -63,7 +62,7 @@ class BrokerManager(Console): self.objects[id] = (record.name, None, None) def objectStats(self, broker, record): - className = record.getClassKey()[1] + className = record.getClassKey().getClassName() if className != "queue": return @@ -103,11 +102,7 @@ class BrokerManager(Console): def Display (self): - classes = self.session.getClasses("org.apache.qpid.broker") - for cls in classes: - if cls[1] == "queue": - queueClassKey = cls - self.session.bindClass(queueClassKey) + self.session.bindClass("org.apache.qpid.broker", "queue") print "Queue Name Sec Depth Enq Rate Deq Rate" print "========================================================================================" try: -- cgit v1.2.1 From 77fc3858671839e143f5617c292ee0f407e155ac Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 1 Dec 2008 19:29:54 +0000 Subject: Replaced nonexistent type "double" with "float". In python, float represents a double-precision floating point number. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722191 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 2 +- qpid/python/qpid/management.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 9f5f621ba0..827a28d891 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -630,7 +630,7 @@ class Session: elif typecode == 10: value.encode (codec) # REF elif typecode == 11: codec.write_uint8 (int(value)) # BOOL elif typecode == 12: codec.write_float (float(value)) # FLOAT - elif typecode == 13: codec.write_double (double(value)) # DOUBLE + elif typecode == 13: codec.write_double (float(value)) # DOUBLE elif typecode == 14: codec.write_uuid (value.bytes) # UUID elif typecode == 15: codec.write_map (value) # FTABLE elif typecode == 16: codec.write_int8 (int(value)) # S8 diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 4a87d2d533..477f3e8f2b 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -450,7 +450,7 @@ class managementClient: elif typecode == 12: # FLOAT codec.write_float (float (value)) elif typecode == 13: # DOUBLE - codec.write_double (double (value)) + codec.write_double (float (value)) elif typecode == 14: # UUID codec.write_uuid (value) elif typecode == 15: # FTABLE -- cgit v1.2.1 From 645a5a84eb3e784e1c75d306b89c6001e6e90cd7 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 1 Dec 2008 21:03:39 +0000 Subject: Fixed a logic bug in a try/finally git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722229 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 827a28d891..6b0826f37d 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -1182,7 +1182,7 @@ class ManagedConnection(Thread): if self.canceled: return delay = self.DELAY_MIN - except: + finally: self.cv.release() except: if delay < self.DELAY_MAX: -- cgit v1.2.1 From 109a1026f6c05c6a857dc9f09bf19f1986b6c92c Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 1 Dec 2008 21:04:39 +0000 Subject: Added handling of connection loss with connection retry. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722230 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-queue-stats | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index 3c727fddfd..e8b9e4c36a 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -36,22 +36,24 @@ class BrokerManager(Console): self.url = host self.objects = {} self.filter = None - self.session = Session(self, rcvEvents=False, rcvHeartbeats=False, userBindings=True) - try: - self.broker = self.session.addBroker(self.url) - except socket.error, e: - print "Socket Error %s - %s" % (e[0], e[1]) - sys.exit (1) - except Closed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit (1) - except ConnectionFailed, e: - print "Connect Failed %d - %s" % (e[0], e[1]) - sys.exit(1) + self.session = Session(self, rcvEvents=False, rcvHeartbeats=False, + userBindings=True, manageConnections=True) + self.broker = self.session.addBroker(self.url) + self.firstError = True def setFilter(self,filter): self.filter = filter + def brokerConnected(self, broker): + if not self.firstError: + print "*** Broker connected" + self.firstError = False + + def brokerDisconnected(self, broker): + print "*** Broker connection lost - %s, retrying..." % broker.getError() + self.firstError = False + self.objects.clear() + def objectProps(self, broker, record): className = record.getClassKey().getClassName() if className != "queue": @@ -108,6 +110,9 @@ class BrokerManager(Console): try: while True: sleep (1) + if self.firstError and self.broker.getError(): + self.firstError = False + print "*** Error: %s, retrying..." % self.broker.getError() except KeyboardInterrupt: print self.session.delBroker(self.broker) -- cgit v1.2.1 From 9e63f6094e8e634341559ac680035062248869fe Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 1 Dec 2008 22:53:46 +0000 Subject: Gets of specific object-ids should only involve the agent(s) that own the object id. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722272 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 6b0826f37d..fe329db289 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -302,10 +302,17 @@ class Session: if agent.broker.isConnected(): agentList.append(agent) else: - for broker in brokerList: - for agent in broker.getAgents(): - if agent.broker.isConnected(): - agentList.append(agent) + if "_objectId" in kwargs: + oid = kwargs["_objectId"] + for broker in brokerList: + for agent in broker.getAgents(): + if agent.getBrokerBank() == oid.getBrokerBank() and agent.getAgentBank() == oid.getAgentBank(): + agentList.append(agent) + else: + for broker in brokerList: + for agent in broker.getAgents(): + if agent.broker.isConnected(): + agentList.append(agent) if len(agentList) == 0: return [] -- cgit v1.2.1 From 8f2be96183d93310a0c31b70404dc00c10ed4680 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 2 Dec 2008 18:35:41 +0000 Subject: QPID-1419: remove wip support from 0-9 python client git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@722557 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection08.py | 6 ++++++ qpid/python/qpid/peer.py | 6 +----- qpid/python/qpid/testlib.py | 18 ++++++++++-------- 3 files changed, 17 insertions(+), 13 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection08.py b/qpid/python/qpid/connection08.py index 8f2eef4770..be94a792cb 100644 --- a/qpid/python/qpid/connection08.py +++ b/qpid/python/qpid/connection08.py @@ -122,6 +122,12 @@ class Connection: raise "frame error: expected %r, got %r" % (self.FRAME_END, garbage) return frame + def write_0_9(self, frame): + self.write_8_0(frame) + + def read_0_9(self): + return self.read_8_0() + def write_0_10(self, frame): c = self.codec flags = 0 diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 0932efeab3..648f32ceef 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -193,11 +193,7 @@ class Channel: self.futures = {} self.control_queue = Queue(0)#used for incoming methods that appas may want to handle themselves - # Use reliable framing if version == 0-9. - if spec.major == 0 and spec.minor == 9: - self.invoker = self.invoke_reliable - else: - self.invoker = self.invoke_method + self.invoker = self.invoke_method self.use_execution_layer = (spec.major == 0 and spec.minor == 10) or (spec.major == 99 and spec.minor == 0) self.synchronous = True diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 18ef030ec0..bed0d10198 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -108,6 +108,10 @@ Options: # NB: AMQP 0-8 identifies itself as 8-0 for historical reasons. return self.spec.major==8 and self.spec.minor==0 + def use09spec(self): + "True if we are running with the 0-9 (non-wip) spec." + return self.spec.major==0 and self.spec.minor==9 + def _parseargs(self, args): # Defaults self.setBroker("localhost") @@ -157,14 +161,12 @@ Options: if len(self.tests) == 0: if not self.skip_self_test: self.tests=findmodules("tests") - if self.use08spec(): + if self.use08spec() or self.use09spec(): self.tests+=findmodules("tests_0-8") elif (self.spec.major == 99 and self.spec.minor == 0): self.tests+=findmodules("tests_0-10_preview") elif (self.spec.major == 0 and self.spec.minor == 10): self.tests+=findmodules("tests_0-10") - else: - self.tests+=findmodules("tests_0-9") def testSuite(self): class IgnoringTestSuite(unittest.TestSuite): @@ -232,7 +234,7 @@ class TestBase(unittest.TestCase): self.client = self.connect() self.channel = self.client.channel(1) self.version = (self.client.spec.major, self.client.spec.minor) - if self.version == (8, 0): + if self.version == (8, 0) or self.version == (0, 9): self.channel.channel_open() else: self.channel.session_open() @@ -278,7 +280,7 @@ class TestBase(unittest.TestCase): def consume(self, queueName): """Consume from named queue returns the Queue object.""" - if testrunner.use08spec(): + if testrunner.use08spec() or testrunner.use09spec(): reply = self.channel.basic_consume(queue=queueName, no_ack=True) return self.client.queue(reply.consumer_tag) else: @@ -309,7 +311,7 @@ class TestBase(unittest.TestCase): Publish to exchange and assert queue.get() returns the same message. """ body = self.uniqueString() - if testrunner.use08spec(): + if testrunner.use08spec() or testrunner.use09spec(): self.channel.basic_publish( exchange=exchange, content=Content(body, properties=properties), @@ -319,7 +321,7 @@ class TestBase(unittest.TestCase): destination=exchange, content=Content(body, properties={'application_headers':properties,'routing_key':routing_key})) msg = queue.get(timeout=1) - if testrunner.use08spec(): + if testrunner.use08spec() or testrunner.use09spec(): self.assertEqual(body, msg.content.body) if (properties): self.assertEqual(properties, msg.content.properties) @@ -336,7 +338,7 @@ class TestBase(unittest.TestCase): self.assertPublishGet(self.consume(queue), exchange, routing_key, properties) def assertChannelException(self, expectedCode, message): - if self.version == (8, 0): #or "transitional" in self.client.spec.file: + if self.version == (8, 0) or self.version == (0, 9): if not isinstance(message, Message): self.fail("expected channel_close method, got %s" % (message)) self.assertEqual("channel", message.method.klass.name) self.assertEqual("close", message.method.name) -- cgit v1.2.1 From ceb2e4419fe081d096bacda9966adc705bf3dc73 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 3 Dec 2008 18:43:16 +0000 Subject: QPID-1512 - Catch only connection-related exceptions in qmf console connection thread - Added __hash__ method for UUID in qpid.datatypes git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@723008 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 2 +- qpid/python/commands/qpid-route | 2 +- qpid/python/qmf/console.py | 15 +++++++++++---- qpid/python/qpid/datatypes.py | 3 +++ 4 files changed, 16 insertions(+), 6 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 6bfa6939f1..beec67c826 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -379,7 +379,7 @@ try: else: Usage () except Exception,e: - print "Failed:", e.args[0] + print "Failed:", e.args sys.exit(1) bm.Disconnect() diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index 5733c62a27..e0e655683a 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -508,7 +508,7 @@ try: Usage() except Exception,e: - print "Failed:", e.args[0] + print "Failed:", e.args sys.exit(1) rm.disconnect() diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index fe329db289..739ccb6b6b 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -1191,9 +1191,16 @@ class ManagedConnection(Thread): delay = self.DELAY_MIN finally: self.cv.release() - except: + except socket.error: + if delay < self.DELAY_MAX: + delay *= self.DELAY_FACTOR + except SessionDetached: if delay < self.DELAY_MAX: delay *= self.DELAY_FACTOR + except Closed: + if delay < self.DELAY_MAX: + delay *= self.DELAY_FACTOR + try: self.cv.acquire() self.cv.wait(delay) @@ -1332,13 +1339,13 @@ class Broker: except socket.error, e: self.error = "Socket Error %s - %s" % (e[0], e[1]) - raise Exception(self.error) + raise except Closed, e: self.error = "Connect Failed %d - %s" % (e[0], e[1]) - raise Exception(self.error) + raise except ConnectionFailed, e: self.error = "Connect Failed %d - %s" % (e[0], e[1]) - raise Exception(self.error) + raise def _updateAgent(self, obj): bankKey = (obj.brokerBank, obj.agentBank) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 38fc163dd9..eb1f86b0b0 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -297,6 +297,9 @@ class UUID: def __repr__(self): return "UUID(%r)" % str(self) + def __hash__(self): + return self.bytes.__hash__() + class timestamp(float): def __new__(cls, obj=None): -- cgit v1.2.1 From 2897451bcf74c5a593e1d51f64fb1cb751b3060c Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 3 Dec 2008 20:42:38 +0000 Subject: Avoid a divide-by-zero that can occur if updates are sent with a timestamp equal to the last update. This can occur when running qpid-queue-stats and then running qpid-tool on the same broker. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@723063 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-queue-stats | 2 ++ 1 file changed, 2 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-queue-stats b/qpid/python/commands/qpid-queue-stats index e8b9e4c36a..356a1d2d8d 100755 --- a/qpid/python/commands/qpid-queue-stats +++ b/qpid/python/commands/qpid-queue-stats @@ -95,6 +95,8 @@ class BrokerManager(Console): self.objects[id] = (name, first, record) deltaTime = float (record.getTimestamps()[0] - lastSample.getTimestamps()[0]) + if deltaTime < 1000000000.0: + return enqueueRate = float (record.msgTotalEnqueues - lastSample.msgTotalEnqueues) / \ (deltaTime / 1000000000.0) dequeueRate = float (record.msgTotalDequeues - lastSample.msgTotalDequeues) / \ -- cgit v1.2.1 From 839d13cc613be929fdc01aecb62f0666c91cb3e5 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Sat, 6 Dec 2008 16:17:26 +0000 Subject: QPID-691: fixed a typo in getopt args git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724003 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/testlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index bed0d10198..31f52169ae 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -123,7 +123,7 @@ Options: try: opts, self.tests = getopt(args, "s:e:b:h?dvSi:I:F:", - ["help", "spec", "errata=", "server", + ["help", "spec", "errata=", "broker=", "verbose", "skip-self-test", "ignore", "ignore-file", "spec-folder"]) except GetoptError, e: -- cgit v1.2.1 From 776aa03e340e3cb753bd56dea363caa44b866640 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 9 Dec 2008 21:24:51 +0000 Subject: modified hello-world to optionally take a broker as an argument git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724898 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/hello-world | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/hello-world b/qpid/python/hello-world index 5d312e7371..efee84059c 100755 --- a/qpid/python/hello-world +++ b/qpid/python/hello-world @@ -17,12 +17,23 @@ # specific language governing permissions and limitations # under the License. # +import sys from qpid.connection import Connection from qpid.util import connect from qpid.datatypes import uuid4, Message +broker = "127.0.0.1" +port = 5672 + +if len(sys.argv) > 1: broker = sys.argv[1] +if len(sys.argv) > 2: port = int(sys.argv[2]) + +if len(sys.argv) > 3: + print >> sys.stderr, "usage: hello-world [ [ ] ]" + sys.exit(1) + # connect to the server and start a session -conn = Connection(connect("127.0.0.1", 5672)) +conn = Connection(connect(broker, port)) conn.start() ssn = conn.session(str(uuid4())) -- cgit v1.2.1 From e9e411c10d4472b18608725cfdd848564f5f8ae3 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 9 Dec 2008 21:25:03 +0000 Subject: updated README git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724899 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/README.txt | 62 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 19 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/README.txt b/qpid/python/README.txt index e7bb5af408..ba3cbfea92 100644 --- a/qpid/python/README.txt +++ b/qpid/python/README.txt @@ -1,32 +1,56 @@ -= RUNNING THE PYTHON TESTS = += INSTALLATION = -The tests/ directory contains a collection of python unit tests to -exercise functions of a broker. +Extract the release archive into a directory of your choice and set +your PYTHONPATH accordingly: + + tar -xzf qpid-python-.tar.gz -C + export PYTHONPATH=/qpid-/python + += GETTING STARTED = + +The python client includes a simple hello-world example that publishes +and consumes a message: + + cp /qpid-/python/hello-world . + ./hello-world + += EXAMPLES = + +More comprehensive examples can be found here: + + cd /qpid-/python/examples + += RUNNING THE TESTS = + +The "tests" directory contains a collection of unit tests for the +python client. The "tests_0-10", "tests_0-9", and "tests_0-8" +directories contain protocol level conformance tests for AMQP brokers +of the specified version. Simplest way to run the tests: - * Run a broker on the default port + 1. Run a broker on the default port - * ./run-tests + 2. ./run-tests -s -For additional options: ./run-tests --help +Where is one of "0-8", "0-9", or "0-10-errata". +See the run-tests usage for for additional options: -== Expected failures == + ./run-tests -h -Until we complete functionality, tests may fail because the tested -functionality is missing in the broker. To skip expected failures -in the C++ or Java brokers: +== Expected failures == - ./run-tests -I +Certain tests are expected to fail to do incomplete functionality or +unresolved interop issues. To skip expected failures for the C++ or +Java brokers: -=== File List === + ./run-tests -I -1. cpp_failing_0-10.txt -2. cpp_failing_0-9.txt -3. cpp_failing_0-8.txt -4. java_failing_0-9.txt -5. java_failing_0-8.txt -6. cpp_failing_0-10_preview.txt -- will be depricated soon. +Where is one of the following files: -If you fix a failure, please remove it from the corresponding list. + * cpp_failing_0-10.txt + * cpp_failing_0-9.txt + * cpp_failing_0-8.txt + * java_failing_0-9.txt + * java_failing_0-8.txt -- cgit v1.2.1 From b8d419092b87652996ab341eff482bff32c2f386 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 9 Dec 2008 21:28:46 +0000 Subject: corrected release notes git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724900 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/RELEASE_NOTES | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/RELEASE_NOTES b/qpid/python/RELEASE_NOTES index 96f92a2d90..c0903df38e 100644 --- a/qpid/python/RELEASE_NOTES +++ b/qpid/python/RELEASE_NOTES @@ -4,7 +4,7 @@ Apache Python M4 Release Notes The Qpid M4 release of the python client contains support the for both 0-8 and 0-10 of the AMQP specification as well as support for the non-WIP portion of the 0-9 specification. You can access these -specficiations from: +specifications from: http://jira.amqp.org/confluence/display/AMQP/Download @@ -13,6 +13,5 @@ project page at: http://cwiki.apache.org/confluence/display/qpid/Index -The README file provided contains some details on running the python -based test suite that is included with this distro. - +The README file provided contains some details on installing and using +the python client that is included with this distribution. -- cgit v1.2.1 From 49abcbb5c4b9fdba6bb359575f760ed075b73d29 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 9 Dec 2008 22:00:23 +0000 Subject: applied patch from jross to fix some typos git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@724909 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/README.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/README.txt b/qpid/python/README.txt index ba3cbfea92..bae9f6ab0b 100644 --- a/qpid/python/README.txt +++ b/qpid/python/README.txt @@ -41,13 +41,13 @@ See the run-tests usage for for additional options: == Expected failures == -Certain tests are expected to fail to do incomplete functionality or +Certain tests are expected to fail due to incomplete functionality or unresolved interop issues. To skip expected failures for the C++ or Java brokers: - ./run-tests -I + ./run-tests -I -Where is one of the following files: +Where is one of the following files: * cpp_failing_0-10.txt * cpp_failing_0-9.txt -- cgit v1.2.1 From d50fa64fa49b18ae284a1963233849c106683c58 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 10 Dec 2008 23:23:24 +0000 Subject: QPID-1526: add checks for exclusive ownership plus tests to verify they are executed git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@725483 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/queue.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index a3b23a1c32..05e18081fa 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -88,7 +88,7 @@ class QueueTests(TestBase010): # TestBase.setUp has already opened session(1) s1 = self.session # Here we open a second separate connection: - s2 = self.conn.session("other", 2) + s2 = self.conn.session("other") #declare an exclusive queue: s1.queue_declare(queue="exclusive-queue", exclusive=True, auto_delete=True) @@ -98,6 +98,22 @@ class QueueTests(TestBase010): self.fail("Expected second exclusive queue_declare to raise a channel exception") except SessionException, e: self.assertEquals(405, e.args[0].error_code) + + s3 = self.conn.session("subscriber") + try: + #other connection should not be allowed to declare this: + s3.message_subscribe(queue="exclusive-queue") + self.fail("Expected message_subscribe on an exclusive queue to raise a channel exception") + except SessionException, e: + self.assertEquals(405, e.args[0].error_code) + + s4 = self.conn.session("deleter") + try: + #other connection should not be allowed to declare this: + s4.queue_delete(queue="exclusive-queue") + self.fail("Expected queue_delete on an exclusive queue to raise a channel exception") + except SessionException, e: + self.assertEquals(405, e.args[0].error_code) def test_declare_passive(self): -- cgit v1.2.1 From 4d3a365d2c7fc3326536818e3d33c4dd7df247df Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 15 Dec 2008 18:09:59 +0000 Subject: Suppress spurious object updates when "rcvObjects" is false. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@726750 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 739ccb6b6b..bba0386d6a 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -577,7 +577,7 @@ class Session: finally: self.cv.release() - if self.console != None: + if self.console and self.rcvObjects: if prop: self.console.objectProps(broker, object) if stat: -- cgit v1.2.1 From fa2afe4e0015cb48856525b1385ecaee638d045d Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Mon, 15 Dec 2008 18:12:25 +0000 Subject: Added license headers to the following files. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@726752 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/doc/test-requirements.txt | 19 +++++++++++++++++++ qpid/python/examples/direct/verify | 19 +++++++++++++++++++ qpid/python/examples/fanout/verify | 19 +++++++++++++++++++ qpid/python/examples/pubsub/verify | 19 +++++++++++++++++++ qpid/python/examples/request-response/verify | 19 +++++++++++++++++++ qpid/python/examples/xml-exchange/verify | 19 +++++++++++++++++++ qpid/python/models/fedsim/__init__.py | 18 ++++++++++++++++++ 7 files changed, 132 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/doc/test-requirements.txt b/qpid/python/doc/test-requirements.txt index a1ba414eb2..5089b49dbe 100644 --- a/qpid/python/doc/test-requirements.txt +++ b/qpid/python/doc/test-requirements.txt @@ -1,3 +1,22 @@ +############################################################################### +# 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. +############################################################################### + * start and stop server, possibly in different configurations, should at least be able to specify host and port diff --git a/qpid/python/examples/direct/verify b/qpid/python/examples/direct/verify index 01d81a18a1..92f87bf827 100644 --- a/qpid/python/examples/direct/verify +++ b/qpid/python/examples/direct/verify @@ -1,3 +1,22 @@ +# +# 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. +# + # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify clients ./declare_queues.py ./direct_producer.py ./direct_consumer.py outputs ./declare_queues.py.out ./direct_producer.py.out ./direct_consumer.py.out diff --git a/qpid/python/examples/fanout/verify b/qpid/python/examples/fanout/verify index 6a3132a94f..9e5c364bfa 100644 --- a/qpid/python/examples/fanout/verify +++ b/qpid/python/examples/fanout/verify @@ -1,3 +1,22 @@ +# +# 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. +# + # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify background "Subscribed" ./fanout_consumer.py background "Subscribed" ./fanout_consumer.py diff --git a/qpid/python/examples/pubsub/verify b/qpid/python/examples/pubsub/verify index 963d2e32e1..cf1bade62e 100644 --- a/qpid/python/examples/pubsub/verify +++ b/qpid/python/examples/pubsub/verify @@ -1,3 +1,22 @@ +# +# 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. +# + # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify background "Queues created" ./topic_subscriber.py clients ./topic_publisher.py diff --git a/qpid/python/examples/request-response/verify b/qpid/python/examples/request-response/verify index cf8151d4e4..3c058febb2 100644 --- a/qpid/python/examples/request-response/verify +++ b/qpid/python/examples/request-response/verify @@ -1,3 +1,22 @@ +# +# 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. +# + # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify background "Request server running" ./server.py clients ./client.py diff --git a/qpid/python/examples/xml-exchange/verify b/qpid/python/examples/xml-exchange/verify index bf05463f1d..a93a32dc90 100644 --- a/qpid/python/examples/xml-exchange/verify +++ b/qpid/python/examples/xml-exchange/verify @@ -1,3 +1,22 @@ +# +# 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. +# + # See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify clients ./declare_queues.py ./xml_producer.py ./xml_consumer.py outputs ./declare_queues.py.out ./xml_producer.py.out ./xml_consumer.py.out diff --git a/qpid/python/models/fedsim/__init__.py b/qpid/python/models/fedsim/__init__.py index 792d600548..63a3f41f28 100644 --- a/qpid/python/models/fedsim/__init__.py +++ b/qpid/python/models/fedsim/__init__.py @@ -1 +1,19 @@ # +# 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. +# + -- cgit v1.2.1 From 2bfa65fcea4fee81620a7325fc75a347d013bd72 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 16 Dec 2008 18:29:38 +0000 Subject: Fixed problems with object representation functions. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@727109 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index bba0386d6a..0009726fe7 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -1022,7 +1022,7 @@ class Object(object): if result != u"": result += u":" try: - valstr = unicode(value) + valstr = unicode(self._session._displayValue(value, property.type)) except: valstr = u"" result += valstr @@ -1044,7 +1044,9 @@ class Object(object): self.statistics = newer.getStatistics() def __repr__(self): - return self.getIndex().encode("utf8") + key = self.getClassKey() + return key.getPackageName() + ":" + key.getClassName() +\ + "[" + self.getObjectId().__repr__() + "] " + self.getIndex().encode("utf8") def __getattr__(self, name): for method in self._schema.getMethods(): @@ -1607,10 +1609,10 @@ class DebugConsole(Console): print "delAgent:", agent def objectProps(self, broker, record): - print "objectProps:", record.getClassKey() + print "objectProps:", record def objectStats(self, broker, record): - print "objectStats:", record.getClassKey() + print "objectStats:", record def event(self, broker, event): print "event:", event -- cgit v1.2.1 From cab5db31674b25b3c70b2ac5ed4ea97ab413f932 Mon Sep 17 00:00:00 2001 From: Jonathan Robie Date: Tue, 16 Dec 2008 18:50:55 +0000 Subject: Modified Connect() constructor throughout. User name and password are now supplied for the connection. (Someone changed this, and it no longer matched the original tutorial.) git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@727116 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/direct/declare_queues.py | 2 +- qpid/python/examples/direct/direct_consumer.py | 2 +- qpid/python/examples/direct/direct_producer.py | 2 +- qpid/python/examples/direct/listener.py | 2 +- qpid/python/examples/fanout/fanout_consumer.py | 2 +- qpid/python/examples/fanout/fanout_producer.py | 2 +- qpid/python/examples/fanout/listener.py | 2 +- qpid/python/examples/pubsub/topic_subscriber.py | 2 +- qpid/python/examples/request-response/client.py | 2 +- qpid/python/examples/request-response/server.py | 2 +- qpid/python/examples/xml-exchange/declare_queues.py | 2 +- qpid/python/examples/xml-exchange/listener.py | 2 +- qpid/python/examples/xml-exchange/xml_consumer.py | 2 +- qpid/python/examples/xml-exchange/xml_producer.py | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/examples/direct/declare_queues.py b/qpid/python/examples/direct/declare_queues.py index 3cc4a10c0b..13818ee9d7 100755 --- a/qpid/python/examples/direct/declare_queues.py +++ b/qpid/python/examples/direct/declare_queues.py @@ -54,7 +54,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/direct/direct_consumer.py b/qpid/python/examples/direct/direct_consumer.py index 6e30541d6c..b07e53c5c7 100755 --- a/qpid/python/examples/direct/direct_consumer.py +++ b/qpid/python/examples/direct/direct_consumer.py @@ -52,7 +52,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/direct/direct_producer.py b/qpid/python/examples/direct/direct_producer.py index 984d112243..fcbb4675e4 100755 --- a/qpid/python/examples/direct/direct_producer.py +++ b/qpid/python/examples/direct/direct_producer.py @@ -52,7 +52,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/direct/listener.py b/qpid/python/examples/direct/listener.py index d1c4b1d645..9d06bd3929 100755 --- a/qpid/python/examples/direct/listener.py +++ b/qpid/python/examples/direct/listener.py @@ -73,7 +73,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/fanout/fanout_consumer.py b/qpid/python/examples/fanout/fanout_consumer.py index d2a6f5242f..0452baa8da 100755 --- a/qpid/python/examples/fanout/fanout_consumer.py +++ b/qpid/python/examples/fanout/fanout_consumer.py @@ -50,7 +50,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/fanout/fanout_producer.py b/qpid/python/examples/fanout/fanout_producer.py index 4d24a460d1..c4df252c70 100755 --- a/qpid/python/examples/fanout/fanout_producer.py +++ b/qpid/python/examples/fanout/fanout_producer.py @@ -49,7 +49,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/fanout/listener.py b/qpid/python/examples/fanout/listener.py index 9863357a41..29db402e9d 100755 --- a/qpid/python/examples/fanout/listener.py +++ b/qpid/python/examples/fanout/listener.py @@ -70,7 +70,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/pubsub/topic_subscriber.py b/qpid/python/examples/pubsub/topic_subscriber.py index 7a886974ee..489c7cbb19 100755 --- a/qpid/python/examples/pubsub/topic_subscriber.py +++ b/qpid/python/examples/pubsub/topic_subscriber.py @@ -81,7 +81,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/request-response/client.py b/qpid/python/examples/request-response/client.py index 563e4f2992..b29fcf3ea7 100755 --- a/qpid/python/examples/request-response/client.py +++ b/qpid/python/examples/request-response/client.py @@ -73,7 +73,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/request-response/server.py b/qpid/python/examples/request-response/server.py index 7300ced03a..a80c4541e4 100755 --- a/qpid/python/examples/request-response/server.py +++ b/qpid/python/examples/request-response/server.py @@ -64,7 +64,7 @@ if len(sys.argv) > 2 : port=int(sys.argv[2]) socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/declare_queues.py b/qpid/python/examples/xml-exchange/declare_queues.py index d8c2d0c34f..ca40af5dc5 100755 --- a/qpid/python/examples/xml-exchange/declare_queues.py +++ b/qpid/python/examples/xml-exchange/declare_queues.py @@ -53,7 +53,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/listener.py b/qpid/python/examples/xml-exchange/listener.py index c6fa4ec57d..a56f5d6018 100755 --- a/qpid/python/examples/xml-exchange/listener.py +++ b/qpid/python/examples/xml-exchange/listener.py @@ -70,7 +70,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/xml_consumer.py b/qpid/python/examples/xml-exchange/xml_consumer.py index e414d98991..cd89110b05 100755 --- a/qpid/python/examples/xml-exchange/xml_consumer.py +++ b/qpid/python/examples/xml-exchange/xml_consumer.py @@ -52,7 +52,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) diff --git a/qpid/python/examples/xml-exchange/xml_producer.py b/qpid/python/examples/xml-exchange/xml_producer.py index 2641b185c6..fa97cab4e1 100755 --- a/qpid/python/examples/xml-exchange/xml_producer.py +++ b/qpid/python/examples/xml-exchange/xml_producer.py @@ -70,7 +70,7 @@ if len(sys.argv) > 2 : # Create a connection. socket = connect(host, port) -connection = Connection (sock=socket) +connection = Connection (sock=socket, username=user, password=password) connection.start() session = connection.session(str(uuid4())) -- cgit v1.2.1 From 5b8b6553e5e5d8cc64d74ee359ba42c755f3755e Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 17 Dec 2008 18:36:47 +0000 Subject: Restrict queries to the broker-resident agent only. This optimization prevents long timeouts caused by non-responsive agents on the broker. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@727462 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index beec67c826..1b7b5171ed 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -95,13 +95,17 @@ class BrokerManager: self.url = brokerUrl self.qmf = Session() self.broker = self.qmf.addBroker(brokerUrl) + agents = self.qmf.getAgents() + for a in agents: + if a.getAgentBank() == 0: + self.brokerAgent = a def Disconnect(self): self.qmf.delBroker(self.broker) def Overview (self): - exchanges = self.qmf.getObjects(_class="exchange") - queues = self.qmf.getObjects(_class="queue") + exchanges = self.qmf.getObjects(_class="exchange", _agent=self.brokerAgent) + queues = self.qmf.getObjects(_class="queue", _agent=self.brokerAgent) print "Total Exchanges: %d" % len (exchanges) etype = {} for ex in exchanges: @@ -122,7 +126,7 @@ class BrokerManager: print " non-durable: %d" % (len (queues) - _durable) def ExchangeList (self, filter): - exchanges = self.qmf.getObjects(_class="exchange") + exchanges = self.qmf.getObjects(_class="exchange", _agent=self.brokerAgent) caption1 = "Type " caption2 = "Exchange Name" maxNameLen = len(caption2) @@ -145,9 +149,9 @@ class BrokerManager: print def ExchangeListRecurse (self, filter): - exchanges = self.qmf.getObjects(_class="exchange") - bindings = self.qmf.getObjects(_class="binding") - queues = self.qmf.getObjects(_class="queue") + exchanges = self.qmf.getObjects(_class="exchange", _agent=self.brokerAgent) + bindings = self.qmf.getObjects(_class="binding", _agent=self.brokerAgent) + queues = self.qmf.getObjects(_class="queue", _agent=self.brokerAgent) for ex in exchanges: if self.match (ex.name, filter): print "Exchange '%s' (%s)" % (ex.name, ex.type) @@ -161,7 +165,7 @@ class BrokerManager: def QueueList (self, filter): - queues = self.qmf.getObjects(_class="queue") + queues = self.qmf.getObjects(_class="queue", _agent=self.brokerAgent) caption = "Queue Name" maxNameLen = len(caption) @@ -191,9 +195,9 @@ class BrokerManager: print def QueueListRecurse (self, filter): - exchanges = self.qmf.getObjects(_class="exchange") - bindings = self.qmf.getObjects(_class="binding") - queues = self.qmf.getObjects(_class="queue") + exchanges = self.qmf.getObjects(_class="exchange", _agent=self.brokerAgent) + bindings = self.qmf.getObjects(_class="binding", _agent=self.brokerAgent) + queues = self.qmf.getObjects(_class="queue", _agent=self.brokerAgent) for queue in queues: if self.match (queue.name, filter): print "Queue '%s'" % queue.name @@ -378,8 +382,10 @@ try: bm.Unbind (cargs[1:]) else: Usage () +except KeyboardInterrupt: + print except Exception,e: print "Failed:", e.args sys.exit(1) - -bm.Disconnect() +finally: + bm.Disconnect() -- cgit v1.2.1 From f371986c9814181aece149342c105ab66b15b444 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 8 Jan 2009 17:10:20 +0000 Subject: made codegen happen on module import rather than on object instantiation, this makes things like help(Session) much more useful git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@732760 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 23 +++++----------- qpid/python/qpid/generator.py | 60 ++++++++++++++++++++++++++++++++++++++++++ qpid/python/qpid/invoker.py | 48 --------------------------------- qpid/python/qpid/session.py | 23 +++------------- qpid/python/qpid/spec.py | 2 ++ 5 files changed, 72 insertions(+), 84 deletions(-) create mode 100644 qpid/python/qpid/generator.py delete mode 100644 qpid/python/qpid/invoker.py (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 4c9c02822a..6665e3e40c 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -23,9 +23,8 @@ from util import wait, notify from assembler import Assembler, Segment from codec010 import StringCodec from session import Session -from invoker import Invoker -from spec010 import Control, Command, load -from spec import default +from generator import control_invoker +from spec import SPEC from exceptions import * from logging import getLogger import delegates @@ -63,12 +62,9 @@ def sslwrap(sock): class Connection(Assembler): - def __init__(self, sock, spec=None, delegate=client, **args): + def __init__(self, sock, spec=SPEC, delegate=client, **args): Assembler.__init__(self, sslwrap(sock)) - if spec == None: - spec = load(default()) self.spec = spec - self.track = self.spec["track"] self.lock = RLock() self.attached = {} @@ -96,7 +92,7 @@ class Connection(Assembler): else: ssn = self.sessions.get(name) if ssn is None: - ssn = Session(name, self.spec, delegate=delegate) + ssn = Session(name, delegate=delegate) self.sessions[name] = ssn elif ssn.channel is not None: if force: @@ -189,23 +185,16 @@ class Connection(Assembler): log = getLogger("qpid.io.ctl") -class Channel(Invoker): +class Channel(control_invoker(SPEC)): def __init__(self, connection, id): self.connection = connection self.id = id self.session = None - def resolve_method(self, name): - inst = self.connection.spec.instructions.get(name) - if inst is not None and isinstance(inst, Control): - return self.METHOD, inst - else: - return self.ERROR, None - def invoke(self, type, args, kwargs): ctl = type.new(args, kwargs) - sc = StringCodec(self.connection.spec) + sc = StringCodec(self.spec) sc.write_control(ctl) self.connection.write_segment(Segment(True, True, type.segment_type, type.track, self.id, sc.encoded)) diff --git a/qpid/python/qpid/generator.py b/qpid/python/qpid/generator.py new file mode 100644 index 0000000000..307ea562d7 --- /dev/null +++ b/qpid/python/qpid/generator.py @@ -0,0 +1,60 @@ +# +# 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 sys + +from spec010 import Control + +def METHOD(module, inst): + method = lambda self, *args, **kwargs: self.invoke(inst, args, kwargs) + if sys.version_info[:2] > (2, 3): + method.__name__ = inst.pyname + method.__doc__ = inst.pydoc + method.__module__ = module + return method + +def generate(spec, module, predicate=lambda x: True): + dict = {"spec": spec} + + for name, enum in spec.enums.items(): + dict[name] = enum + + for name, st in spec.structs_by_name.items(): + dict[name] = METHOD(module, st) + + for st in spec.structs.values(): + dict[st.name] = METHOD(module, st) + + for name, inst in spec.instructions.items(): + if predicate(inst): + dict[name] = METHOD(module, inst) + + return dict + +def invoker(name, spec, predicate=lambda x: True): + return type("%s_%s_%s" % (name, spec.major, spec.minor), + (), generate(spec, invoker.__module__, predicate)) + +def command_invoker(spec): + is_command = lambda cmd: cmd.track == spec["track.command"].value + return invoker("CommandInvoker", spec, is_command) + +def control_invoker(spec): + is_control = lambda inst: isinstance(inst, Control) + return invoker("ControlInvoker", spec, is_control) diff --git a/qpid/python/qpid/invoker.py b/qpid/python/qpid/invoker.py deleted file mode 100644 index 635f3ee769..0000000000 --- a/qpid/python/qpid/invoker.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# 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 sys - -# TODO: need a better naming for this class now that it does the value -# stuff -class Invoker: - - def METHOD(self, name, resolved): - method = lambda *args, **kwargs: self.invoke(resolved, args, kwargs) - if sys.version_info[:2] > (2, 3): - method.__name__ = resolved.pyname - method.__doc__ = resolved.pydoc - method.__module__ = self.__class__.__module__ - self.__dict__[name] = method - return method - - def VALUE(self, name, resolved): - self.__dict__[name] = resolved - return resolved - - def ERROR(self, name, resolved): - raise AttributeError("%s instance has no attribute '%s'" % - (self.__class__.__name__, name)) - - def resolve_method(self, name): - return ERROR, None - - def __getattr__(self, name): - disp, resolved = self.resolve_method(name) - return disp(name, resolved) diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py index 4a7ecbc28a..587a226aa1 100644 --- a/qpid/python/qpid/session.py +++ b/qpid/python/qpid/session.py @@ -18,7 +18,8 @@ # from threading import Condition, RLock, Lock, currentThread -from invoker import Invoker +from spec import SPEC +from generator import command_invoker from datatypes import RangedSet, Struct, Future from codec010 import StringCodec from assembler import Segment @@ -43,11 +44,10 @@ def server(*args): INCOMPLETE = object() -class Session(Invoker): +class Session(command_invoker(SPEC)): - def __init__(self, name, spec, auto_sync=True, timeout=10, delegate=client): + def __init__(self, name, auto_sync=True, timeout=10, delegate=client): self.name = name - self.spec = spec self.auto_sync = auto_sync self.timeout = timeout self.channel = None @@ -133,21 +133,6 @@ class Session(Invoker): finally: self.lock.release() - def resolve_method(self, name): - cmd = self.spec.instructions.get(name) - if cmd is not None and cmd.track == self.spec["track.command"].value: - return self.METHOD, cmd - else: - # XXX - for st in self.spec.structs.values(): - if st.name == name: - return self.METHOD, st - if self.spec.structs_by_name.has_key(name): - return self.METHOD, self.spec.structs_by_name[name] - if self.spec.enums.has_key(name): - return self.VALUE, self.spec.enums[name] - return self.ERROR, None - def invoke(self, type, args, kwargs): # XXX if not hasattr(type, "track"): diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py index e6d914044c..cd76c70c5c 100644 --- a/qpid/python/qpid/spec.py +++ b/qpid/python/qpid/spec.py @@ -57,3 +57,5 @@ def load(specfile, *errata): return spec010.load(specfile, *errata) else: return spec08.load(specfile, *errata) + +SPEC = load(default()) -- cgit v1.2.1 From ded2aef1b9e1de09944c22b18de9784736cd1109 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 8 Jan 2009 17:16:38 +0000 Subject: removed amqp-doc as it is broken and the same functionality is now available through the python help builtin git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@732762 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/amqp-doc | 80 ---------------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100755 qpid/python/amqp-doc (limited to 'qpid/python') diff --git a/qpid/python/amqp-doc b/qpid/python/amqp-doc deleted file mode 100755 index 1f5910f942..0000000000 --- a/qpid/python/amqp-doc +++ /dev/null @@ -1,80 +0,0 @@ -#!/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 sys, re -from qpid.spec import load, pythonize -from getopt import gnu_getopt as getopt, GetoptError -from fnmatch import fnmatchcase as fnmatch - -def die(msg): - print >> sys.stderr, msg - sys.exit(1) - -def usage(msg = ""): - return ("""%s - -Usage %s [] [ ... ] - -Options: - -e, --regexp use regex instead of glob when matching - -s, --spec location of amqp.xml -""" % (msg, sys.argv[0])).strip() - -try: - opts, args = getopt(sys.argv[1:], "s:ea:", ["regexp", "spec=", "additional="]) -except GetoptError, e: - die(str(e)) - -regexp = False -spec = "../specs/amqp.0-9.xml" -errata = [] -for k, v in opts: - if k == "-e" or k == "--regexp": regexp = True - if k == "-s" or k == "--spec": spec = v - if k == "-a" or k == "--additional": errata.append(v) - -if regexp: - def match(pattern, value): - try: - return re.match(pattern, value) - except Exception, e: - die("error: '%s': %s" % (pattern, e)) -else: - def match(pattern, value): - return fnmatch(value, pattern) - -spec = load(spec, *errata) -methods = {} -patterns = args -for pattern in patterns: - for c in spec.classes: - for m in c.methods: - name = pythonize("%s_%s" % (c.name, m.name)) - if match(pattern, name): - methods[name] = m.define_method(name) - -if patterns: - if methods: - AMQP = type("AMQP[%s]" % ", ".join(patterns), (), methods) - else: - die("no matches") -else: - AMQP = spec.define_class("AMQP") - -help(AMQP) -- cgit v1.2.1 From f938d4a5c7ad2e50b63b4ca4310677ebc980a30b Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 8 Jan 2009 18:27:39 +0000 Subject: ignore hearbeat controls rather than barfing on them, also permit the heartbeat to be specified on connection creation git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@732778 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegates.py | 9 +++++++-- qpid/python/tests/connection.py | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index bf26553dda..61c8e8c326 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -61,6 +61,9 @@ class Delegate: self.connection.opened = False notify(self.connection.condition) + def connection_heartbeat(self, ch, hrt): + pass + def session_attach(self, ch, a): try: self.connection.attach(a.name, ch, self.delegate, a.force) @@ -139,11 +142,13 @@ class Client(Delegate): "version": "development", "platform": os.name} - def __init__(self, connection, username="guest", password="guest", mechanism="PLAIN"): + def __init__(self, connection, username="guest", password="guest", + mechanism="PLAIN", heartbeat=None): Delegate.__init__(self, connection) self.username = username self.password = password self.mechanism = mechanism + self.heartbeat = heartbeat def start(self): self.connection.write_header(self.spec.major, self.spec.minor) @@ -154,7 +159,7 @@ class Client(Delegate): ch.connection_start_ok(client_properties=Client.PROPERTIES, mechanism=self.mechanism, response=r) def connection_tune(self, ch, tune): - ch.connection_tune_ok() + ch.connection_tune_ok(heartbeat=self.heartbeat) ch.connection_open() def connection_open_ok(self, ch, open_ok): diff --git a/qpid/python/tests/connection.py b/qpid/python/tests/connection.py index 512fa62189..19cdad9f97 100644 --- a/qpid/python/tests/connection.py +++ b/qpid/python/tests/connection.py @@ -25,7 +25,6 @@ from qpid.datatypes import Message from qpid.testlib import testrunner from qpid.delegates import Server from qpid.queue import Queue -from qpid.spec010 import load from qpid.session import Delegate PORT = 1234 @@ -62,13 +61,14 @@ class TestSession(Delegate): cmd.acquire_mode, m) elif cmd.destination == "abort": self.session.channel.connection.sock.close() + elif cmd.destination == "heartbeat": + self.session.channel.connection_heartbeat() else: self.queue.put((cmd, headers, body)) class ConnectionTest(TestCase): def setUp(self): - self.spec = load(testrunner.get_spec_file("amqp.0-10.xml")) self.queue = Queue() self.running = True started = Event() @@ -76,7 +76,7 @@ class ConnectionTest(TestCase): def run(): ts = TestServer(self.queue) for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()): - conn = Connection(s, self.spec, ts.connection) + conn = Connection(s, delegate=ts.connection) try: conn.start(5) except Closed: @@ -94,8 +94,8 @@ class ConnectionTest(TestCase): connect("0.0.0.0", PORT).close() self.server.join(3) - def connect(self): - return Connection(connect("0.0.0.0", PORT), self.spec) + def connect(self, **kwargs): + return Connection(connect("0.0.0.0", PORT), **kwargs) def test(self): c = self.connect() @@ -213,3 +213,10 @@ class ConnectionTest(TestCase): s.auto_sync = False s.message_transfer("echo", message=Message("test")) s.sync(10) + + def testHeartbeat(self): + c = self.connect(heartbeat=10) + c.start(10) + s = c.session("test") + s.channel.connection_heartbeat() + s.message_transfer("heartbeat") -- cgit v1.2.1 From 36bdf084bc4fc98746c7f579352d7f7099167b1a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 8 Jan 2009 18:30:28 +0000 Subject: Added missing import statement git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@732783 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 1 + 1 file changed, 1 insertion(+) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 0009726fe7..86cf26acf9 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -25,6 +25,7 @@ import struct import socket import re from qpid.peer import Closed +from qpid.session import SessionDetached from qpid.connection import Connection, ConnectionFailed from qpid.datatypes import UUID, uuid4, Message, RangedSet from qpid.util import connect, ssl, URL -- cgit v1.2.1 From f458ae0764d1235448fc07a92f607bb0762cda12 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 9 Jan 2009 14:18:23 +0000 Subject: Fixed code problem for Python versions less than 2.5 git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@733041 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 1b7b5171ed..ff3c7db46e 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -101,7 +101,8 @@ class BrokerManager: self.brokerAgent = a def Disconnect(self): - self.qmf.delBroker(self.broker) + if self.broker: + self.qmf.delBroker(self.broker) def Overview (self): exchanges = self.qmf.getObjects(_class="exchange", _agent=self.brokerAgent) @@ -387,5 +388,5 @@ except KeyboardInterrupt: except Exception,e: print "Failed:", e.args sys.exit(1) -finally: - bm.Disconnect() + +bm.Disconnect() -- cgit v1.2.1 From 62371078f1f853a05d0ff2e31190baf018e4afc9 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Thu, 15 Jan 2009 11:29:38 +0000 Subject: QPID-1567: Initial support for asynchronous queue state replication * Added QueueEvents class with per broker instance * Modified qpid::broker::Queue to notify QueueEvents of enqueues and dequeues (based on configuration) * Added replication subdir containing two plugins: - an event listener that registers with QueueEvents and creates messages representing received events on a replication queue - a custom exchange type for processing messages of the format created by the listener plugin * Added new option for controlling event generation to qpid::client::QueueOptions * Added new queue option to qpid-config script for the same git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@734674 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index ff3c7db46e..ff2040e994 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -37,6 +37,7 @@ _policyType = None _lvq = False _msgSequence = False _ive = False +_eventGeneration = None FILECOUNT = "qpid.file_count" FILESIZE = "qpid.file_size" @@ -47,6 +48,7 @@ CLUSTER_DURABLE = "qpid.persist_last_node" LVQ = "qpid.last_value_queue" MSG_SEQUENCE = "qpid.msg_sequence" IVE = "qpid.ive" +QUEUE_EVENT_GENERATION = "qpid.queue_event_generation" def Usage (): print "Usage: qpid-config [OPTIONS]" @@ -74,6 +76,10 @@ def Usage (): print " --max-queue-count N Maximum in-memory queue size as a number of messages" print " --policy-type TYPE Action taken when queue limit is reached (reject, flow_to_disk, ring, ring_strict)" print " --last-value-queue Enable LVQ behavior on the queue" + print " --generate-queue-events N" + print " If set to 1, every enqueue will generate an event that can be processed by" + print " registered listeners (e.g. for replication). If set to 2, events will be" + print " generated for enqueues and dequeues" print print "Add Exchange Options:" print " --durable Exchange is durable" @@ -193,6 +199,7 @@ class BrokerManager: if MAX_QUEUE_COUNT in args: print "--max-queue-count=%d" % args[MAX_QUEUE_COUNT], if POLICY_TYPE in args: print "--policy-type=%s" % args[POLICY_TYPE], if LVQ in args and args[LVQ] == 1: print "--last-value-queue", + if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%d" % args[GENERATE_QUEUE_EVENTS], print def QueueListRecurse (self, filter): @@ -249,6 +256,8 @@ class BrokerManager: declArgs[CLUSTER_DURABLE] = 1 if _lvq: declArgs[LVQ] = 1 + if _eventGeneration: + declArgs[QUEUE_EVENT_GENERATION] = _eventGeneration self.broker.getAmqpSession().queue_declare (queue=qname, durable=_durable, arguments=declArgs) @@ -304,7 +313,7 @@ def YN (bool): try: longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=", - "last-value-queue", "sequence", "ive") + "last-value-queue", "sequence", "ive", "generate-queue-events=") (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -340,6 +349,8 @@ for opt in optlist: _msgSequence = True if opt[0] == "--ive": _ive = True + if opt[0] == "--generate-queue-events": + _eventGeneration = int (opt[1]) nargs = len (cargs) bm = BrokerManager () -- cgit v1.2.1 From 6bd7a44652de1e5c91fbe0148d34e446cd4ee5a9 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 15 Jan 2009 12:39:17 +0000 Subject: Added lvq-no-browse support to qpid-config. Added tests for the qpid-specific queue parameters. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@734689 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 65 ++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 16 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index ff2040e994..a7d537edb5 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -33,8 +33,8 @@ _fileCount = 8 _fileSize = 24 _maxQueueSize = None _maxQueueCount = None -_policyType = None -_lvq = False +_limitPolicy = None +_order = None _msgSequence = False _ive = False _eventGeneration = None @@ -46,6 +46,7 @@ MAX_QUEUE_COUNT = "qpid.max_count" POLICY_TYPE = "qpid.policy_type" CLUSTER_DURABLE = "qpid.persist_last_node" LVQ = "qpid.last_value_queue" +LVQNB = "qpid.last_value_queue_no_browse" MSG_SEQUENCE = "qpid.msg_sequence" IVE = "qpid.ive" QUEUE_EVENT_GENERATION = "qpid.queue_event_generation" @@ -74,8 +75,18 @@ def Usage (): print " --file-size N (24) File size in pages (64Kib/page)" print " --max-queue-size N Maximum in-memory queue size as bytes" print " --max-queue-count N Maximum in-memory queue size as a number of messages" - print " --policy-type TYPE Action taken when queue limit is reached (reject, flow_to_disk, ring, ring_strict)" - print " --last-value-queue Enable LVQ behavior on the queue" + print " --limit-policy [none | reject | flow-to-disk | ring | ring-strict]" + print " Action taken when queue limit is reached:" + print " none (default) - Use broker's default policy" + print " reject - Reject enqueued messages" + print " flow-to-disk - Page messages to disk" + print " ring - Replace oldest unacquired message with new" + print " ring-strict - Replace oldest message, reject if oldest is acquired" + print " --order [fifo | lvq | lvq-no-browse]" + print " Set queue ordering policy:" + print " fifo (default) - First in, first out" + print " lvq - Last Value Queue ordering, allows queue browsing" + print " lvq-no-browse - Last Value Queue ordering, browsing clients may lose data" print " --generate-queue-events N" print " If set to 1, every enqueue will generate an event that can be processed by" print " registered listeners (e.g. for replication). If set to 2, events will be" @@ -197,8 +208,9 @@ class BrokerManager: if FILECOUNT in args: print "--file-count=%d" % args[FILECOUNT], if MAX_QUEUE_SIZE in args: print "--max-queue-size=%d" % args[MAX_QUEUE_SIZE], if MAX_QUEUE_COUNT in args: print "--max-queue-count=%d" % args[MAX_QUEUE_COUNT], - if POLICY_TYPE in args: print "--policy-type=%s" % args[POLICY_TYPE], - if LVQ in args and args[LVQ] == 1: print "--last-value-queue", + if POLICY_TYPE in args: print "--limit-policy=%s" % args[POLICY_TYPE].replace("_", "-"), + if LVQ in args and args[LVQ] == 1: print "--order lvq", + if LVQNB in args and args[LVQNB] == 1: print "--order lvq-no-browse", if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%d" % args[GENERATE_QUEUE_EVENTS], print @@ -250,12 +262,27 @@ class BrokerManager: declArgs[MAX_QUEUE_SIZE] = _maxQueueSize if _maxQueueCount: declArgs[MAX_QUEUE_COUNT] = _maxQueueCount - if _policyType: - declArgs[POLICY_TYPE] = _policyType + if _limitPolicy: + if _limitPolicy == "none": + pass + elif _limitPolicy == "reject": + declArgs[POLICY_TYPE] = "reject" + elif _limitPolicy == "flow-to-disk": + declArgs[POLICY_TYPE] = "flow_to_disk" + elif _limitPolicy == "ring": + declArgs[POLICY_TYPE] = "ring" + elif _limitPolicy == "ring-strict": + declArgs[POLICY_TYPE] = "ring_strict" + if _clusterDurable: declArgs[CLUSTER_DURABLE] = 1 - if _lvq: - declArgs[LVQ] = 1 + if _order: + if _order == "fifo": + pass + elif _order == "lvq": + declArgs[LVQ] = 1 + elif _order == "lvq-no-browse": + declArgs[LVQNB] = 1 if _eventGeneration: declArgs[QUEUE_EVENT_GENERATION] = _eventGeneration @@ -312,8 +339,8 @@ def YN (bool): try: longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", - "file-size=", "max-queue-size=", "max-queue-count=", "policy-type=", - "last-value-queue", "sequence", "ive", "generate-queue-events=") + "file-size=", "max-queue-size=", "max-queue-count=", "limit-policy=", + "order=", "sequence", "ive", "generate-queue-events=") (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -341,10 +368,16 @@ for opt in optlist: _maxQueueSize = int (opt[1]) if opt[0] == "--max-queue-count": _maxQueueCount = int (opt[1]) - if opt[0] == "--policy-type": - _policyType = opt[1] - if opt[0] == "--last-value-queue": - _lvq = True + if opt[0] == "--limit-policy": + _limitPolicy = opt[1] + if _limitPolicy not in ("none", "reject", "flow-to-disk", "ring", "ring-strict"): + print "Error: Invalid --limit-policy argument" + sys.exit(1) + if opt[0] == "--order": + _order = opt[1] + if _order not in ("fifo", "lvq", "lvq-no-browse"): + print "Error: Invalid --order argument" + sys.exit(1) if opt[0] == "--sequence": _msgSequence = True if opt[0] == "--ive": -- cgit v1.2.1 From f3287a51ca39d64b9cbc848b8db2c5a72fd2bdd6 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Tue, 20 Jan 2009 13:30:08 +0000 Subject: QPID-1567: added 'exactly-once' guarantee to asynchronous replication of queue state * altered replication protocol to detect and eliminate duplicates * added support for acknowledged transfer over inter-broker bridges * added option to qpid-route to control this git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@736018 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-route | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-route b/qpid/python/commands/qpid-route index e0e655683a..324ce2e176 100755 --- a/qpid/python/commands/qpid-route +++ b/qpid/python/commands/qpid-route @@ -48,6 +48,7 @@ def Usage(): print " -d [ --durable ] Added configuration shall be durable" print " -e [ --del-empty-link ] Delete link after deleting last route on the link" print " -s [ --src-local ] Make connection to source broker (push route)" + print " --ack N Acknowledge transfers over the bridge in batches of N" print " -t [ --transport ]" print " Specify transport to use for links, defaults to tcp" print @@ -62,6 +63,7 @@ _durable = False _dellink = False _srclocal = False _transport = "tcp" +_ack = 0 class RouteManager: def __init__(self, localBroker): @@ -234,7 +236,7 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." - res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, _srclocal, dynamic) + res = link.bridge(_durable, exchange, exchange, routingKey, tag, excludes, False, _srclocal, dynamic, _ack) if res.status != 0: raise Exception(res.text) if _verbose: @@ -256,7 +258,7 @@ class RouteManager: if _verbose: print "Creating inter-broker binding..." - res = link.bridge(_durable, queue, exchange, "", "", "", True, _srclocal, False) + res = link.bridge(_durable, queue, exchange, "", "", "", True, _srclocal, False, _ack) if res.status != 0: raise Exception(res.text) if _verbose: @@ -401,7 +403,7 @@ def YN(val): ## try: - longOpts = ("verbose", "quiet", "durable", "del-empty-link", "src-local", "transport=") + longOpts = ("verbose", "quiet", "durable", "del-empty-link", "src-local", "transport=", "ack=") (optlist, encArgs) = getopt.gnu_getopt(sys.argv[1:], "vqdest:", longOpts) except: Usage() @@ -425,6 +427,8 @@ for opt in optlist: _srclocal = True if opt[0] == "-t" or opt[0] == "--transport": _transport = opt[1] + if opt[0] == "--ack": + _ack = int(opt[1]) nargs = len(cargs) if nargs < 2: -- cgit v1.2.1 From d23a587fee328ffa88f4ed18d2a0766db1c36d88 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Thu, 22 Jan 2009 18:55:12 +0000 Subject: ensure that we always close the socket, even if the other end doesn't git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@736742 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/connection.py | 8 +++++--- qpid/python/qpid/delegates.py | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py index 6665e3e40c..3ab430587a 100644 --- a/qpid/python/qpid/connection.py +++ b/qpid/python/qpid/connection.py @@ -71,8 +71,11 @@ class Connection(Assembler): self.sessions = {} self.condition = Condition() + # XXX: we should combine this into a single comprehensive state + # model (whatever that means) self.opened = False self.failed = False + self.closed = False self.close_code = (None, "connection aborted") self.thread = Thread(target=self.run) @@ -160,15 +163,14 @@ class Connection(Assembler): raise ConnectionFailed(*self.close_code) def run(self): - # XXX: we don't really have a good way to exit this loop without - # getting the other end to kill the socket - while True: + while not self.closed: try: seg = self.read_segment() except Closed: self.detach_all() break self.delegate.received(seg) + self.sock.close() def close(self, timeout=None): if not self.opened: return diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 61c8e8c326..7cfd9b11db 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -59,6 +59,7 @@ class Delegate: def connection_close_ok(self, ch, close_ok): self.connection.opened = False + self.connection.closed = True notify(self.connection.condition) def connection_heartbeat(self, ch, hrt): -- cgit v1.2.1 From d8c1199a62dbff0e287b54b924ec4ccb319ac037 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Mon, 26 Jan 2009 20:24:49 +0000 Subject: Make sure docstrings are regular strings and not unicode. This works around a bug where epydoc ignores them if they're unicode. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@737818 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/generator.py b/qpid/python/qpid/generator.py index 307ea562d7..729425d6a3 100644 --- a/qpid/python/qpid/generator.py +++ b/qpid/python/qpid/generator.py @@ -24,8 +24,8 @@ from spec010 import Control def METHOD(module, inst): method = lambda self, *args, **kwargs: self.invoke(inst, args, kwargs) if sys.version_info[:2] > (2, 3): - method.__name__ = inst.pyname - method.__doc__ = inst.pydoc + method.__name__ = str(inst.pyname) + method.__doc__ = str(inst.pydoc) method.__module__ = module return method -- cgit v1.2.1 From a26e85a730375155b69df1913c907aad6144b028 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 26 Jan 2009 23:17:29 +0000 Subject: Added qpid-cluster utility plus model changes to support it. Fixed a segfault during cluster member shutdown. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@737935 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-cluster | 180 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100755 qpid/python/commands/qpid-cluster (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-cluster b/qpid/python/commands/qpid-cluster new file mode 100755 index 0000000000..f2028d944b --- /dev/null +++ b/qpid/python/commands/qpid-cluster @@ -0,0 +1,180 @@ +#!/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 os +import getopt +import sys +import locale +from qmf.console import Session + +_host = "localhost" +_stopId = None +_stopAll = False +_force = False + +def Usage (): + print "Usage: qpid-cluster [OPTIONS] [broker-addr]" + print + print " broker-addr is in the form: [username/password@] hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" + print + print "Options:" + print " -s [--stop] ID Stop one member of the cluster by its ID" + print " -k [--all-stop] Shut down the whole cluster" + print " -f [--force] Suppress the 'are-you-sure?' prompt" + print + sys.exit (1) + +class BrokerManager: + def __init__(self): + self.brokerName = None + self.qmf = None + self.broker = None + + def SetBroker(self, brokerUrl): + self.url = brokerUrl + self.qmf = Session() + self.broker = self.qmf.addBroker(brokerUrl) + agents = self.qmf.getAgents() + for a in agents: + if a.getAgentBank() == 0: + self.brokerAgent = a + + def Disconnect(self): + if self.broker: + self.qmf.delBroker(self.broker) + + def overview(self): + packages = self.qmf.getPackages() + if "org.apache.qpid.cluster" not in packages: + print "Clustering is not installed on the broker." + sys.exit(0) + + clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) + if len(clusters) == 0: + print "Clustering is installed but not enabled on the broker." + sys.exit(0) + + cluster = clusters[0] + myUrl = cluster.publishedURL + memberList = cluster.members.split(";") + idList = cluster.memberIDs.split(";") + + print " Cluster Name: %s" % cluster.clusterName + print "Cluster Status: %s" % cluster.status + print " Cluster Size: %d" % cluster.clusterSize + print " Members: ID=%s URL=%s" % (idList[0], memberList[0]) + for idx in range(1,len(idList)): + print " : ID=%s URL=%s" % (idList[idx], memberList[idx]) + + def stopMember(self, id): + clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) + if len(clusters) == 0: + print "Clustering is installed but not enabled on the broker." + sys.exit(0) + + cluster = clusters[0] + idList = cluster.memberIDs.split(";") + if id not in idList: + print "No member with matching ID found" + sys.exit(1) + + if not _force: + prompt = "Warning: " + if len(idList) == 1: + prompt += "This command will shut down the last running cluster member." + else: + prompt += "This command will shut down a cluster member." + prompt += " Are you sure? [N]: " + + confirm = raw_input(prompt) + if len(confirm) == 0 or confirm[0].upper() != 'Y': + print "Operation canceled" + sys.exit(1) + + cluster.stopClusterNode(id) + + def stopAll(self): + clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) + if len(clusters) == 0: + print "Clustering is installed but not enabled on the broker." + sys.exit(0) + + if not _force: + prompt = "Warning: This command will shut down the entire cluster." + prompt += " Are you sure? [N]: " + + confirm = raw_input(prompt) + if len(confirm) == 0 or confirm[0].upper() != 'Y': + print "Operation canceled" + sys.exit(1) + + cluster = clusters[0] + cluster.stopFullCluster() + +## +## Main Program +## + +try: + longOpts = ("stop=", "all-stop", "force") + (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "s:kf", longOpts) +except: + Usage () + +try: + encoding = locale.getpreferredencoding() + cargs = [a.decode(encoding) for a in encArgs] +except: + cargs = encArgs + +for opt in optlist: + if opt[0] == "-s" or opt[0] == "--stop": + _stopId = opt[1] + if opt[0] == "-k" or opt[0] == "--all-stop": + _stopAll = True + if opt[0] == "-f" or opt[0] == "--force": + _force = True + +nargs = len(cargs) +bm = BrokerManager() + +if nargs == 1: + _host = cargs[0] + +try: + bm.SetBroker(_host) + if _stopId: + bm.stopMember(_stopId) + elif _stopAll: + bm.stopAll() + else: + bm.overview() +except KeyboardInterrupt: + print +except Exception,e: + if e.__repr__().find("connection aborted") > 0: + # we expect this when asking the connected broker to shut down + sys.exit(0) + print "Failed:", e.args + sys.exit(1) + +bm.Disconnect() -- cgit v1.2.1 From 75e72b1140efa2dcb0d67896a9449bad026a8fb6 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Mon, 2 Feb 2009 15:58:16 +0000 Subject: Added Object.isDeleted(), Object.update() Fixed a bug in Object.merge() Added additional pydoc text git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@740017 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 86cf26acf9..49f920aa51 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -968,9 +968,10 @@ class ObjectId: return (self.first, self.second).__eq__(other) class Object(object): - """ """ + """ This class defines a 'proxy' object representing a real managed object on an agent. + Actions taken on this proxy are remotely affected on the real managed object. + """ def __init__(self, session, broker, schema, codec, prop, stat): - """ """ self._session = session self._broker = broker self._schema = schema @@ -1015,6 +1016,10 @@ class Object(object): """ Return the current, creation, and deletion times for this object. """ return self._currentTime, self._createTime, self._deleteTime + def isDeleted(self): + """ Return True iff this object has been deleted. """ + return self._deleteTime != 0 + def getIndex(self): """ Return a string describing this object's primary key. """ result = u"" @@ -1030,9 +1035,11 @@ class Object(object): return result def getProperties(self): + """ Return a list of object properties """ return self._properties def getStatistics(self): + """ Return a list of object statistics """ return self._statistics def mergeUpdate(self, newer): @@ -1040,9 +1047,17 @@ class Object(object): if self._objectId != newer._objectId: raise Exception("Objects with different object-ids") if len(newer.getProperties()) > 0: - self.properties = newer.getProperties() + self._properties = newer.getProperties() if len(newer.getStatistics()) > 0: - self.statistics = newer.getStatistics() + self._statistics = newer.getStatistics() + + def update(self): + """ Contact the agent and retrieve the lastest property and statistic values for this object. """ + obj = self._session.getObjects(_objectId = self._objectId, _broker=self._broker) + if obj: + self.mergeUpdate(obj[0]) + else: + raise Exception("Underlying object no longer exists") def __repr__(self): key = self.getClassKey() -- cgit v1.2.1 From 6b4f3c57a9cbaa808e66cd284af90a70da7172e1 Mon Sep 17 00:00:00 2001 From: Jonathan Robie Date: Thu, 12 Feb 2009 16:08:27 +0000 Subject: Instructions on how to run the Python examples. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@743788 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/README | 267 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 qpid/python/examples/README (limited to 'qpid/python') diff --git a/qpid/python/examples/README b/qpid/python/examples/README new file mode 100644 index 0000000000..24d449afc2 --- /dev/null +++ b/qpid/python/examples/README @@ -0,0 +1,267 @@ +Running the Python Examples +============================ + + +Running the Direct Examples +---------------------------- + +To run the direct examples, do the following: + +1. Make sure that a qpidd broker is running: + + $ ps -eaf | grep qpidd + + If a broker is running, you should see the qpidd process in the output of the above command. + +2.Declare a message queue and bind it to an exchange by running declare_queues.py, as follows: + + $ python declare_queues.py + + This program has no output. After this program has been run, all messages sent to the amq.direct exchange using the routing key routing_key are sent to the queue named message_queue. + +3.Publish a series of messages to the amq.direct exchange by running direct_producer.py, as follows: + + $ python direct_producer.py + +This program has no output; the messages are routed to the message queue, as instructed by the binding. + +4. Read the messages from the message queue using direct_consumer.py or listener.py, as follows: + + $ python direct_consumer.py + + or + + $ python listener.py + +You should see the following output: + +message 0 +message 1 +message 2 +message 3 +message 4 +message 5 +message 6 +message 7 +message 8 +message 9 +That's all, folks! + + + +Running the Fanout Examples +---------------------------- + +To run the programs for the Fanout example, do the following: + +1. Make sure that a qpidd broker is running: + + $ ps -eaf | grep qpidd + +If a broker is running, you should see the qpidd process in the output of the above command. + +2. In separate windows, start two or more fanout consumers or fanout listeners as follows: + + $ python fanout_consumer.py + + or + + $ python listener.py + +These programs each create a private queue, bind it to the amq.fanout exchange, and wait for messages to arrive on their queue. + +3. In a separate window, publish a series of messages to the amq.fanout exchange by running fanout_producer.py, as follows: + + $ python fanout_producer.py + +This program has no output; the messages are routed to the message queue, as instructed by the binding. + +4. Go to the windows where you are running consumers or listeners. You should see the following output for each listener or consumer: + + message 0 + message 1 + message 2 + message 3 + message 4 + message 5 + message 6 + message 7 + message 8 + message 9 + That's all, folks! + + + +Running the Publish-Subscribe Examples +--------------------------------------- + +To run the programs for the Publish-Subscribe example, do the following: + +1. Make sure that a qpidd broker is running: + + $ ps -eaf | grep qpidd + +If a broker is running, you should see the qpidd process in the output of the above command. + +2. In separate windows, start one or more topic subscribers by running topic_subscriber.py, as follows: + + $ python topic_subscriber.py + +You will see output similar to this: + + Queues created - please start the topic producer + Subscribing local queue 'local_news' to news-53408183-fcee-4b92-950b-90abb297e739' + Subscribing local queue 'local_weather' to weather-53408183-fcee-4b92-950b-90abb297e739' + Subscribing local queue 'local_usa' to usa-53408183-fcee-4b92-950b-90abb297e739' + Subscribing local queue 'local_europe' to europe-53408183-fcee-4b92-950b-90abb297e739' + Messages on 'news' queue: + +Each topic consumer creates a set of private queues, and binds each queue to the amq.topic exchange together with a binding that indicates which messages should be routed to the queue. + +3.In another window, start the topic publisher, which publishes messages to the amq.topic exchange, as follows: + + $ python topic_publisher.py + +This program has no output; the messages are routed to the message queues for each topic_consumer as specified by the bindings the consumer created. + +4. Go back to the window for each topic consumer. You should see output like this: + + Messages on 'news' queue: + usa.news 0 + usa.news 1 + usa.news 2 + usa.news 3 + usa.news 4 + europe.news 0 + europe.news 1 + europe.news 2 + europe.news 3 + europe.news 4 + That's all, folks! + Messages on 'weather' queue: + usa.weather 0 + usa.weather 1 + usa.weather 2 + usa.weather 3 + usa.weather 4 + europe.weather 0 + europe.weather 1 + europe.weather 2 + europe.weather 3 + europe.weather 4 + That's all, folks! + Messages on 'usa' queue: + usa.news 0 + usa.news 1 + usa.news 2 + usa.news 3 + usa.news 4 + usa.weather 0 + usa.weather 1 + usa.weather 2 + usa.weather 3 + usa.weather 4 + That's all, folks! + Messages on 'europe' queue: + europe.news 0 + europe.news 1 + europe.news 2 + europe.news 3 + europe.news 4 + europe.weather 0 + europe.weather 1 + europe.weather 2 + europe.weather 3 + europe.weather 4 + That's all, folks! + + +Running the Request/Response Examples +-------------------------------------- + +To run the programs for the Request/Response example, do the following: + +1. Make sure that a qpidd broker is running: + + $ ps -eaf | grep qpidd + +If a broker is running, you should see the qpidd process in the output of the above command. + +2. Run the server. + + $ python server.py + +You should see the following output: + + Request server running - run your client now. + (Times out after 100 seconds ...) + +3. In a separate window, start a client: + + $ python client.py + +You should see the following output: + + Request: Twas brillig, and the slithy toves + Request: Did gyre and gimble in the wabe. + Request: All mimsy were the borogroves, + Request: And the mome raths outgrabe. + Messages on queue: reply_to:db0f862e-6b36-4e0f-a4b2-ad049eb435ce + Response: TWAS BRILLIG, AND THE SLITHY TOVES + Response: DID GYRE AND GIMBLE IN THE WABE. + Response: ALL MIMSY WERE THE BOROGROVES, + Response: AND THE MOME RATHS OUTGRABE. + No more messages! + + +Running the XML-based Routing Examples +--------------------------------------- + +To run the programs for the XML-based Routing example, do the following: + +1. Make sure that a qpidd broker is running: + + $ ps -eaf | grep qpidd + +If a broker is running, you should see the qpidd process in the output of the above command. + +2. Declare an XML exchange and a message queue, then bind the queue to the exchange by running declare_queues.py, as follows: + + $ python declare_queues.py + +This program has no output. After this program has been run, all messages sent to the xml exchange using the routing key weather are sent to the queue named message_queue if they satisfy the conditions specified in the following XQuery, which is used in the binding: + + let $w := ./weather + return $w/station = 'Raleigh-Durham International Airport (KRDU)' + and $w/temperature_f > 50 + and $w/temperature_f - $w/dewpoint > 5 + and $w/wind_speed_mph > 7 + and $w/wind_speed_mph < 20 + +3. Publish a series of messages to the xml exchange by running xml_producer.py, as follows: + + $ python xml_producer.py + +The messages are routed to the message queue, as prescribed by the binding. Each message represents a weather report, such as this one: + + + Raleigh-Durham International Airport (KRDU) + 16 + 70 + 35 + + +4. Read the messages from the message queue using direct_consumer.py or listener.py, as follows: + + $ python xml_consumer.py + + or + + $ python listener.py + +You should see the following output: + +Raleigh-Durham International Airport (KRDU) +1670 +35 + -- cgit v1.2.1 From 64b26d7ac98b821e176f97d96a6580dafac353f6 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 12 Feb 2009 18:10:23 +0000 Subject: Remove unicode when dealing with usernames and passwords in URLs. This solves a problem seen with qpid-route when credentials are supplied. A test was added to regression test. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@743819 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 49f920aa51..ed25c77acc 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -96,8 +96,8 @@ class BrokerURL(URL): self.port = 5671 else: self.port = 5672 - self.authName = self.user or "guest" - self.authPass = self.password or "guest" + self.authName = str(self.user) or "guest" + self.authPass = str(self.password) or "guest" self.authMech = "PLAIN" def name(self): -- cgit v1.2.1 From fcdd0466a29df17e20d4d7c158cad4f91ca8700a Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 12 Feb 2009 20:01:09 +0000 Subject: Fixed regression that breaks the default username behavior git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@743856 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index ed25c77acc..867d707f31 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -96,8 +96,8 @@ class BrokerURL(URL): self.port = 5671 else: self.port = 5672 - self.authName = str(self.user) or "guest" - self.authPass = str(self.password) or "guest" + self.authName = str(self.user or "guest") + self.authPass = str(self.password or "guest") self.authMech = "PLAIN" def name(self): -- cgit v1.2.1 From c1095c7c31360307e66b1a7d549775c34dba92fd Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 19 Feb 2009 12:00:46 +0000 Subject: QPID-1669 - Added client connection management to qpid-cluster. Also added better session-id discrimination in the qmf.console library. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@745832 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-cluster | 175 ++++++++++++++++++++++++++++++++++---- qpid/python/qmf/console.py | 4 +- 2 files changed, 160 insertions(+), 19 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-cluster b/qpid/python/commands/qpid-cluster index f2028d944b..2928ba65ea 100755 --- a/qpid/python/commands/qpid-cluster +++ b/qpid/python/commands/qpid-cluster @@ -23,12 +23,17 @@ import os import getopt import sys import locale +import socket +import re from qmf.console import Session _host = "localhost" _stopId = None _stopAll = False _force = False +_numeric = False +_showConn = False +_delConn = None def Usage (): print "Usage: qpid-cluster [OPTIONS] [broker-addr]" @@ -37,12 +42,40 @@ def Usage (): print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print print "Options:" - print " -s [--stop] ID Stop one member of the cluster by its ID" - print " -k [--all-stop] Shut down the whole cluster" - print " -f [--force] Suppress the 'are-you-sure?' prompt" + print " -C [--all-connections] View client connections to all cluster members" + print " -c [--connections] ID View client connections to specified member" + print " -d [--del-connection] HOST:PORT" + print " Disconnect a client connection" + print " -s [--stop] ID Stop one member of the cluster by its ID" + print " -k [--all-stop] Shut down the whole cluster" + print " -f [--force] Suppress the 'are-you-sure?' prompt" + print " -n [--numeric] Don't resolve names" print sys.exit (1) + +class IpAddr: + def __init__(self, text): + if text.find(":") != -1: + tokens = text.split(":") + text = tokens[0] + self.port = int(tokens[1]) + else: + self.port = 5672 + self.dottedQuad = socket.gethostbyname(text) + nums = self.dottedQuad.split(".") + self.addr = (int(nums[0]) << 24) + (int(nums[1]) << 16) + (int(nums[2]) << 8) + int(nums[3]) + + def bestAddr(self, addrPortList): + bestDiff = 0xFFFFFFFF + bestAddr = None + for addrPort in addrPortList: + diff = IpAddr(addrPort[0]).addr ^ self.addr + if diff < bestDiff: + bestDiff = diff + bestAddr = addrPort + return bestAddr + class BrokerManager: def __init__(self): self.brokerName = None @@ -62,7 +95,7 @@ class BrokerManager: if self.broker: self.qmf.delBroker(self.broker) - def overview(self): + def _getClusters(self): packages = self.qmf.getPackages() if "org.apache.qpid.cluster" not in packages: print "Clustering is not installed on the broker." @@ -73,8 +106,35 @@ class BrokerManager: print "Clustering is installed but not enabled on the broker." sys.exit(0) + return clusters + + def _getHostList(self, urlList): + hosts = [] + hostAddr = IpAddr(_host) + for url in urlList: + if url.find("amqp:") != 0: + raise Exception("Invalid URL 1") + url = url[5:] + addrs = str(url).split(",") + addrList = [] + for addr in addrs: + tokens = addr.split(":") + if len(tokens) != 3: + raise Exception("Invalid URL 2") + addrList.append((tokens[1], tokens[2])) + + # Find the address in the list that is most likely to be in the same subnet as the address + # with which we made the original QMF connection. This increases the probability that we will + # be able to reach the cluster member. + + best = hostAddr.bestAddr(addrList) + bestUrl = best[0] + ":" + best[1] + hosts.append(bestUrl) + return hosts + + def overview(self): + clusters = self._getClusters() cluster = clusters[0] - myUrl = cluster.publishedURL memberList = cluster.members.split(";") idList = cluster.memberIDs.split(";") @@ -86,11 +146,7 @@ class BrokerManager: print " : ID=%s URL=%s" % (idList[idx], memberList[idx]) def stopMember(self, id): - clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) - if len(clusters) == 0: - print "Clustering is installed but not enabled on the broker." - sys.exit(0) - + clusters = self._getClusters() cluster = clusters[0] idList = cluster.memberIDs.split(";") if id not in idList: @@ -113,11 +169,7 @@ class BrokerManager: cluster.stopClusterNode(id) def stopAll(self): - clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) - if len(clusters) == 0: - print "Clustering is installed but not enabled on the broker." - sys.exit(0) - + clusters = self._getClusters() if not _force: prompt = "Warning: This command will shut down the entire cluster." prompt += " Are you sure? [N]: " @@ -130,15 +182,72 @@ class BrokerManager: cluster = clusters[0] cluster.stopFullCluster() + def showConnections(self): + clusters = self._getClusters() + cluster = clusters[0] + memberList = cluster.members.split(";") + idList = cluster.memberIDs.split(";") + displayList = [] + hostList = self._getHostList(memberList) + self.qmf.delBroker(self.broker) + self.broker = None + self.brokers = [] + pattern = re.compile("^\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+$") + + idx = 0 + for host in hostList: + if _showConn == "all" or _showConn == idList[idx] or _delConn: + self.brokers.append(self.qmf.addBroker(host)) + displayList.append(idList[idx]) + idx += 1 + + idx = 0 + found = False + for broker in self.brokers: + if not _delConn: + print "Clients on Member: ID=%s:" % displayList[idx] + connList = self.qmf.getObjects(_class="connection", _package="org.apache.qpid.broker", _broker=broker) + for conn in connList: + if pattern.match(conn.address): + if _numeric or _delConn: + a = conn.address + else: + tokens = conn.address.split(":") + try: + hostList = socket.gethostbyaddr(tokens[0]) + host = hostList[0] + except: + host = tokens[0] + a = host + ":" + tokens[1] + if _delConn: + tokens = _delConn.split(":") + ip = socket.gethostbyname(tokens[0]) + toDelete = ip + ":" + tokens[1] + if a == toDelete: + print "Closing connection from client: %s" % a + conn.close() + found = True + else: + print " %s" % a + idx += 1 + if not _delConn: + print + if _delConn and not found: + print "Client connection '%s' not found" % _delConn + + for broker in self.brokers: + self.qmf.delBroker(broker) + + ## ## Main Program ## try: - longOpts = ("stop=", "all-stop", "force") - (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "s:kf", longOpts) + longOpts = ("stop=", "all-stop", "force", "connections=", "all-connections" "del-connection=", "numeric") + (optlist, encArgs) = getopt.gnu_getopt(sys.argv[1:], "s:kfCc:d:n", longOpts) except: - Usage () + Usage() try: encoding = locale.getpreferredencoding() @@ -146,13 +255,41 @@ try: except: cargs = encArgs +count = 0 for opt in optlist: if opt[0] == "-s" or opt[0] == "--stop": _stopId = opt[1] + if len(_stopId.split(":")) != 2: + print "Member ID must be of form: :" + sys.exit(1) + count += 1 if opt[0] == "-k" or opt[0] == "--all-stop": _stopAll = True + count += 1 if opt[0] == "-f" or opt[0] == "--force": _force = True + if opt[0] == "-n" or opt[0] == "--numeric": + _numeric = True + if opt[0] == "-C" or opt[0] == "--all-connections": + _showConn = "all" + count += 1 + if opt[0] == "-c" or opt[0] == "--connections": + _showConn = opt[1] + if len(_showConn.split(":")) != 2: + print "Member ID must be of form: :" + sys.exit(1) + count += 1 + if opt[0] == "-d" or opt[0] == "--del-connection": + _delConn = opt[1] + if len(_delConn.split(":")) != 2: + print "Connection must be of form: :" + sys.exit(1) + count += 1 + +if count > 1: + print "Only one command option may be supplied" + print + Usage() nargs = len(cargs) bm = BrokerManager() @@ -166,6 +303,8 @@ try: bm.stopMember(_stopId) elif _stopAll: bm.stopAll() + elif _showConn or _delConn: + bm.showConnections() else: bm.overview() except KeyboardInterrupt: diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 867d707f31..abc0929955 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -1230,6 +1230,7 @@ class ManagedConnection(Thread): class Broker: """ This object represents a connection (or potential connection) to a QMF broker. """ SYNC_TIME = 60 + nextSeq = 1 def __init__(self, session, host, port, authMech, authUser, authPass, ssl=False): self.session = session @@ -1242,7 +1243,8 @@ class Broker: self.error = None self.brokerId = None self.connected = False - self.amqpSessionId = "%s.%d" % (os.uname()[1], os.getpid()) + self.amqpSessionId = "%s.%d.%d" % (os.uname()[1], os.getpid(), Broker.nextSeq) + Broker.nextSeq += 1 if self.session.manageConnections: self.thread = ManagedConnection(self) self.thread.start() -- cgit v1.2.1 From 043cfb51401c11dbe9a9db7c07580f3ff84efb3b Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Tue, 24 Feb 2009 14:15:28 +0000 Subject: QPID-1676 - Added client process name and pid to connection object for management. Supported in C++, Python, and Ruby clients. Still needs to be added to the Java clients. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@747389 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/delegates.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py index 7cfd9b11db..a720e2e1c7 100644 --- a/qpid/python/qpid/delegates.py +++ b/qpid/python/qpid/delegates.py @@ -21,6 +21,7 @@ import os, connection, session from util import notify from datatypes import RangedSet from logging import getLogger +import sys log = getLogger("qpid.io.ctl") @@ -141,7 +142,10 @@ class Client(Delegate): PROPERTIES = {"product": "qpid python client", "version": "development", - "platform": os.name} + "platform": os.name, + "qpid.client_process": os.path.basename(sys.argv[0]), + "qpid.client_pid": os.getpid(), + "qpid.client_ppid": os.getppid()} def __init__(self, connection, username="guest", password="guest", mechanism="PLAIN", heartbeat=None): -- cgit v1.2.1 From e43cde0cb4340153391acd5d4f3cb9130ca7b474 Mon Sep 17 00:00:00 2001 From: "Stephen D. Huston" Date: Tue, 24 Feb 2009 16:26:47 +0000 Subject: Replace os.uname with platform.uname to enable this to work on Windows git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@747417 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 3 ++- qpid/python/qpid/managementdata.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index abc0929955..39ef157f82 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -20,6 +20,7 @@ """ Console API for Qpid Management Framework """ import os +import platform import qpid import struct import socket @@ -1243,7 +1244,7 @@ class Broker: self.error = None self.brokerId = None self.connected = False - self.amqpSessionId = "%s.%d.%d" % (os.uname()[1], os.getpid(), Broker.nextSeq) + self.amqpSessionId = "%s.%d.%d" % (platform.uname()[1], os.getpid(), Broker.nextSeq) Broker.nextSeq += 1 if self.session.manageConnections: self.thread = ManagedConnection(self) diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 46c746c0f9..2fe85dbdf3 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -29,6 +29,7 @@ import re import socket import struct import os +import platform import locale from qpid.management import managementChannel, managementClient from threading import Lock @@ -202,7 +203,7 @@ class ManagementData: self.lastUnit = None self.methodSeq = 1 self.methodsPending = {} - self.sessionId = "%s.%d" % (os.uname()[1], os.getpid()) + self.sessionId = "%s.%d" % (platform.uname()[1], os.getpid()) self.broker = Broker (host) self.conn = Connection (connect (self.broker.host, self.broker.port), -- cgit v1.2.1 From fcc6dc5cd355cb0fd0340c2bcf095c8d055d1cb5 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Wed, 25 Feb 2009 15:46:04 +0000 Subject: Fix for bug in credit reallocation, along with test from rafaels@redhat.com that reproduces it. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@747833 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/tests_0-10/message.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index cbcef5602f..f2e2dc88ee 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -462,6 +462,42 @@ class MessageTests(TestBase010): self.assertDataEquals(session, q.get(timeout = 1), "abcdefgh") self.assertEmpty(q) + def test_window_flush_ack_flow(self): + """ + Test basic window based flow control with unit = bytes + """ + #declare an exclusive queue + ssn = self.session + ssn.queue_declare(queue = "q", exclusive=True, auto_delete=True) + #create consumer + ssn.message_subscribe(queue = "q", destination = "c", + accept_mode=ssn.accept_mode.explicit) + ssn.message_set_flow_mode(flow_mode = ssn.flow_mode.window, destination = "c") + + #send message A + ssn.message_transfer(message=Message(ssn.delivery_properties(routing_key="q"), "A")) + + for unit in ssn.credit_unit.values(): + ssn.message_flow("c", unit, 0xFFFFFFFFL) + + q = ssn.incoming("c") + msgA = q.get(timeout=10) + + ssn.message_flush(destination="c") + + # XXX + ssn.receiver._completed.add(msgA.id) + ssn.channel.session_completed(ssn.receiver._completed) + ssn.message_accept(RangedSet(msgA.id)) + + for unit in ssn.credit_unit.values(): + ssn.message_flow("c", unit, 0xFFFFFFFFL) + + #send message B + ssn.message_transfer(message=Message(ssn.delivery_properties(routing_key="q"), "B")) + + msgB = q.get(timeout=10) + def test_subscribe_not_acquired(self): """ Test the not-acquired modes works as expected for a simple case -- cgit v1.2.1 From fac33a9073284b701afa8d8aec0a2f15bb653805 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Wed, 25 Feb 2009 19:41:17 +0000 Subject: Added a new utility for viewing broker stats. Fixed a bug in qpid-cluster that causes failure when username/password are included in the broker URL. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@747897 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-cluster | 3 + qpid/python/commands/qpid-stat | 385 ++++++++++++++++++++++++++++++++++++++ qpid/python/qpid/disp.py | 29 ++- 3 files changed, 414 insertions(+), 3 deletions(-) create mode 100755 qpid/python/commands/qpid-stat (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-cluster b/qpid/python/commands/qpid-cluster index 2928ba65ea..4bc40e5abc 100755 --- a/qpid/python/commands/qpid-cluster +++ b/qpid/python/commands/qpid-cluster @@ -56,6 +56,9 @@ def Usage (): class IpAddr: def __init__(self, text): + if text.find("@") != -1: + tokens = text.split("@") + text = tokens[1] if text.find(":") != -1: tokens = text.split(":") text = tokens[0] diff --git a/qpid/python/commands/qpid-stat b/qpid/python/commands/qpid-stat new file mode 100755 index 0000000000..2ac0cad9af --- /dev/null +++ b/qpid/python/commands/qpid-stat @@ -0,0 +1,385 @@ +#!/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 os +import getopt +import sys +import locale +import socket +import re +from qmf.console import Session, Console +from qpid.disp import Display + +_host = "localhost" +_top = False +_types = "" +pattern = re.compile("^\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+$") + +def Usage (): + print "Usage: qpid-stat [OPTIONS] [broker-addr]" + print + print " broker-addr is in the form: [username/password@] hostname | ip-address [:]" + print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" + print +# print "General Options:" +# print " -n [--numeric] Don't resolve names" +# print " -t [--top] Repeatedly display top items" +# print + print "Display Options:" + print + print " -b Show Brokers" + print " -c Show Connections" +# print " -s Show Sessions" +# print " -e Show Exchanges" +# print " -q Show Queues" + print + sys.exit (1) + +def num(value): + if value < 2000: + return str(value) + value /= 1000 + if value < 2000: + return str(value) + "k" + value /= 1000 + if value < 2000: + return str(value) + "m" + value /= 1000 + return str(value) + "g" + +class IpAddr: + def __init__(self, text): + if text.find("@") != -1: + tokens = text.split("@") + text = tokens[1] + if text.find(":") != -1: + tokens = text.split(":") + text = tokens[0] + self.port = int(tokens[1]) + else: + self.port = 5672 + self.dottedQuad = socket.gethostbyname(text) + nums = self.dottedQuad.split(".") + self.addr = (int(nums[0]) << 24) + (int(nums[1]) << 16) + (int(nums[2]) << 8) + int(nums[3]) + + def bestAddr(self, addrPortList): + bestDiff = 0xFFFFFFFF + bestAddr = None + for addrPort in addrPortList: + diff = IpAddr(addrPort[0]).addr ^ self.addr + if diff < bestDiff: + bestDiff = diff + bestAddr = addrPort + return bestAddr + +class Broker(object): + def __init__(self, qmf, broker): + self.broker = broker + list = qmf.getObjects(_class="connection", _package="org.apache.qpid.broker", _broker=broker) + bobj = qmf.getObjects(_class="broker", _package="org.apache.qpid.broker", _broker=broker)[0] + self.currentTime = bobj.getTimestamps()[0] + try: + self.uptime = bobj.uptime + except: + self.uptime = 0 + self.connections = {} + self.sessions = {} + self.exchanges = {} + self.queues = {} + for conn in list: + if pattern.match(conn.address): + self.connections[conn.getObjectId()] = conn + list = qmf.getObjects(_class="session", _package="org.apache.qpid.broker", _broker=broker) + for sess in list: + if sess.connectionRef in self.connections: + self.sessions[sess.getObjectId()] = sess + list = qmf.getObjects(_class="exchange", _package="org.apache.qpid.broker", _broker=broker) + for exchange in list: + self.exchanges[exchange.getObjectId()] = exchange + list = qmf.getObjects(_class="queue", _package="org.apache.qpid.broker", _broker=broker) + for queue in list: + self.queues[queue.getObjectId()] = queue + + def getName(self): + return self.broker.getUrl() + + def getCurrentTime(self): + return self.currentTime + + def getUptime(self): + return self.uptime + +class BrokerManager(Console): + def __init__(self): + self.brokerName = None + self.qmf = None + self.broker = None + self.brokers = [] + self.cluster = None + + def SetBroker(self, brokerUrl): + self.url = brokerUrl + self.qmf = Session() + self.broker = self.qmf.addBroker(brokerUrl) + agents = self.qmf.getAgents() + for a in agents: + if a.getAgentBank() == 0: + self.brokerAgent = a + + def Disconnect(self): + if self.broker: + self.qmf.delBroker(self.broker) + + def _getCluster(self): + packages = self.qmf.getPackages() + if "org.apache.qpid.cluster" not in packages: + return None + + clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) + if len(clusters) == 0: + print "Clustering is installed but not enabled on the broker." + return None + + self.cluster = clusters[0] + + def _getHostList(self, urlList): + hosts = [] + hostAddr = IpAddr(_host) + for url in urlList: + if url.find("amqp:") != 0: + raise Exception("Invalid URL 1") + url = url[5:] + addrs = str(url).split(",") + addrList = [] + for addr in addrs: + tokens = addr.split(":") + if len(tokens) != 3: + raise Exception("Invalid URL 2") + addrList.append((tokens[1], tokens[2])) + + # Find the address in the list that is most likely to be in the same subnet as the address + # with which we made the original QMF connection. This increases the probability that we will + # be able to reach the cluster member. + + best = hostAddr.bestAddr(addrList) + bestUrl = best[0] + ":" + best[1] + hosts.append(bestUrl) + return hosts + + def displaySubs(self, subs, indent, broker=None, conn=None, sess=None, exchange=None, queue=None): + if len(subs) == 0: + return + this = subs[0] + remaining = subs[1:] + newindent = indent + " " + if this == 'b': + pass + elif this == 'c': + if broker: + for oid in broker.connections: + iconn = broker.connections[oid] + self.printConnSub(indent, broker.getName(), iconn) + self.displaySubs(remaining, newindent, broker=broker, conn=iconn, + sess=sess, exchange=exchange, queue=queue) + elif this == 's': + pass + elif this == 'e': + pass + elif this == 'q': + pass + print + + def displayBroker(self, subs): + disp = Display(prefix=" ") + heads = ('Broker', 'cluster', 'uptime', 'conn', 'sess', 'exch', 'queue') + rows = [] + for broker in self.brokers: + if self.cluster: + ctext = "%s(%s)" % (self.cluster.clusterName, self.cluster.status) + else: + ctext = "" + utext = "" + if broker.getUptime() > 0: + utext = disp.duration(broker.getUptime()) + row = (broker.getName(), ctext, utext, + str(len(broker.connections)), str(len(broker.sessions)), + str(len(broker.exchanges)), str(len(broker.queues))) + rows.append(row) + disp.table("Brokers", heads, rows) + + def displayConn(self, subs): + disp = Display(prefix=" ") + heads = [] + if self.cluster: + heads.append('broker') + heads.append('client addr') + heads.append('client(pid)') + heads.append('auth') + heads.append('connected') + heads.append('idle') + heads.append('msgIn') + heads.append('msgOut') + rows = [] + for broker in self.brokers: + for oid in broker.connections: + conn = broker.connections[oid] + row = [] + if self.cluster: + row.append(broker.getName()) + row.append(conn.address) + procpid = "" + if conn.remoteProcessName: + procpid += conn.remoteProcessName + if conn.remotePid: + procpid += "(%d)" % conn.remotePid + row.append(procpid) + row.append(conn.authIdentity) + row.append(disp.duration(broker.getCurrentTime() - conn.getTimestamps()[1])) + idle = broker.getCurrentTime() - conn.getTimestamps()[0] + if idle < 10000000000: + itext = "" + else: + itext = disp.duration(idle) + row.append(itext) + row.append(num(conn.framesFromClient)) + row.append(num(conn.framesToClient)) + rows.append(row) + title = "Connections" + if self.cluster: + title += " for cluster '%s'" % self.cluster.clusterName + disp.table(title, heads, rows) + + def displaySession(self, subs): + disp = Display(prefix=" ") + + def displayExchange(self, subs): + disp = Display(prefix=" ") + heads = [] + if self.cluster: + heads.append('broker') + heads.append("exchange") + heads.append("type") + heads.append("dur") + heads.append("bind") + heads.append("msgIn") + heads.append("msgOut") + heads.append("msgDrop") + heads.append("byteIn") + heads.append("byteOut") + heads.append("byteDrop") + rows = [] + for broker in self.brokers: + for oid in broker.exchanges: + ex = broker.exchanges[oid] + row = [] + if self.cluster: + row.append(broker.getName()) + if ex.durable: + dur = "Y" + else: + dur = "" + row.append(ex.name) + row.append(ex.type) + row.append(dur) + row.append(num(ex.bindingCount)) + row.append(num(ex.msgReceives)) + row.append(num(ex.msgRoutes)) + row.append(num(ex.msgDrops)) + row.append(num(ex.byteReceives)) + row.append(num(ex.byteRoutes)) + row.append(num(ex.byteDrops)) + rows.append(row) + title = "Exchanges" + if self.cluster: + title += " for cluster '%s'" % self.cluster.clusterName + disp.table(title, heads, rows) + + def displayMain(self, main, subs): + if main == 'b': self.displayBroker(subs) + elif main == 'c': self.displayConn(subs) + elif main == 's': self.displaySession(subs) + elif main == 'e': self.displayExchange(subs) + + def display(self): + self._getCluster() + if self.cluster: + memberList = self.cluster.members.split(";") + hostList = self._getHostList(memberList) + self.qmf.delBroker(self.broker) + self.broker = None + for host in hostList: + b = self.qmf.addBroker(host) + self.brokers.append(Broker(self.qmf, b)) + else: + self.brokers.append(Broker(self.qmf, self.broker)) + + self.displayMain(_types[0], _types[1:]) + + +## +## Main Program +## + +try: + longOpts = ("top", "numeric") + (optlist, encArgs) = getopt.gnu_getopt(sys.argv[1:], "bc", longOpts) +except: + Usage() + +try: + encoding = locale.getpreferredencoding() + cargs = [a.decode(encoding) for a in encArgs] +except: + cargs = encArgs + +for opt in optlist: + if opt[0] == "-t" or opt[0] == "--top": + _top = True + elif opt[0] == "-n" or opt[0] == "--numeric": + _numeric = True + elif len(opt[0]) == 2: + char = opt[0][1] + if "bcseq".find(char) != -1: + _types += char + else: + Usage() + else: + Usage() + +if len(_types) == 0: + Usage() + +nargs = len(cargs) +bm = BrokerManager() + +if nargs == 1: + _host = cargs[0] + +try: + bm.SetBroker(_host) + bm.display() +except KeyboardInterrupt: + print +except Exception,e: + print "Failed:", e.args + sys.exit(1) + +bm.Disconnect() diff --git a/qpid/python/qpid/disp.py b/qpid/python/qpid/disp.py index e46cb33c60..eb55616f32 100644 --- a/qpid/python/qpid/disp.py +++ b/qpid/python/qpid/disp.py @@ -24,13 +24,20 @@ from time import strftime, gmtime class Display: """ Display formatting for QPID Management CLI """ - def __init__ (self): - self.tableSpacing = 2 - self.tablePrefix = " " + def __init__ (self, spacing=2, prefix=" "): + self.tableSpacing = spacing + self.tablePrefix = prefix self.timestampFormat = "%X" def table (self, title, heads, rows): """ Print a formatted table with autosized columns """ + + # Pad the rows to the number of heads + for row in rows: + diff = len(heads) - len(row) + for idx in range(diff): + row.append("") + print title if len (rows) == 0: return @@ -77,3 +84,19 @@ class Display: def timestamp (self, nsec): """ Format a nanosecond-since-the-epoch timestamp for printing """ return strftime (self.timestampFormat, gmtime (nsec / 1000000000)) + + def duration(self, nsec): + if nsec < 0: nsec = 0 + sec = nsec / 1000000000 + min = sec / 60 + hour = min / 60 + day = hour / 24 + result = "" + if day > 0: + result = "%dd " % day + if hour > 0 or result != "": + result += "%dh " % (hour % 24) + if min > 0 or result != "": + result += "%dm " % (min % 60) + result += "%ds" % (sec % 60) + return result -- cgit v1.2.1 From adbb618fa2ae734cc9f87e57ae15df14c77edf77 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 26 Feb 2009 21:26:47 +0000 Subject: Improvements to qpid-stat: Support for exchanges and queues Sorting and limiting Improved table formatting git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@748315 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-stat | 216 ++++++++++++++++++++++++++--------------- qpid/python/qpid/disp.py | 138 +++++++++++++++++++++++++- 2 files changed, 275 insertions(+), 79 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-stat b/qpid/python/commands/qpid-stat index 2ac0cad9af..9d41f4e966 100755 --- a/qpid/python/commands/qpid-stat +++ b/qpid/python/commands/qpid-stat @@ -26,11 +26,14 @@ import locale import socket import re from qmf.console import Session, Console -from qpid.disp import Display +from qpid.disp import Display, Header, Sorter _host = "localhost" _top = False _types = "" +_limit = 50 +_increasing = False +_sortcol = None pattern = re.compile("^\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+$") def Usage (): @@ -48,23 +51,15 @@ def Usage (): print " -b Show Brokers" print " -c Show Connections" # print " -s Show Sessions" -# print " -e Show Exchanges" -# print " -q Show Queues" + print " -e Show Exchanges" + print " -q Show Queues" + print + print " -S [--sort-by] COLNAME Sort by column name" + print " -I [--increasing] Sort by increasing value (default = decreasing)" + print " -L [--limit] NUM Limit output to NUM rows (default = 50)" print sys.exit (1) -def num(value): - if value < 2000: - return str(value) - value /= 1000 - if value < 2000: - return str(value) + "k" - value /= 1000 - if value < 2000: - return str(value) + "m" - value /= 1000 - return str(value) + "g" - class IpAddr: def __init__(self, text): if text.find("@") != -1: @@ -93,7 +88,6 @@ class IpAddr: class Broker(object): def __init__(self, qmf, broker): self.broker = broker - list = qmf.getObjects(_class="connection", _package="org.apache.qpid.broker", _broker=broker) bobj = qmf.getObjects(_class="broker", _package="org.apache.qpid.broker", _broker=broker)[0] self.currentTime = bobj.getTimestamps()[0] try: @@ -104,17 +98,23 @@ class Broker(object): self.sessions = {} self.exchanges = {} self.queues = {} + package = "org.apache.qpid.broker" + + list = qmf.getObjects(_class="connection", _package=package, _broker=broker) for conn in list: if pattern.match(conn.address): self.connections[conn.getObjectId()] = conn - list = qmf.getObjects(_class="session", _package="org.apache.qpid.broker", _broker=broker) + + list = qmf.getObjects(_class="session", _package=package, _broker=broker) for sess in list: if sess.connectionRef in self.connections: self.sessions[sess.getObjectId()] = sess - list = qmf.getObjects(_class="exchange", _package="org.apache.qpid.broker", _broker=broker) + + list = qmf.getObjects(_class="exchange", _package=package, _broker=broker) for exchange in list: self.exchanges[exchange.getObjectId()] = exchange - list = qmf.getObjects(_class="queue", _package="org.apache.qpid.broker", _broker=broker) + + list = qmf.getObjects(_class="queue", _package=package, _broker=broker) for queue in list: self.queues[queue.getObjectId()] = queue @@ -209,34 +209,45 @@ class BrokerManager(Console): def displayBroker(self, subs): disp = Display(prefix=" ") - heads = ('Broker', 'cluster', 'uptime', 'conn', 'sess', 'exch', 'queue') + heads = [] + heads.append(Header('broker')) + heads.append(Header('cluster')) + heads.append(Header('uptime', Header.DURATION)) + heads.append(Header('conn', Header.KMG)) + heads.append(Header('sess', Header.KMG)) + heads.append(Header('exch', Header.KMG)) + heads.append(Header('queue', Header.KMG)) rows = [] for broker in self.brokers: if self.cluster: ctext = "%s(%s)" % (self.cluster.clusterName, self.cluster.status) else: ctext = "" - utext = "" - if broker.getUptime() > 0: - utext = disp.duration(broker.getUptime()) - row = (broker.getName(), ctext, utext, - str(len(broker.connections)), str(len(broker.sessions)), - str(len(broker.exchanges)), str(len(broker.queues))) + row = (broker.getName(), ctext, broker.getUptime(), + len(broker.connections), len(broker.sessions), + len(broker.exchanges), len(broker.queues)) rows.append(row) - disp.table("Brokers", heads, rows) + title = "Brokers" + if _sortcol: + sorter = Sorter(heads, rows, _sortcol, _limit, _increasing) + dispRows = sorter.getSorted() + else: + dispRows = rows + disp.formattedTable(title, heads, dispRows) def displayConn(self, subs): disp = Display(prefix=" ") heads = [] if self.cluster: - heads.append('broker') - heads.append('client addr') - heads.append('client(pid)') - heads.append('auth') - heads.append('connected') - heads.append('idle') - heads.append('msgIn') - heads.append('msgOut') + heads.append(Header('broker')) + heads.append(Header('client-addr')) + heads.append(Header('cproc')) + heads.append(Header('cpid')) + heads.append(Header('auth')) + heads.append(Header('connected', Header.DURATION)) + heads.append(Header('idle', Header.DURATION)) + heads.append(Header('msgIn', Header.KMG)) + heads.append(Header('msgOut', Header.KMG)) rows = [] for broker in self.brokers: for oid in broker.connections: @@ -245,27 +256,24 @@ class BrokerManager(Console): if self.cluster: row.append(broker.getName()) row.append(conn.address) - procpid = "" - if conn.remoteProcessName: - procpid += conn.remoteProcessName - if conn.remotePid: - procpid += "(%d)" % conn.remotePid - row.append(procpid) + row.append(conn.remoteProcessName) + row.append(conn.remotePid) row.append(conn.authIdentity) - row.append(disp.duration(broker.getCurrentTime() - conn.getTimestamps()[1])) + row.append(broker.getCurrentTime() - conn.getTimestamps()[1]) idle = broker.getCurrentTime() - conn.getTimestamps()[0] - if idle < 10000000000: - itext = "" - else: - itext = disp.duration(idle) - row.append(itext) - row.append(num(conn.framesFromClient)) - row.append(num(conn.framesToClient)) + row.append(broker.getCurrentTime() - conn.getTimestamps()[0]) + row.append(conn.framesFromClient) + row.append(conn.framesToClient) rows.append(row) title = "Connections" if self.cluster: title += " for cluster '%s'" % self.cluster.clusterName - disp.table(title, heads, rows) + if _sortcol: + sorter = Sorter(heads, rows, _sortcol, _limit, _increasing) + dispRows = sorter.getSorted() + else: + dispRows = rows + disp.formattedTable(title, heads, dispRows) def displaySession(self, subs): disp = Display(prefix=" ") @@ -274,17 +282,17 @@ class BrokerManager(Console): disp = Display(prefix=" ") heads = [] if self.cluster: - heads.append('broker') - heads.append("exchange") - heads.append("type") - heads.append("dur") - heads.append("bind") - heads.append("msgIn") - heads.append("msgOut") - heads.append("msgDrop") - heads.append("byteIn") - heads.append("byteOut") - heads.append("byteDrop") + heads.append(Header('broker')) + heads.append(Header("exchange")) + heads.append(Header("type")) + heads.append(Header("dur", Header.Y)) + heads.append(Header("bind", Header.KMG)) + heads.append(Header("msgIn", Header.KMG)) + heads.append(Header("msgOut", Header.KMG)) + heads.append(Header("msgDrop", Header.KMG)) + heads.append(Header("byteIn", Header.KMG)) + heads.append(Header("byteOut", Header.KMG)) + heads.append(Header("byteDrop", Header.KMG)) rows = [] for broker in self.brokers: for oid in broker.exchanges: @@ -292,31 +300,80 @@ class BrokerManager(Console): row = [] if self.cluster: row.append(broker.getName()) - if ex.durable: - dur = "Y" - else: - dur = "" row.append(ex.name) row.append(ex.type) - row.append(dur) - row.append(num(ex.bindingCount)) - row.append(num(ex.msgReceives)) - row.append(num(ex.msgRoutes)) - row.append(num(ex.msgDrops)) - row.append(num(ex.byteReceives)) - row.append(num(ex.byteRoutes)) - row.append(num(ex.byteDrops)) + row.append(ex.durable) + row.append(ex.bindingCount) + row.append(ex.msgReceives) + row.append(ex.msgRoutes) + row.append(ex.msgDrops) + row.append(ex.byteReceives) + row.append(ex.byteRoutes) + row.append(ex.byteDrops) rows.append(row) title = "Exchanges" if self.cluster: title += " for cluster '%s'" % self.cluster.clusterName - disp.table(title, heads, rows) + if _sortcol: + sorter = Sorter(heads, rows, _sortcol, _limit, _increasing) + dispRows = sorter.getSorted() + else: + dispRows = rows + disp.formattedTable(title, heads, dispRows) + + def displayQueue(self, subs): + disp = Display(prefix=" ") + heads = [] + if self.cluster: + heads.append(Header('broker')) + heads.append(Header("queue")) + heads.append(Header("dur", Header.Y)) + heads.append(Header("autoDel", Header.Y)) + heads.append(Header("excl", Header.Y)) + heads.append(Header("msg", Header.KMG)) + heads.append(Header("msgIn", Header.KMG)) + heads.append(Header("msgOut", Header.KMG)) + heads.append(Header("bytes", Header.KMG)) + heads.append(Header("bytesIn", Header.KMG)) + heads.append(Header("bytesOut", Header.KMG)) + heads.append(Header("cons", Header.KMG)) + heads.append(Header("bind", Header.KMG)) + rows = [] + for broker in self.brokers: + for oid in broker.queues: + q = broker.queues[oid] + row = [] + if self.cluster: + row.append(broker.getName()) + row.append(q.name) + row.append(q.durable) + row.append(q.autoDelete) + row.append(q.exclusive) + row.append(q.msgDepth) + row.append(q.msgTotalEnqueues) + row.append(q.msgTotalDequeues) + row.append(q.byteDepth) + row.append(q.byteTotalEnqueues) + row.append(q.byteTotalDequeues) + row.append(q.consumerCount) + row.append(q.bindingCount) + rows.append(row) + title = "Queues" + if self.cluster: + title += " for cluster '%s'" % self.cluster.clusterName + if _sortcol: + sorter = Sorter(heads, rows, _sortcol, _limit, _increasing) + dispRows = sorter.getSorted() + else: + dispRows = rows + disp.formattedTable(title, heads, dispRows) def displayMain(self, main, subs): if main == 'b': self.displayBroker(subs) elif main == 'c': self.displayConn(subs) elif main == 's': self.displaySession(subs) elif main == 'e': self.displayExchange(subs) + elif main == 'q': self.displayQueue(subs) def display(self): self._getCluster() @@ -339,8 +396,8 @@ class BrokerManager(Console): ## try: - longOpts = ("top", "numeric") - (optlist, encArgs) = getopt.gnu_getopt(sys.argv[1:], "bc", longOpts) + longOpts = ("top", "numeric", "sort-by=", "limit=", "increasing") + (optlist, encArgs) = getopt.gnu_getopt(sys.argv[1:], "bceqS:L:I", longOpts) except: Usage() @@ -355,6 +412,12 @@ for opt in optlist: _top = True elif opt[0] == "-n" or opt[0] == "--numeric": _numeric = True + elif opt[0] == "-S" or opt[0] == "--sort-by": + _sortcol = opt[1] + elif opt[0] == "-I" or opt[0] == "--increasing": + _increasing = True + elif opt[0] == "-L" or opt[0] == "--limit": + _limit = int(opt[1]) elif len(opt[0]) == 2: char = opt[0][1] if "bcseq".find(char) != -1: @@ -380,6 +443,7 @@ except KeyboardInterrupt: print except Exception,e: print "Failed:", e.args + #raise # TODO: Remove before flight sys.exit(1) bm.Disconnect() diff --git a/qpid/python/qpid/disp.py b/qpid/python/qpid/disp.py index eb55616f32..1b315c9d98 100644 --- a/qpid/python/qpid/disp.py +++ b/qpid/python/qpid/disp.py @@ -21,16 +21,108 @@ from time import strftime, gmtime +class Header: + """ """ + NONE = 1 + KMG = 2 + YN = 3 + Y = 4 + TIME_LONG = 5 + TIME_SHORT = 6 + DURATION = 7 + + def __init__(self, text, format=NONE): + self.text = text + self.format = format + + def __repr__(self): + return self.text + + def __str__(self): + return self.text + + def formatted(self, value): + try: + if value == None: + return '' + if self.format == Header.NONE: + return value + if self.format == Header.KMG: + return self.num(value) + if self.format == Header.YN: + if value: + return 'Y' + return 'N' + if self.format == Header.Y: + if value: + return 'Y' + return '' + if self.format == Header.TIME_LONG: + return strftime("%c", gmtime(value / 1000000000)) + if self.format == Header.TIME_SHORT: + return strftime("%X", gmtime(value / 1000000000)) + if self.format == Header.DURATION: + if value < 0: value = 0 + sec = value / 1000000000 + min = sec / 60 + hour = min / 60 + day = hour / 24 + result = "" + if day > 0: + result = "%dd " % day + if hour > 0 or result != "": + result += "%dh " % (hour % 24) + if min > 0 or result != "": + result += "%dm " % (min % 60) + result += "%ds" % (sec % 60) + return result + except: + return "?" + + def numCell(self, value, tag): + fp = float(value) / 1000. + if fp < 10.0: + return "%1.2f%c" % (fp, tag) + if fp < 100.0: + return "%2.1f%c" % (fp, tag) + return "%4d%c" % (value / 1000, tag) + + def num(self, value): + if value < 1000: + return "%4d" % value + if value < 1000000: + return self.numCell(value, 'k') + value /= 1000 + if value < 1000000: + return self.numCell(value, 'm') + value /= 1000 + return self.numCell(value, 'g') + + class Display: """ Display formatting for QPID Management CLI """ - def __init__ (self, spacing=2, prefix=" "): + def __init__(self, spacing=2, prefix=" "): self.tableSpacing = spacing self.tablePrefix = prefix self.timestampFormat = "%X" - def table (self, title, heads, rows): - """ Print a formatted table with autosized columns """ + def formattedTable(self, title, heads, rows): + fRows = [] + for row in rows: + fRow = [] + col = 0 + for cell in row: + fRow.append(heads[col].formatted(cell)) + col += 1 + fRows.append(fRow) + headtext = [] + for head in heads: + headtext.append(head.text) + self.table(title, headtext, fRows) + + def table(self, title, heads, rows): + """ Print a table with autosized columns """ # Pad the rows to the number of heads for row in rows: @@ -100,3 +192,43 @@ class Display: result += "%dm " % (min % 60) result += "%ds" % (sec % 60) return result + +class Sortable: + """ """ + def __init__(self, row, sortIndex): + self.row = row + self.sortIndex = sortIndex + if sortIndex >= len(row): + raise Exception("sort index exceeds row boundary") + + def __cmp__(self, other): + return cmp(self.row[self.sortIndex], other.row[self.sortIndex]) + + def getRow(self): + return self.row + +class Sorter: + """ """ + def __init__(self, heads, rows, sortCol, limit=0, inc=True): + col = 0 + for head in heads: + if head.text == sortCol: + break + col += 1 + if col == len(heads): + raise Exception("sortCol '%s', not found in headers" % sortCol) + + list = [] + for row in rows: + list.append(Sortable(row, col)) + list.sort(reverse=not inc) + count = 0 + self.sorted = [] + for row in list: + self.sorted.append(row.getRow()) + count += 1 + if count == limit: + break + + def getSorted(self): + return self.sorted -- cgit v1.2.1 From bd21b0fc3875abe944a8f4ff980d29b1906a4f02 Mon Sep 17 00:00:00 2001 From: Gordon Sim Date: Mon, 2 Mar 2009 13:27:16 +0000 Subject: Typo in printing of 'generate events' option on queue. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@749300 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index a7d537edb5..4a6129a153 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -211,7 +211,7 @@ class BrokerManager: if POLICY_TYPE in args: print "--limit-policy=%s" % args[POLICY_TYPE].replace("_", "-"), if LVQ in args and args[LVQ] == 1: print "--order lvq", if LVQNB in args and args[LVQNB] == 1: print "--order lvq-no-browse", - if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%d" % args[GENERATE_QUEUE_EVENTS], + if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%d" % args[QUEUE_EVENT_GENERATION], print def QueueListRecurse (self, filter): -- cgit v1.2.1 From a7bb158ab88b8d23337444cd77015d8e602d3d30 Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 6 Mar 2009 15:03:00 +0000 Subject: codec and unicode tests and fixes git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@750934 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 19 +++++++++++++------ qpid/python/qpid/datatypes.py | 3 ++- qpid/python/qpid/spec010.py | 40 +++++++++++++++++++++++++++------------- qpid/python/tests/codec010.py | 16 ++++++++++++++-- 4 files changed, 56 insertions(+), 22 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index f34025ef17..62b0f6cf2a 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -19,7 +19,7 @@ import datetime from packer import Packer -from datatypes import serial, timestamp, RangedSet, Struct +from datatypes import serial, timestamp, RangedSet, Struct, UUID class CodecException(Exception): pass @@ -131,6 +131,11 @@ class Codec(Packer): def write_str16(self, s): self.write_vbin16(s.encode("utf8")) + def read_str16_latin(self): + return self.read_vbin16().decode("iso-8859-15") + def write_str16_latin(self, s): + self.write_vbin16(s.encode("iso-8859-15")) + def read_vbin16(self): return self.read(self.read_uint16()) @@ -166,7 +171,7 @@ class Codec(Packer): if m is not None: sc.write_uint32(len(m)) for k, v in m.items(): - type = self.spec.encoding(v.__class__) + type = self.spec.encoding(v) if type == None: raise CodecException("no encoding for %s" % v.__class__) sc.write_str8(k) @@ -191,9 +196,9 @@ class Codec(Packer): sc = StringCodec(self.spec) if a is not None: if len(a) > 0: - type = self.spec.encoding(a[0].__class__) + type = self.spec.encoding(a[0]) else: - type = self.spec.encoding(None.__class__) + type = self.spec.encoding(None) sc.write_uint8(type.code) sc.write_uint32(len(a)) for o in a: @@ -216,7 +221,7 @@ class Codec(Packer): if l is not None: sc.write_uint32(len(l)) for o in l: - type = self.spec.encoding(o.__class__) + type = self.spec.encoding(o) sc.write_uint8(type.code) type.encode(sc, o) self.write_vbin32(sc.encoded) @@ -273,9 +278,11 @@ class Codec(Packer): getattr(self, attr)(n) def read_uuid(self): - return self.unpack("16s") + return UUID(self.unpack("16s")) def write_uuid(self, s): + if isinstance(s, UUID): + s = s.bytes self.pack("16s", s) def read_bin128(self): diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index eb1f86b0b0..532995e051 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -289,7 +289,8 @@ class UUID: def __cmp__(self, other): if isinstance(other, UUID): return cmp(self.bytes, other.bytes) - raise NotImplemented() + else: + return -1 def __str__(self): return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack("!LHHHHL", self.bytes) diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index cbc85a5e8b..9a88b88169 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -467,19 +467,30 @@ class Exception(Named, Node): node.exceptions.append(self) Node.register(self) +def direct(t): + return lambda x: t + +def map_str(s): + for c in s: + if ord(c) >= 0x80: + return "vbin16" + return "str16_latin" + class Spec(Node): ENCODINGS = { - basestring: "vbin16", - int: "int64", - long: "int64", - float: "float", - None.__class__: "void", - list: "list", - tuple: "list", - dict: "map", - datatypes.timestamp: "datetime", - datetime.datetime: "datetime" + unicode: direct("str16"), + str: map_str, + int: direct("int64"), + long: direct("int64"), + float: direct("float"), + None.__class__: direct("void"), + list: direct("list"), + tuple: direct("list"), + dict: direct("map"), + datatypes.timestamp: direct("datetime"), + datetime.datetime: direct("datetime"), + datatypes.UUID: direct("uuid") } def __init__(self, major, minor, port, children): @@ -500,11 +511,14 @@ class Spec(Node): self.structs_by_name = {} self.enums = {} - def encoding(self, klass): + def encoding(self, obj): + return self._encoding(obj.__class__, obj) + + def _encoding(self, klass, obj): if Spec.ENCODINGS.has_key(klass): - return self.named[Spec.ENCODINGS[klass]] + return self.named[Spec.ENCODINGS[klass](obj)] for base in klass.__bases__: - result = self.encoding(base) + result = self._encoding(base, obj) if result != None: return result diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index 1912eac591..099734a040 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -23,7 +23,7 @@ from unittest import TestCase from qpid.spec010 import load from qpid.codec010 import StringCodec from qpid.testlib import testrunner -from qpid.datatypes import timestamp +from qpid.datatypes import timestamp, uuid4 class CodecTest(TestCase): @@ -42,6 +42,12 @@ class CodecTest(TestCase): def testMapString(self): self.check("map", {"string": "this is a test"}) + def testMapUnicode(self): + self.check("map", {"unicode": u"this is a unicode test"}) + + def testMapBinary(self): + self.check("map", {"binary": "\x7f\xb4R^\xe5\xf0:\x89\x96E1\xf6\xfe\xb9\x1b\xf5"}) + def testMapInt(self): self.check("map", {"int": 3}) @@ -68,14 +74,20 @@ class CodecTest(TestCase): def testMapList(self): self.check("map", {"list": [1, "two", 3.0, -4]}) + def testMapUUID(self): + self.check("map", {"uuid": uuid4()}) + def testMapAll(self): decoded = self.check("map", {"string": "this is a test", + "unicode": u"this is a unicode test", + "binary": "\x7f\xb4R^\xe5\xf0:\x89\x96E1\xf6\xfe\xb9\x1b\xf5", "int": 3, "long": 2**32, "timestamp": timestamp(0), "none": None, "map": {"string": "nested map"}, - "list": [1, "two", 3.0, -4]}) + "list": [1, "two", 3.0, -4], + "uuid": uuid4()}) assert isinstance(decoded["timestamp"], timestamp) def testMapEmpty(self): -- cgit v1.2.1 From 6c5ccd4ee71158861c08c677f8e3c334125d7d2b Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 6 Mar 2009 16:23:14 +0000 Subject: added support for encoding buffers git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@750960 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/codec010.py | 6 ++++++ qpid/python/qpid/spec010.py | 1 + qpid/python/tests/codec010.py | 5 +++++ 3 files changed, 12 insertions(+) (limited to 'qpid/python') diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 62b0f6cf2a..f07362c38d 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -118,6 +118,8 @@ class Codec(Packer): def read_vbin8(self): return self.read(self.read_uint8()) def write_vbin8(self, b): + if isinstance(b, buffer): + b = str(b) self.write_uint8(len(b)) self.write(b) @@ -140,6 +142,8 @@ class Codec(Packer): def read_vbin16(self): return self.read(self.read_uint16()) def write_vbin16(self, b): + if isinstance(b, buffer): + b = str(b) self.write_uint16(len(b)) self.write(b) @@ -163,6 +167,8 @@ class Codec(Packer): def read_vbin32(self): return self.read(self.read_uint32()) def write_vbin32(self, b): + if isinstance(b, buffer): + b = str(b) self.write_uint32(len(b)) self.write(b) diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 9a88b88169..2408d01f55 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -481,6 +481,7 @@ class Spec(Node): ENCODINGS = { unicode: direct("str16"), str: map_str, + buffer: direct("vbin32"), int: direct("int64"), long: direct("int64"), float: direct("float"), diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py index 099734a040..a1f89dc3f4 100644 --- a/qpid/python/tests/codec010.py +++ b/qpid/python/tests/codec010.py @@ -48,6 +48,11 @@ class CodecTest(TestCase): def testMapBinary(self): self.check("map", {"binary": "\x7f\xb4R^\xe5\xf0:\x89\x96E1\xf6\xfe\xb9\x1b\xf5"}) + def testMapBuffer(self): + s = "\x7f\xb4R^\xe5\xf0:\x89\x96E1\xf6\xfe\xb9\x1b\xf5" + dec = self.check("map", {"buffer": buffer(s)}, False) + assert dec["buffer"] == s + def testMapInt(self): self.check("map", {"int": 3}) -- cgit v1.2.1 From fb1021551ca7ca5bf027fad70cc7162f1f007c3a Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Fri, 6 Mar 2009 20:28:04 +0000 Subject: fixed uuid decoding errors due to read_uuid semantics change git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@751064 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 6 +++--- qpid/python/qpid/managementdata.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 39ef157f82..541ba8724f 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -28,7 +28,7 @@ import re from qpid.peer import Closed from qpid.session import SessionDetached from qpid.connection import Connection, ConnectionFailed -from qpid.datatypes import UUID, uuid4, Message, RangedSet +from qpid.datatypes import Message, RangedSet from qpid.util import connect, ssl, URL from qpid.codec010 import StringCodec as Codec from threading import Lock, Condition, Thread @@ -415,7 +415,7 @@ class Session: self.console.brokerDisconnected(broker) def _handleBrokerResp(self, broker, codec, seq): - broker.brokerId = UUID(codec.read_uuid()) + broker.brokerId = codec.read_uuid() if self.console != None: self.console.brokerInfo(broker) @@ -616,7 +616,7 @@ class Session: elif typecode == 11: data = codec.read_uint8() != 0 # BOOL elif typecode == 12: data = codec.read_float() # FLOAT elif typecode == 13: data = codec.read_double() # DOUBLE - elif typecode == 14: data = UUID(codec.read_uuid()) # UUID + elif typecode == 14: data = codec.read_uuid() # UUID elif typecode == 15: data = codec.read_map() # FTABLE elif typecode == 16: data = codec.read_int8() # S8 elif typecode == 17: data = codec.read_int16() # S16 diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index 2fe85dbdf3..e1fd8d54eb 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -263,7 +263,7 @@ class ManagementData: else: return "True" elif typecode == 14: - return "%08x-%04x-%04x-%04x-%04x%08x" % struct.unpack ("!LHHHHL", value) + return str (value) elif typecode == 15: return str (value) return "*type-error*" -- cgit v1.2.1 From bb129a7e588be295cca85498041ba28c7cf855ad Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 10 Mar 2009 17:37:33 +0000 Subject: changed regular string encoding from str16_latin to str16 for backwards compatibility git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@752177 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qpid/spec010.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'qpid/python') diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 2408d01f55..13a64d7db6 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -474,7 +474,7 @@ def map_str(s): for c in s: if ord(c) >= 0x80: return "vbin16" - return "str16_latin" + return "str16" class Spec(Node): -- cgit v1.2.1 From 71eea97e76057db06a69368053fc4af1c19facad Mon Sep 17 00:00:00 2001 From: Martin Ritchie Date: Wed, 11 Mar 2009 17:20:22 +0000 Subject: Update versions to 0.5 git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@752533 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/setup.py b/qpid/python/setup.py index a49fa6ca51..069cb14a3c 100644 --- a/qpid/python/setup.py +++ b/qpid/python/setup.py @@ -19,7 +19,7 @@ # from distutils.core import setup -setup(name="qpid", version="0.1", packages=["qpid"], scripts=["amqp-doc"], - url="http://incubator.apache.org/qpid", +setup(name="qpid", version="0.5", packages=["qpid"], scripts=["amqp-doc"], + url="http://qpid.apache.org/", license="Apache Software License", description="Python language client implementation for Apache Qpid") -- cgit v1.2.1 From 6a644249ba25e38dfa6852dac3377e62aa7386df Mon Sep 17 00:00:00 2001 From: Jonathan Robie Date: Thu, 12 Mar 2009 18:59:07 +0000 Subject: Tests Unicode and handling of all datatypes in application headers. Things commented out don't work yet. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@752972 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/datatypes/client.py | 122 +++++++++++++++++++ qpid/python/examples/datatypes/server.py | 124 ++++++++++++++++++++ qpid/python/examples/datatypes/testdata.py | 180 +++++++++++++++++++++++++++++ qpid/python/examples/datatypes/verify | 24 ++++ 4 files changed, 450 insertions(+) create mode 100755 qpid/python/examples/datatypes/client.py create mode 100755 qpid/python/examples/datatypes/server.py create mode 100644 qpid/python/examples/datatypes/testdata.py create mode 100644 qpid/python/examples/datatypes/verify (limited to 'qpid/python') diff --git a/qpid/python/examples/datatypes/client.py b/qpid/python/examples/datatypes/client.py new file mode 100755 index 0000000000..088e529909 --- /dev/null +++ b/qpid/python/examples/datatypes/client.py @@ -0,0 +1,122 @@ +#!/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. +# +""" + client.py + + Client for testing use of Unicode and datatypes. + + Both client and server will be written in C++ and Python. + Tests can run clients and servers written in different + languages, and they can be run on 32-bit and 64-bit architectures. + +""" + +import qpid +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +import testdata + +#----- Initialization -------------------------------------- + + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +# Create a connection. +socket = connect(host, port) +connection = Connection (sock=socket, username=user, password=password) +connection.start() +session = connection.session(str(uuid4())) + + +#----- Main Body -- ---------------------------------------- + +# Create a response queue for the server to send responses to. Use the +# same string as the name of the queue and the name of the routing +# key. + +reply_to = "reply_to:" + session.name +session.queue_declare(queue=reply_to, exclusive=True) +session.exchange_bind(exchange="amq.direct", queue=reply_to, binding_key=reply_to) + +# Create a local queue and subscribe it to the response queue + +local_queue_name = "local_queue" +queue = session.incoming(local_queue_name) + +# Call message_subscribe() to tell the broker to deliver messages from +# the server's reply_to queue to our local client queue. The server +# will start delivering messages as soon as message credit is +# available. + +session.message_subscribe(queue=reply_to, destination=local_queue_name) +queue.start() + +# Set up the properties. Perhaps a few application headers? + +delivery_properties = session.delivery_properties(routing_key="request") + +message_properties = session.message_properties() + +message_properties.content_encoding="text/plain; charset='utf-8'" + +testdata.set_application_headers(message_properties) +message_properties.reply_to = session.reply_to("amq.direct", reply_to) + +# deliver the message - remember to encode the Unicode string! +request = Message(message_properties, delivery_properties, testdata.String_Greek.encode("utf8")) +session.message_transfer(destination="amq.direct", message=request) + +# Now see what messages the server sent to our reply_to queue + +try: + response = queue.get(timeout=10) + content = response.body + session.message_accept(RangedSet(response.id)) + testdata.check_message(response) + print "Response: " + content +except Empty: + print "No more messages!" + exit(1) +except: + print "Unexpected exception!" + exit(1) + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. + +session.close(timeout=10) diff --git a/qpid/python/examples/datatypes/server.py b/qpid/python/examples/datatypes/server.py new file mode 100755 index 0000000000..18e6fa4ad7 --- /dev/null +++ b/qpid/python/examples/datatypes/server.py @@ -0,0 +1,124 @@ +#!/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. +# +""" + server.py + + Server for testing use of Unicode and datatypes. + + Both client and server will be written in C++ and Python. + Tests can run clients and servers written in different + languages, and they can be run on 32-bit and 64-bit architectures. +""" + +import testdata + +import qpid +import sys +import os +from qpid.util import connect +from qpid.connection import Connection +from qpid.datatypes import Message, RangedSet, uuid4 +from qpid.queue import Empty + +#----- Functions ------------------------------------------- +def respond(session, request): + + # The routing key for the response is the request's reply-to + # property. The body for the response is the request's body, + # converted to upper case. + + testdata.check_message(request) + + message_properties = request.get("message_properties") + reply_to = message_properties.reply_to + + testdata.set_application_headers(message_properties) + + if reply_to == None: + raise Exception("This message is missing the 'reply_to' property, which is required") + + delivery_properties = session.delivery_properties(routing_key=reply_to["routing_key"]) + response = Message(delivery_properties, message_properties, testdata.String_Greek.encode("utf8")) + print "Sending response ..." + session.message_transfer(destination=reply_to["exchange"], message=response) + +#----- Initialization -------------------------------------- + + +# Set parameters for login + +host="127.0.0.1" +port=5672 +user="guest" +password="guest" + +# If an alternate host or port has been specified, use that instead +# (this is used in our unit tests) +if len(sys.argv) > 1 : + host=sys.argv[1] +if len(sys.argv) > 2 : + port=int(sys.argv[2]) + +socket = connect(host, port) +connection = Connection (sock=socket, username=user, password=password) +connection.start() +session = connection.session(str(uuid4())) + +#----- Main Body -- ---------------------------------------- + +# Create a request queue and subscribe to it + +session.queue_declare(queue="request", exclusive=True) +session.exchange_bind(exchange="amq.direct", queue="request", binding_key="request") + +local_queue_name = "local_queue" + +session.message_subscribe(queue="request", destination=local_queue_name) + +queue = session.incoming(local_queue_name) +queue.start() + +# Remind the user to start the client program + +print "Request server running - run your client now." +print "(Times out after 100 seconds ...)" +sys.stdout.flush() + +# Respond to each request + +# If we get a message, send it back to the user (as indicated in the +# ReplyTo property) + +while True: + try: + request = queue.get(timeout=100) + session.message_accept(RangedSet(request.id)) + + respond(session, request) + except Empty: + print "No more messages!" + break; + + +#----- Cleanup ------------------------------------------------ + +# Clean up before exiting so there are no open threads. + +session.close(timeout=10) diff --git a/qpid/python/examples/datatypes/testdata.py b/qpid/python/examples/datatypes/testdata.py new file mode 100644 index 0000000000..cdf140d400 --- /dev/null +++ b/qpid/python/examples/datatypes/testdata.py @@ -0,0 +1,180 @@ +# -*- encoding: utf-8 -*- + +from qpid.datatypes import uuid4, timestamp + +#----- Some variables to test boundary conditions on various data types + +void = None +boolean_true = True +boolean_false = False +Uint8_0 = 0 +Uint8_max = 255 +Uint16_0 = 0 +Uint16_max = 65535 +Uint32_0 = 0 +Uint32_max = 4294967295 +Uint64_0 = 0 +Uint64_max = 18446744073709551615 +Int8_min = -128 +Int8_0 = 0 +Int8_max = 127 +Int16_min = -32768 +Int16_0 = 0 +Int16_max = 32767 +Int32_min = -2147483648 +Int32_0 = 0 +Int32_max = 2147483647 +Int64_min = -9223372036854775808 +Int64_0 = 0 +Int64_max = 9223372036854775807 + +Float_pi = 3.14159265 +Float_neg = -1E4 +Float_big = 1267.43233E12 +Float_small = 12.78e-12 +Float_neg0 = -0 +Float_pos0 = 0 +Float_INF = float('inf') +Float_Negative_INF = float('-inf') + +Double_pi = 3.1415926535897932384626433832795 +Double_neg = -1E4 +Double_big = 1267.43233E12 +Double_small = 12.78e-2 +Double_neg0 = -0 +Double_pos0 = 0 +Double_INF = float('inf') +Double_Negative_INF = float('-inf') + +char_1byte = u'0024' # $ +char_2byte = u'00A2' # ¢ +char_3byte = u'20AC' # € +char_4byte = u'10ABCD' + +timestamp = timestamp() + +UUID = uuid4() + +String_Greek = u"ἐξίσταντο δὲ πάντες καὶ διηπόρουν, ἄλλος πρὸς ἄλλον λέγοντες, Τί θέλει τοῦτο εἶναι;" + +String_Empty = "" + +#----- A few functions ---------------------------------------------------------- + +def near_enough(float1, float2, delta): + return abs(float1-float2) < delta + +def set_application_headers(message_properties): + + message_properties.application_headers = {} + message_properties.application_headers["void"] = None + message_properties.application_headers["boolean_true"] = boolean_true + message_properties.application_headers["boolean_false"] = boolean_false + message_properties.application_headers["Uint8_0"] = Uint8_0 + message_properties.application_headers["Uint8_max"] = Uint8_max + message_properties.application_headers["Uint16_0"] = Uint16_0 + message_properties.application_headers["Uint16_max"] = Uint16_max + message_properties.application_headers["Uint32_0"] = Uint32_0 + message_properties.application_headers["Uint32_max"] = Uint32_max + message_properties.application_headers["Uint64_0"] = Uint64_0 +# message_properties.application_headers["Uint64_max"] = Uint64_max + message_properties.application_headers["Int8_min"] = Int8_min + message_properties.application_headers["Int8_0"] = Int8_0 + message_properties.application_headers["Int8_max"] = Int8_max + message_properties.application_headers["Int16_min"] = Int16_min + message_properties.application_headers["Int16_0"] = Int16_0 + message_properties.application_headers["Int16_max"] = Int16_max + message_properties.application_headers["Int32_min"] = Int32_min + message_properties.application_headers["Int32_0"] = Int32_0 + message_properties.application_headers["Int32_max"] = Int32_max + message_properties.application_headers["Int64_min"] = Int64_min + message_properties.application_headers["Int64_0"] = Int64_0 + message_properties.application_headers["Int64_max"] = Int64_max + + message_properties.application_headers["Float_pi"] = Float_pi + message_properties.application_headers["Float_neg"] = Float_neg + message_properties.application_headers["Float_big"] = Float_big + message_properties.application_headers["Float_small"] = Float_small + message_properties.application_headers["Float_neg0"] = Float_neg0 + message_properties.application_headers["Float_pos0"] = Float_pos0 + message_properties.application_headers["Float_INF"] = Float_INF + message_properties.application_headers["Float_Negative_INF"] = Float_Negative_INF + + message_properties.application_headers["Double_pi"] = Double_pi + message_properties.application_headers["Double_neg"] = Double_neg + message_properties.application_headers["Double_big"] = Double_big + message_properties.application_headers["Double_small"] = Double_small + message_properties.application_headers["Double_neg0"] = Double_neg0 + message_properties.application_headers["Double_pos0"] = Double_pos0 + message_properties.application_headers["Double_INF"] = Double_INF + message_properties.application_headers["Double_Negative_INF"] = Double_Negative_INF + + message_properties.application_headers["char_1byte"] = char_1byte + message_properties.application_headers["char_2byte"] = char_2byte + message_properties.application_headers["char_3byte"] = char_3byte + message_properties.application_headers["char_4byte"] = char_4byte + + message_properties.application_headers["timestamp"] = timestamp + message_properties.application_headers["UUID"] = uuid4() + message_properties.application_headers["String_Greek"] = String_Greek + message_properties.application_headers["String_Empty"] = String_Empty + +def check_message(message): + +# message_properties = message.message_properties() + message_properties = message.get("message_properties") + assert message_properties.application_headers["void"] == None + assert message_properties.application_headers["boolean_true"] == boolean_true + assert message_properties.application_headers["boolean_false"] == boolean_false + assert message_properties.application_headers["Uint8_0"] == Uint8_0 + assert message_properties.application_headers["Uint8_max"] == Uint8_max + assert message_properties.application_headers["Uint16_0"] == Uint16_0 + assert message_properties.application_headers["Uint16_max"] == Uint16_max + assert message_properties.application_headers["Uint32_0"] == Uint32_0 + assert message_properties.application_headers["Uint32_max"] == Uint32_max + assert message_properties.application_headers["Uint64_0"] == Uint64_0 +# assert message_properties.application_headers["Uint64_max"] == Uint64_max + assert message_properties.application_headers["Int8_min"] == Int8_min + assert message_properties.application_headers["Int8_0"] == Int8_0 + assert message_properties.application_headers["Int8_max"] == Int8_max + assert message_properties.application_headers["Int16_min"] == Int16_min + assert message_properties.application_headers["Int16_0"] == Int16_0 + assert message_properties.application_headers["Int16_max"] == Int16_max + assert message_properties.application_headers["Int32_min"] == Int32_min + assert message_properties.application_headers["Int32_0"] == Int32_0 + assert message_properties.application_headers["Int32_max"] == Int32_max + assert message_properties.application_headers["Int64_min"] == Int64_min + assert message_properties.application_headers["Int64_0"] == Int64_0 + assert message_properties.application_headers["Int64_max"] == Int64_max + +# Change floating point comparisons to allow inexactness + + assert near_enough(message_properties.application_headers["Float_pi"], Float_pi, 0.00001) + assert near_enough(message_properties.application_headers["Float_neg"], Float_neg, 0.00001) + assert near_enough(message_properties.application_headers["Float_big"], Float_big, Float_big/1000000) + assert near_enough(message_properties.application_headers["Float_small"], Float_small, 0.00001) + assert message_properties.application_headers["Float_neg0"] == Float_neg0 + assert message_properties.application_headers["Float_pos0"] == Float_pos0 + assert message_properties.application_headers["Float_INF"] == Float_INF + assert message_properties.application_headers["Float_Negative_INF"] == Float_Negative_INF + + assert near_enough(message_properties.application_headers["Double_pi"], Double_pi, 0.00001) + assert near_enough(message_properties.application_headers["Double_neg"], Double_neg, 0.00001) + assert near_enough(message_properties.application_headers["Double_big"], Double_big, Double_big/1000000) + assert near_enough(message_properties.application_headers["Double_small"], Double_small, 0.00001) + assert message_properties.application_headers["Double_neg0"] == Double_neg0 + assert message_properties.application_headers["Double_pos0"] == Double_pos0 + assert message_properties.application_headers["Double_INF"] == Double_INF + assert message_properties.application_headers["Double_Negative_INF"] == Double_Negative_INF + + assert message_properties.application_headers["char_1byte"] == char_1byte + assert message_properties.application_headers["char_2byte"] == char_2byte + assert message_properties.application_headers["char_3byte"] == char_3byte + assert message_properties.application_headers["char_4byte"] == char_4byte + +# assert message_properties.application_headers["timestamp"] == timestamp +# assert message_properties.application_headers["UUID"] == UUID + assert message_properties.application_headers["String_Greek"] == String_Greek + assert message_properties.application_headers["String_Empty"] == String_Empty + + diff --git a/qpid/python/examples/datatypes/verify b/qpid/python/examples/datatypes/verify new file mode 100644 index 0000000000..cdc41b454e --- /dev/null +++ b/qpid/python/examples/datatypes/verify @@ -0,0 +1,24 @@ +# +# 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. +# + +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Request server running" ./server.py +clients ./client.py +kill %% # Must kill the server. +outputs "./client.py.out" " server.py.out" -- cgit v1.2.1 From 32d5fef50b5995378abb6459133460ffddf66ea4 Mon Sep 17 00:00:00 2001 From: Jonathan Robie Date: Fri, 13 Mar 2009 20:20:29 +0000 Subject: Removing the verify script - it breaks builds. Will restore when fixed. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@753381 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/examples/datatypes/verify | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 qpid/python/examples/datatypes/verify (limited to 'qpid/python') diff --git a/qpid/python/examples/datatypes/verify b/qpid/python/examples/datatypes/verify deleted file mode 100644 index cdc41b454e..0000000000 --- a/qpid/python/examples/datatypes/verify +++ /dev/null @@ -1,24 +0,0 @@ -# -# 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. -# - -# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify -background "Request server running" ./server.py -clients ./client.py -kill %% # Must kill the server. -outputs "./client.py.out" " server.py.out" -- cgit v1.2.1 From c5e1410fbbde5cd7f404f85821382d2ccdfce58e Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Thu, 19 Mar 2009 14:45:21 +0000 Subject: QPID-1758 Applied qpid-config patch from Ffrench Mathilde. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@756033 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-config | 53 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 4a6129a153..144fe36953 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -27,8 +27,12 @@ from qmf.console import Session _recursive = False _host = "localhost" +_altern_ex = None +_passive = False _durable = False _clusterDurable = False +_if_empty = True +_if_unused = True _fileCount = 8 _fileSize = 24 _maxQueueSize = None @@ -58,7 +62,7 @@ def Usage (): print " qpid-config [OPTIONS] add exchange [AddExchangeOptions]" print " qpid-config [OPTIONS] del exchange " print " qpid-config [OPTIONS] add queue [AddQueueOptions]" - print " qpid-config [OPTIONS] del queue " + print " qpid-config [OPTIONS] del queue [DelQueueOptions]" print " qpid-config [OPTIONS] bind [binding-key]" print " qpid-config [OPTIONS] unbind [binding-key]" print @@ -69,6 +73,13 @@ def Usage (): print " ex: localhost, 10.1.1.7:10000, broker-host:10000, guest/guest@localhost" print print "Add Queue Options:" + print " --altern-ex [name of the alternate exchange]" + print " The alternate-exchange field specifies how messages on this queue should" + print " be treated when they are rejected by a subscriber, or when they are" + print " orphaned by queue deletion. When present, rejected or orphaned messages" + print " MUST be routed to the alternate-exchange. In all cases the messages MUST" + print " be removed from the queue." + print " --passive Do not actually change the broker state (queue will not be created)" print " --durable Queue is durable" print " --cluster-durable Queue becomes durable if there is only one functioning cluster node" print " --file-count N (8) Number of files in queue's persistence journal" @@ -92,7 +103,20 @@ def Usage (): print " registered listeners (e.g. for replication). If set to 2, events will be" print " generated for enqueues and dequeues" print + print "Del Queue Options:" + print " --force Force delete of queue even if it's currently used or it's not empty" + print " --force-if-not-empty Force delete of queue even if it's not empty" + print " --force-if-used Force delete of queue even if it's currently used" + print print "Add Exchange Options:" + print " --altern-ex [name of the alternate exchange]" + print " In the event that a message cannot be routed, this is the name of the exchange to" + print " which the message will be sent. Messages transferred using message.transfer will" + print " be routed to the alternate-exchange only if they are sent with the \"none\"" + print " accept-mode, and the discard-unroutable delivery property is set to false, and" + print " there is no queue to route to for the given message according to the bindings" + print " on this exchange." + print " --passive Do not actually change teh broker state (exchange will not be created)" print " --durable Exchange is durable" print " --sequence Exchange will insert a 'qpid.msg_sequence' field in the message header" print " with a value that increments for each message forwarded." @@ -241,7 +265,10 @@ class BrokerManager: declArgs[MSG_SEQUENCE] = 1 if _ive: declArgs[IVE] = 1 - self.broker.getAmqpSession().exchange_declare (exchange=ename, type=etype, durable=_durable, arguments=declArgs) + if _altern_ex != None: + self.broker.getAmqpSession().exchange_declare (exchange=ename, type=etype, alternate_exchange=_altern_ex, passive=_passive, durable=_durable, arguments=declArgs) + else: + self.broker.getAmqpSession().exchange_declare (exchange=ename, type=etype, passive=_passive, durable=_durable, arguments=declArgs) def DelExchange (self, args): if len (args) < 1: @@ -286,13 +313,16 @@ class BrokerManager: if _eventGeneration: declArgs[QUEUE_EVENT_GENERATION] = _eventGeneration - self.broker.getAmqpSession().queue_declare (queue=qname, durable=_durable, arguments=declArgs) + if _altern_ex != None: + self.broker.getAmqpSession().queue_declare (queue=qname, alternate_exchange=_altern_ex, passive=_passive, durable=_durable, arguments=declArgs) + else: + self.broker.getAmqpSession().queue_declare (queue=qname, passive=_passive, durable=_durable, arguments=declArgs) def DelQueue (self, args): if len (args) < 1: Usage () qname = args[0] - self.broker.getAmqpSession().queue_delete (queue=qname) + self.broker.getAmqpSession().queue_delete (queue=qname, if_empty=_if_empty, if_unused=_if_unused) def Bind (self, args): if len (args) < 2: @@ -340,7 +370,8 @@ def YN (bool): try: longOpts = ("durable", "cluster-durable", "bindings", "broker-addr=", "file-count=", "file-size=", "max-queue-size=", "max-queue-count=", "limit-policy=", - "order=", "sequence", "ive", "generate-queue-events=") + "order=", "sequence", "ive", "generate-queue-events=", "force", "force-if-not-empty", + "force_if_used", "altern_ex=", "passive") (optlist, encArgs) = getopt.gnu_getopt (sys.argv[1:], "a:b", longOpts) except: Usage () @@ -356,6 +387,10 @@ for opt in optlist: _recursive = True if opt[0] == "-a" or opt[0] == "--broker-addr": _host = opt[1] + if opt[0] == "--altern-ex": + _altern_ex = opt[1] + if opt[0] == "--passive": + _passive = True if opt[0] == "--durable": _durable = True if opt[0] == "--cluster-durable": @@ -384,6 +419,14 @@ for opt in optlist: _ive = True if opt[0] == "--generate-queue-events": _eventGeneration = int (opt[1]) + if opt[0] == "--force": + _if_empty = False + _if_unused = False + if opt[0] == "--force-if-not-empty": + _if_empty = False + if opt[0] == "--force-if-used": + _if_unused = False + nargs = len (cargs) bm = BrokerManager () -- cgit v1.2.1 From af4d1bb1ec4a47a927aa9cf3d7fbad7491e99dae Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Wed, 25 Mar 2009 18:22:55 +0000 Subject: use longs for large hex constant literals, this avoids scary looking warnings on earlier versions of python git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@758389 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/commands/qpid-cluster | 2 +- qpid/python/commands/qpid-stat | 2 +- qpid/python/perftest | 8 +-- qpid/python/qmf/console.py | 8 +-- qpid/python/qpid/datatypes.py | 6 +-- qpid/python/qpid/management.py | 8 +-- qpid/python/qpid/peer.py | 2 +- qpid/python/qpid/spec010.py | 2 +- qpid/python/qpid/testlib.py | 12 ++--- qpid/python/tests/datatypes.py | 10 ++-- qpid/python/tests_0-10/alternate_exchange.py | 12 ++--- qpid/python/tests_0-10/broker.py | 16 +++--- qpid/python/tests_0-10/dtx.py | 6 +-- qpid/python/tests_0-10/example.py | 4 +- qpid/python/tests_0-10/exchange.py | 4 +- qpid/python/tests_0-10/management.py | 4 +- qpid/python/tests_0-10/message.py | 76 ++++++++++++++-------------- qpid/python/tests_0-10/persistence.py | 2 +- qpid/python/tests_0-10/queue.py | 16 +++--- qpid/python/tests_0-10/tx.py | 8 +-- 20 files changed, 104 insertions(+), 104 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/commands/qpid-cluster b/qpid/python/commands/qpid-cluster index 4bc40e5abc..07fa666041 100755 --- a/qpid/python/commands/qpid-cluster +++ b/qpid/python/commands/qpid-cluster @@ -70,7 +70,7 @@ class IpAddr: self.addr = (int(nums[0]) << 24) + (int(nums[1]) << 16) + (int(nums[2]) << 8) + int(nums[3]) def bestAddr(self, addrPortList): - bestDiff = 0xFFFFFFFF + bestDiff = 0xFFFFFFFFL bestAddr = None for addrPort in addrPortList: diff = IpAddr(addrPort[0]).addr ^ self.addr diff --git a/qpid/python/commands/qpid-stat b/qpid/python/commands/qpid-stat index 9d41f4e966..1f1d247bb1 100755 --- a/qpid/python/commands/qpid-stat +++ b/qpid/python/commands/qpid-stat @@ -76,7 +76,7 @@ class IpAddr: self.addr = (int(nums[0]) << 24) + (int(nums[1]) << 16) + (int(nums[2]) << 8) + int(nums[3]) def bestAddr(self, addrPortList): - bestDiff = 0xFFFFFFFF + bestDiff = 0xFFFFFFFFL bestAddr = None for addrPort in addrPortList: diff = IpAddr(addrPort[0]).addr ^ self.addr diff --git a/qpid/python/perftest b/qpid/python/perftest index f4d3c95e96..f867566fd0 100755 --- a/qpid/python/perftest +++ b/qpid/python/perftest @@ -44,8 +44,8 @@ def publisher(n): consumer = "consumer" queue = client.queue(consumer) channel.message_subscribe(queue="sync_queue", destination=consumer) - channel.message_flow(consumer, 0, 0xFFFFFFFF) - channel.message_flow(consumer, 1, 0xFFFFFFFF) + channel.message_flow(consumer, 0, 0xFFFFFFFFL) + channel.message_flow(consumer, 1, 0xFFFFFFFFL) queue.get(block = True) print "done" channel.session_close() @@ -62,8 +62,8 @@ def consumer(): consumer = "consumer" queue = client.queue(consumer) channel.message_subscribe(queue="message_queue", destination=consumer) - channel.message_flow(consumer, 0, 0xFFFFFFFF) - channel.message_flow(consumer, 1, 0xFFFFFFFF) + channel.message_flow(consumer, 0, 0xFFFFFFFFL) + channel.message_flow(consumer, 1, 0xFFFFFFFFL) final = "That's done" content = "" message = None diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 541ba8724f..3b99595f1f 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -1337,8 +1337,8 @@ class Broker: acquire_mode=self.amqpSession.acquire_mode.pre_acquired) self.amqpSession.incoming("rdest").listen(self._replyCb, self._exceptionCb) self.amqpSession.message_set_flow_mode(destination="rdest", flow_mode=1) - self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFF) - self.amqpSession.message_flow(destination="rdest", unit=1, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="rdest", unit=0, value=0xFFFFFFFFL) + self.amqpSession.message_flow(destination="rdest", unit=1, value=0xFFFFFFFFL) self.topicName = "topic-%s" % self.amqpSessionId self.amqpSession.queue_declare(queue=self.topicName, exclusive=True, auto_delete=True) @@ -1347,8 +1347,8 @@ class Broker: acquire_mode=self.amqpSession.acquire_mode.pre_acquired) self.amqpSession.incoming("tdest").listen(self._replyCb) self.amqpSession.message_set_flow_mode(destination="tdest", flow_mode=1) - self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFF) - self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFF) + self.amqpSession.message_flow(destination="tdest", unit=0, value=0xFFFFFFFFL) + self.amqpSession.message_flow(destination="tdest", unit=1, value=0xFFFFFFFFL) self.connected = True self.session._handleBrokerConnect(self) diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py index 532995e051..b2dcbe74ab 100644 --- a/qpid/python/qpid/datatypes.py +++ b/qpid/python/qpid/datatypes.py @@ -125,7 +125,7 @@ def serial(o): class Serial: def __init__(self, value): - self.value = value & 0xFFFFFFFF + self.value = value & 0xFFFFFFFFL def __hash__(self): return hash(self.value) @@ -136,8 +136,8 @@ class Serial: other = serial(other) - delta = (self.value - other.value) & 0xFFFFFFFF - neg = delta & 0x80000000 + delta = (self.value - other.value) & 0xFFFFFFFFL + neg = delta & 0x80000000L mag = delta & 0x7FFFFFFF if neg: diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 477f3e8f2b..546e68ae8e 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -177,12 +177,12 @@ class managementChannel: ssn.incoming ("rdest").listen (self.replyCb) ssn.message_set_flow_mode (destination="tdest", flow_mode=1) - ssn.message_flow (destination="tdest", unit=0, value=0xFFFFFFFF) - ssn.message_flow (destination="tdest", unit=1, value=0xFFFFFFFF) + ssn.message_flow (destination="tdest", unit=0, value=0xFFFFFFFFL) + ssn.message_flow (destination="tdest", unit=1, value=0xFFFFFFFFL) ssn.message_set_flow_mode (destination="rdest", flow_mode=1) - ssn.message_flow (destination="rdest", unit=0, value=0xFFFFFFFF) - ssn.message_flow (destination="rdest", unit=1, value=0xFFFFFFFF) + ssn.message_flow (destination="rdest", unit=0, value=0xFFFFFFFFL) + ssn.message_flow (destination="rdest", unit=1, value=0xFFFFFFFFL) def setBrokerInfo (self, data): self.brokerInfo = data diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py index 648f32ceef..18d7848b8d 100644 --- a/qpid/python/qpid/peer.py +++ b/qpid/python/qpid/peer.py @@ -460,6 +460,6 @@ class IncomingCompletion: #TODO: record and manage the ranges properly range = [mark, mark] if (self.mark == -1):#hack until wraparound is implemented - self.channel.execution_complete(cumulative_execution_mark=0xFFFFFFFF, ranged_execution_set=range) + self.channel.execution_complete(cumulative_execution_mark=0xFFFFFFFFL, ranged_execution_set=range) else: self.channel.execution_complete(cumulative_execution_mark=self.mark, ranged_execution_set=range) diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py index 13a64d7db6..eabc8e2983 100644 --- a/qpid/python/qpid/spec010.py +++ b/qpid/python/qpid/spec010.py @@ -484,7 +484,7 @@ class Spec(Node): buffer: direct("vbin32"), int: direct("int64"), long: direct("int64"), - float: direct("float"), + float: direct("double"), None.__class__: direct("void"), list: direct("list"), tuple: direct("list"), diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py index 31f52169ae..7f5ac1fcd2 100644 --- a/qpid/python/qpid/testlib.py +++ b/qpid/python/qpid/testlib.py @@ -288,16 +288,16 @@ class TestBase(unittest.TestCase): else: self.uniqueTag += 1 consumer_tag = "tag" + str(self.uniqueTag) self.channel.message_subscribe(queue=queueName, destination=consumer_tag) - self.channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) - self.channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + self.channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFFL) + self.channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFFL) return self.client.queue(consumer_tag) def subscribe(self, channel=None, **keys): channel = channel or self.channel consumer_tag = keys["destination"] channel.message_subscribe(**keys) - channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) - channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFFL) + channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFFL) def assertEmpty(self, queue): """Assert that the queue is empty""" @@ -388,5 +388,5 @@ class TestBase010(unittest.TestCase): session = session or self.session consumer_tag = keys["destination"] session.message_subscribe(**keys) - session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFF) - session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFFL) + session.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFFL) diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py index 4b9e1bcc78..e9e09094fa 100644 --- a/qpid/python/tests/datatypes.py +++ b/qpid/python/tests/datatypes.py @@ -25,16 +25,16 @@ from qpid.datatypes import * class SerialTest(TestCase): def test(self): - for s in (serial(0), serial(0x8FFFFFFF), serial(0xFFFFFFFF)): + for s in (serial(0), serial(0x8FFFFFFFL), serial(0xFFFFFFFFL)): assert s + 1 > s assert s - 1 < s assert s < s + 1 assert s > s - 1 - assert serial(0xFFFFFFFF) + 1 == serial(0) + assert serial(0xFFFFFFFFL) + 1 == serial(0) - assert min(serial(0xFFFFFFFF), serial(0x0)) == serial(0xFFFFFFFF) - assert max(serial(0xFFFFFFFF), serial(0x0)) == serial(0x0) + assert min(serial(0xFFFFFFFFL), serial(0x0)) == serial(0xFFFFFFFFL) + assert max(serial(0xFFFFFFFFL), serial(0x0)) == serial(0x0) def testIncr(self): s = serial(0) @@ -44,7 +44,7 @@ class SerialTest(TestCase): def testIn(self): l = [serial(1), serial(2), serial(3), serial(4)] assert serial(1) in l - assert serial(0xFFFFFFFF + 2) in l + assert serial(0xFFFFFFFFL + 2) in l assert 4 in l def testNone(self): diff --git a/qpid/python/tests_0-10/alternate_exchange.py b/qpid/python/tests_0-10/alternate_exchange.py index aac8a5e15b..3b75145907 100644 --- a/qpid/python/tests_0-10/alternate_exchange.py +++ b/qpid/python/tests_0-10/alternate_exchange.py @@ -41,16 +41,16 @@ class AlternateExchangeTests(TestBase010): session.queue_declare(queue="returns", exclusive=True, auto_delete=True) session.exchange_bind(queue="returns", exchange="secondary") session.message_subscribe(destination="a", queue="returns") - session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFFL) returned = session.incoming("a") #declare, bind (to the primary exchange) and consume from a queue for 'processed' messages session.queue_declare(queue="processed", exclusive=True, auto_delete=True) session.exchange_bind(queue="processed", exchange="primary", binding_key="my-key") session.message_subscribe(destination="b", queue="processed") - session.message_flow(destination="b", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="b", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="b", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="b", unit=session.credit_unit.byte, value=0xFFFFFFFFL) processed = session.incoming("b") #publish to the primary exchange @@ -81,8 +81,8 @@ class AlternateExchangeTests(TestBase010): session.queue_declare(queue="deleted", exclusive=True, auto_delete=True) session.exchange_bind(exchange="dlq", queue="deleted") session.message_subscribe(destination="dlq", queue="deleted") - session.message_flow(destination="dlq", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="dlq", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="dlq", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="dlq", unit=session.credit_unit.byte, value=0xFFFFFFFFL) dlq = session.incoming("dlq") #create a queue using the dlq as its alternate exchange: diff --git a/qpid/python/tests_0-10/broker.py b/qpid/python/tests_0-10/broker.py index d4aa57765c..81d723e322 100644 --- a/qpid/python/tests_0-10/broker.py +++ b/qpid/python/tests_0-10/broker.py @@ -36,8 +36,8 @@ class BrokerTests(TestBase010): # No ack consumer ctag = "tag1" session.message_subscribe(queue = "myqueue", destination = ctag) - session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFFL) body = "test no-ack" session.message_transfer(message=Message(session.delivery_properties(routing_key="myqueue"), body)) msg = session.incoming(ctag).get(timeout = 5) @@ -47,8 +47,8 @@ class BrokerTests(TestBase010): session.queue_declare(queue = "otherqueue", exclusive=True, auto_delete=True) ctag = "tag2" session.message_subscribe(queue = "otherqueue", destination = ctag, accept_mode = 1) - session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFFL) body = "test ack" session.message_transfer(message=Message(session.delivery_properties(routing_key="otherqueue"), body)) msg = session.incoming(ctag).get(timeout = 5) @@ -64,8 +64,8 @@ class BrokerTests(TestBase010): session.exchange_bind(queue="test-queue", exchange="amq.fanout") consumer_tag = "tag1" session.message_subscribe(queue="test-queue", destination=consumer_tag) - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = consumer_tag) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = consumer_tag) queue = session.incoming(consumer_tag) body = "Immediate Delivery" @@ -86,8 +86,8 @@ class BrokerTests(TestBase010): consumer_tag = "tag1" session.message_subscribe(queue="test-queue", destination=consumer_tag) - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = consumer_tag) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = consumer_tag) queue = session.incoming(consumer_tag) msg = queue.get(timeout=5) self.assert_(msg.body == body) diff --git a/qpid/python/tests_0-10/dtx.py b/qpid/python/tests_0-10/dtx.py index 25c2defd3b..2823385a3b 100644 --- a/qpid/python/tests_0-10/dtx.py +++ b/qpid/python/tests_0-10/dtx.py @@ -575,7 +575,7 @@ class DtxTests(TestBase010): session2.dtx_start(xid=tx) session2.message_subscribe(queue="dummy", destination="dummy") session2.message_flow(destination="dummy", unit=session2.credit_unit.message, value=1) - session2.message_flow(destination="dummy", unit=session2.credit_unit.byte, value=0xFFFFFFFF) + session2.message_flow(destination="dummy", unit=session2.credit_unit.byte, value=0xFFFFFFFFL) msg = session2.incoming("dummy").get(timeout=1) session2.message_accept(RangedSet(msg.id)) session2.message_cancel(destination="dummy") @@ -736,7 +736,7 @@ class DtxTests(TestBase010): #consume from src: session.message_subscribe(destination="temp-swap", queue=src) session.message_flow(destination="temp-swap", unit=session.credit_unit.message, value=1) - session.message_flow(destination="temp-swap", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="temp-swap", unit=session.credit_unit.byte, value=0xFFFFFFFFL) msg = session.incoming("temp-swap").get(timeout=1) session.message_cancel(destination="temp-swap") session.message_accept(RangedSet(msg.id)) @@ -753,7 +753,7 @@ class DtxTests(TestBase010): def assertMessageId(self, expected, queue): self.session.message_subscribe(queue=queue, destination="results") self.session.message_flow(destination="results", unit=self.session.credit_unit.message, value=1) - self.session.message_flow(destination="results", unit=self.session.credit_unit.byte, value=0xFFFFFFFF) + self.session.message_flow(destination="results", unit=self.session.credit_unit.byte, value=0xFFFFFFFFL) self.assertEqual(expected, self.getMessageProperty(self.session.incoming("results").get(timeout=1), 'correlation_id')) self.session.message_cancel(destination="results") diff --git a/qpid/python/tests_0-10/example.py b/qpid/python/tests_0-10/example.py index 83d208192b..e36907d501 100644 --- a/qpid/python/tests_0-10/example.py +++ b/qpid/python/tests_0-10/example.py @@ -69,8 +69,8 @@ class ExampleTest (TestBase010): # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. session.message_subscribe(queue="test-queue", destination="consumer_tag") - session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFFL) # We can use the session.incoming(...) method to access the messages # delivered for our consumer_tag. diff --git a/qpid/python/tests_0-10/exchange.py b/qpid/python/tests_0-10/exchange.py index 4b5dc78143..738e3c4def 100644 --- a/qpid/python/tests_0-10/exchange.py +++ b/qpid/python/tests_0-10/exchange.py @@ -108,8 +108,8 @@ class TestHelper(TestBase010): else: self.uniqueTag += 1 consumer_tag = "tag" + str(self.uniqueTag) self.session.message_subscribe(queue=queueName, destination=consumer_tag) - self.session.message_flow(destination=consumer_tag, unit=self.session.credit_unit.message, value=0xFFFFFFFF) - self.session.message_flow(destination=consumer_tag, unit=self.session.credit_unit.byte, value=0xFFFFFFFF) + self.session.message_flow(destination=consumer_tag, unit=self.session.credit_unit.message, value=0xFFFFFFFFL) + self.session.message_flow(destination=consumer_tag, unit=self.session.credit_unit.byte, value=0xFFFFFFFFL) return self.session.incoming(consumer_tag) diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py index 0632d85da4..545dc3db3b 100644 --- a/qpid/python/tests_0-10/management.py +++ b/qpid/python/tests_0-10/management.py @@ -192,8 +192,8 @@ class ManagementTest (TestBase010): "Consume the messages of the queue and check they are all there in order" session.message_subscribe(queue="src-queue", destination="tag") - session.message_flow(destination="tag", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="tag", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="tag", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("tag") for count in twenty: consumed_msg = queue.get(timeout=1) diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py index f2e2dc88ee..f80eca6363 100644 --- a/qpid/python/tests_0-10/message.py +++ b/qpid/python/tests_0-10/message.py @@ -230,8 +230,8 @@ class MessageTests(TestBase010): session.message_subscribe(destination="my-consumer", queue="test-queue-4") myqueue = session.incoming("my-consumer") - session.message_flow(destination="my-consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="my-consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="my-consumer", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="my-consumer", unit=session.credit_unit.byte, value=0xFFFFFFFFL) #should flush here @@ -258,8 +258,8 @@ class MessageTests(TestBase010): session.queue_declare(queue="test-ack-queue", auto_delete=True) session.message_subscribe(queue = "test-ack-queue", destination = "consumer") - session.message_flow(destination="consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="consumer", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("consumer") delivery_properties = session.delivery_properties(routing_key="test-ack-queue") @@ -289,8 +289,8 @@ class MessageTests(TestBase010): session.close(timeout=10) session = self.session - session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("checker") msg3b = queue.get(timeout=1) @@ -311,16 +311,16 @@ class MessageTests(TestBase010): session.exchange_bind(queue = "r", exchange = "amq.fanout") session.message_subscribe(queue = "q", destination = "consumer") - session.message_flow(destination="consumer", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="consumer", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="consumer", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="consumer", unit=session.credit_unit.byte, value=0xFFFFFFFFL) session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "blah, blah")) msg = session.incoming("consumer").get(timeout = 1) self.assertEquals(msg.body, "blah, blah") session.message_reject(RangedSet(msg.id)) session.message_subscribe(queue = "r", destination = "checker") - session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFFL) msg = session.incoming("checker").get(timeout = 1) self.assertEquals(msg.body, "blah, blah") @@ -341,7 +341,7 @@ class MessageTests(TestBase010): #set message credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.message, value = 5, destination = "c") #set infinite byte credit - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "c") #check that expected number were received q = session.incoming("c") for i in range(1, 6): @@ -374,7 +374,7 @@ class MessageTests(TestBase010): #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") #set infinite message credit - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = "c") #check that expected number were received q = session.incoming("c") for i in range(5): @@ -405,7 +405,7 @@ class MessageTests(TestBase010): #set message credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.message, value = 5, destination = "c") #set infinite byte credit - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "c") #check that expected number were received q = session.incoming("c") for i in range(1, 6): @@ -443,7 +443,7 @@ class MessageTests(TestBase010): #set byte credit to finite amount (less than enough for all messages) session.message_flow(unit = session.credit_unit.byte, value = msg_size*5, destination = "c") #set infinite message credit - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = "c") #check that expected number were received q = session.incoming("c") msgs = [] @@ -508,11 +508,11 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %s" % i)) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "a") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") session.message_subscribe(queue = "q", destination = "b", acquire_mode = 1) - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "b") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "b") for i in range(6, 11): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "Message %s" % i)) @@ -544,8 +544,8 @@ class MessageTests(TestBase010): session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1, accept_mode = 1) session.message_set_flow_mode(flow_mode = session.flow_mode.credit, destination = "a") - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = "a") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") msg = session.incoming("a").get(timeout = 1) self.assertEquals("acquire me", msg.body) #message should still be on the queue: @@ -568,8 +568,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "acquire me")) session.message_subscribe(queue = "q", destination = "a", acquire_mode = 1) - session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFFL) msg = session.incoming("a").get(timeout = 1) self.assertEquals("acquire me", msg.body) #message should still be on the queue: @@ -594,8 +594,8 @@ class MessageTests(TestBase010): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "release me")) session.message_subscribe(queue = "q", destination = "a") - session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFFL) msg = session.incoming("a").get(timeout = 1) self.assertEquals("release me", msg.body) session.message_cancel(destination = "a") @@ -615,7 +615,7 @@ class MessageTests(TestBase010): session.message_subscribe(queue = "q", destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") queue = session.incoming("a") first = queue.get(timeout = 1) for i in range(2, 10): @@ -648,7 +648,7 @@ class MessageTests(TestBase010): session.message_subscribe(queue = "q", destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") queue = session.incoming("a") ids = [] for i in range (1, 11): @@ -673,8 +673,8 @@ class MessageTests(TestBase010): session.close(timeout=10) session = self.session - session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("checker") self.assertEquals("message 4", queue.get(timeout = 1).body) @@ -692,7 +692,7 @@ class MessageTests(TestBase010): session.message_subscribe(queue = "q", destination = "a") session.message_set_flow_mode(flow_mode = 0, destination = "a") session.message_flow(unit = session.credit_unit.message, value = 5, destination = "a") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") queue = session.incoming("a") for i in range(1, 6): @@ -707,7 +707,7 @@ class MessageTests(TestBase010): #now create a not-acquired subscriber session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "b") #check it gets those not consumed queue = session.incoming("b") @@ -735,7 +735,7 @@ class MessageTests(TestBase010): #create a not-acquired subscriber session.message_subscribe(queue = "q", destination = "a", acquire_mode=1) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") #browse through messages @@ -757,7 +757,7 @@ class MessageTests(TestBase010): #create a second not-acquired subscriber session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "b") session.message_flow(unit = session.credit_unit.message, value = 1, destination = "b") #check it gets those not consumed queue = session.incoming("b") @@ -784,12 +784,12 @@ class MessageTests(TestBase010): #create two 'browsers' session.message_subscribe(queue = "q", destination = "a", acquire_mode=1) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") queueA = session.incoming("a") session.message_subscribe(queue = "q", destination = "b", acquire_mode=1) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "b") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "b") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "b") queueB = session.incoming("b") @@ -806,7 +806,7 @@ class MessageTests(TestBase010): #create consumer session.message_subscribe(queue = "q", destination = "c") - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "c") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "c") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "c") queueC = session.incoming("c") #consume the message then ack it @@ -823,8 +823,8 @@ class MessageTests(TestBase010): consumer_tag = "tag1" session.message_subscribe(queue="xyz", destination=consumer_tag) - session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFF, destination = consumer_tag) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.message, value = 0xFFFFFFFFL, destination = consumer_tag) + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = consumer_tag) queue = session.incoming(consumer_tag) msg = queue.get(timeout=1) self.assertEquals("", msg.body) @@ -863,7 +863,7 @@ class MessageTests(TestBase010): messages = session.incoming(d) sleep(1) session.message_flow(unit = session.credit_unit.message, value=2, destination=d) - session.message_flow(unit = session.credit_unit.byte, value=0xFFFFFFFF, destination=d) + session.message_flow(unit = session.credit_unit.byte, value=0xFFFFFFFFL, destination=d) assert messages.get(timeout=1).body == "second" self.assertEmpty(messages) diff --git a/qpid/python/tests_0-10/persistence.py b/qpid/python/tests_0-10/persistence.py index 815ad1f3dc..b93bb0bbfb 100644 --- a/qpid/python/tests_0-10/persistence.py +++ b/qpid/python/tests_0-10/persistence.py @@ -49,7 +49,7 @@ class PersistenceTests(TestBase010): #create consumer session.message_subscribe(queue = "q", destination = "a", accept_mode = 1, acquire_mode=0) - session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFF, destination = "a") + session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") queue = session.incoming("a") diff --git a/qpid/python/tests_0-10/queue.py b/qpid/python/tests_0-10/queue.py index 05e18081fa..eb38965190 100644 --- a/qpid/python/tests_0-10/queue.py +++ b/qpid/python/tests_0-10/queue.py @@ -49,8 +49,8 @@ class QueueTests(TestBase010): #send a further message and consume it, ensuring that the other messages are really gone session.message_transfer(message=Message(session.delivery_properties(routing_key="test-queue"), "four")) session.message_subscribe(queue="test-queue", destination="tag") - session.message_flow(destination="tag", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="tag", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="tag", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("tag") msg = queue.get(timeout=1) self.assertEqual("four", msg.body) @@ -182,11 +182,11 @@ class QueueTests(TestBase010): session.queue_declare(queue="queue-2", exclusive=True, auto_delete=True) session.message_subscribe(queue="queue-1", destination="queue-1") - session.message_flow(destination="queue-1", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="queue-1", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="queue-1", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="queue-1", unit=session.credit_unit.byte, value=0xFFFFFFFFL) session.message_subscribe(queue="queue-2", destination="queue-2") - session.message_flow(destination="queue-2", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="queue-2", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="queue-2", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="queue-2", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue1 = session.incoming("queue-1") queue2 = session.incoming("queue-2") @@ -283,8 +283,8 @@ class QueueTests(TestBase010): #empty queue: session.message_subscribe(destination="consumer_tag", queue="delete-me-2") - session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.body) diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py index da162d54ec..463fbcb888 100644 --- a/qpid/python/tests_0-10/tx.py +++ b/qpid/python/tests_0-10/tx.py @@ -251,13 +251,13 @@ class TxTests(TestBase010): session = session or self.session consumer_tag = keys["destination"] session.message_subscribe(**keys) - session.message_flow(destination=consumer_tag, unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination=consumer_tag, unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination=consumer_tag, unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination=consumer_tag, unit=session.credit_unit.byte, value=0xFFFFFFFFL) def enable_flow(self, tag, session=None): session = session or self.session - session.message_flow(destination=tag, unit=session.credit_unit.message, value=0xFFFFFFFF) - session.message_flow(destination=tag, unit=session.credit_unit.byte, value=0xFFFFFFFF) + session.message_flow(destination=tag, unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination=tag, unit=session.credit_unit.byte, value=0xFFFFFFFFL) def complete(self, session, msg): session.receiver._completed.add(msg.id)#TODO: this may be done automatically -- cgit v1.2.1 From d66d576676f8fcc0ee3fc11fb34322f499c43ca8 Mon Sep 17 00:00:00 2001 From: Ted Ross Date: Fri, 27 Mar 2009 20:46:24 +0000 Subject: QPID-1702 QPID-1706 Updated qmf console in Python and Ruby - Added support for asynchronous method invocation - Added option to override timeout for method request and get request - Added exception handler in delegates.rb to catch Sasl errors - Added tests for the async method features git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@759341 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/python/qmf/console.py | 60 +++++++++++++++++++++++------- qpid/python/qpid/testlib.py | 4 +- qpid/python/tests_0-10/management.py | 71 ++++++++++++++++++++++++++++++++---- 3 files changed, 113 insertions(+), 22 deletions(-) (limited to 'qpid/python') diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index 3b99595f1f..ef2ab264eb 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -77,15 +77,15 @@ class Console: pass def heartbeat(self, agent, timestamp): - """ """ + """ Invoked when an agent heartbeat is received. """ pass def brokerInfo(self, broker): - """ """ + """ Invoked when the connection sequence reaches the point where broker information is available. """ pass def methodResponse(self, broker, seq, response): - """ """ + """ Invoked when a method response from an asynchronous method call is received. """ pass class BrokerURL(URL): @@ -117,7 +117,7 @@ class Session: _CONTEXT_STARTUP = 2 _CONTEXT_MULTIGET = 3 - GET_WAIT_TIME = 60 + DEFAULT_GET_WAIT_TIME = 60 def __init__(self, console=None, rcvObjects=True, rcvEvents=True, rcvHeartbeats=True, manageConnections=False, userBindings=False): @@ -284,6 +284,11 @@ class Session: _broker = - supply a broker as returned by addBroker. + The default timeout for this synchronous operation is 60 seconds. To change the timeout, + use the following argument: + + _timeout =