summaryrefslogtreecommitdiff
path: root/qpid/extras/dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/extras/dispatch')
-rw-r--r--qpid/extras/dispatch/CMakeLists.txt35
-rw-r--r--qpid/extras/dispatch/ChangeLog24
-rw-r--r--qpid/extras/dispatch/README.md12
-rw-r--r--qpid/extras/dispatch/TODO47
-rw-r--r--qpid/extras/dispatch/config.sh27
-rw-r--r--qpid/extras/dispatch/etc/qpid-dispatch.conf54
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch.h1
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/amqp.h18
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/bitmask.h37
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/buffer.h4
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/compose.h3
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/container.h13
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/hash.h25
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/message.h4
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/python_embedded.h7
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/router.h2
-rw-r--r--qpid/extras/dispatch/include/qpid/dispatch/server.h181
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/config/schema.py3
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/adapter.py99
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/binding.py133
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/configuration.py46
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/data.py444
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/link.py233
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/mobile.py307
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/neighbor.py98
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/node.py219
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/path.py359
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/router_engine.py485
-rw-r--r--qpid/extras/dispatch/python/qpid/dispatch/router/routing.py70
-rw-r--r--qpid/extras/dispatch/router/CMakeLists.txt2
-rw-r--r--qpid/extras/dispatch/router/src/main.c2
-rw-r--r--qpid/extras/dispatch/src/agent.c20
-rw-r--r--qpid/extras/dispatch/src/amqp.c30
-rw-r--r--qpid/extras/dispatch/src/bitmask.c124
-rw-r--r--qpid/extras/dispatch/src/buffer.c4
-rw-r--r--qpid/extras/dispatch/src/compose.c8
-rw-r--r--qpid/extras/dispatch/src/container.c73
-rw-r--r--qpid/extras/dispatch/src/dispatch.c32
-rw-r--r--qpid/extras/dispatch/src/hash.c124
-rw-r--r--qpid/extras/dispatch/src/message.c18
-rw-r--r--qpid/extras/dispatch/src/message_private.h2
-rw-r--r--qpid/extras/dispatch/src/python_embedded.c60
-rw-r--r--qpid/extras/dispatch/src/router_agent.c187
-rw-r--r--qpid/extras/dispatch/src/router_node.c1107
-rw-r--r--qpid/extras/dispatch/src/router_private.h177
-rw-r--r--qpid/extras/dispatch/src/router_pynode.c682
-rw-r--r--qpid/extras/dispatch/src/server.c71
-rw-r--r--qpid/extras/dispatch/src/server_private.h1
-rw-r--r--qpid/extras/dispatch/tests/compose_test.c100
-rw-r--r--qpid/extras/dispatch/tests/config-1/A.conf (renamed from qpid/extras/dispatch/tests/onerouter.conf)0
-rw-r--r--qpid/extras/dispatch/tests/config-2/A.conf60
-rw-r--r--qpid/extras/dispatch/tests/config-2/B.conf68
-rw-r--r--qpid/extras/dispatch/tests/config-3-linear/A.conf70
-rw-r--r--qpid/extras/dispatch/tests/config-3-linear/B.conf69
-rw-r--r--qpid/extras/dispatch/tests/config-3-linear/C.conf61
-rw-r--r--qpid/extras/dispatch/tests/config-3-linear/topology.txt12
-rw-r--r--qpid/extras/dispatch/tests/field_test.c4
-rw-r--r--qpid/extras/dispatch/tests/message_test.c18
-rw-r--r--qpid/extras/dispatch/tests/router_engine_test.py985
-rw-r--r--qpid/extras/dispatch/tests/system_tests_one_router.py79
-rw-r--r--qpid/extras/dispatch/tests/tool_test.c33
-rwxr-xr-xqpid/extras/dispatch/tools/src/py/qdstat328
-rw-r--r--qpid/extras/dispatch/tools/src/py/qdtoollibs/__init__.py21
-rw-r--r--qpid/extras/dispatch/tools/src/py/qdtoollibs/disp.py270
64 files changed, 5453 insertions, 2439 deletions
diff --git a/qpid/extras/dispatch/CMakeLists.txt b/qpid/extras/dispatch/CMakeLists.txt
index ae0e126be7..a55de067f9 100644
--- a/qpid/extras/dispatch/CMakeLists.txt
+++ b/qpid/extras/dispatch/CMakeLists.txt
@@ -51,6 +51,7 @@ set(INCLUDE_INSTALL_DIR include CACHE PATH "Include file directory")
set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "Library object file directory")
set(SYSCONF_INSTALL_DIR etc CACHE PATH "System read only configuration directory")
set(SHARE_INSTALL_DIR share CACHE PATH "Shared read only data directory")
+set(DOC_INSTALL_DIR ${SHARE_INSTALL_DIR}/doc CACHE PATH "Shared read-only data directory")
set(MAN_INSTALL_DIR share/man CACHE PATH "Manpage directory")
# determine the location for installing the python packages
@@ -61,13 +62,6 @@ if (PYTHONLIBS_FOUND)
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif (PYTHONLIBS_FOUND)
-include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR}/include
- ${CMAKE_CURRENT_SOURCE_DIR}/src
- ${proton_include}
- ${PYTHON_INCLUDE_PATH}
- )
-
##
## Find dependencies
##
@@ -76,6 +70,13 @@ find_library(pthread_lib pthread)
find_library(rt_lib rt)
find_path(proton_include proton/driver.h)
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${proton_include}
+ ${PYTHON_INCLUDE_PATH}
+ )
+
set(CMAKE_C_FLAGS "-pthread -Wall -Werror -std=gnu99")
set(CATCH_UNDEFINED "-Wl,--no-undefined")
@@ -85,6 +86,8 @@ set(CATCH_UNDEFINED "-Wl,--no-undefined")
set(server_SOURCES
src/agent.c
src/alloc.c
+ src/amqp.c
+ src/bitmask.c
src/buffer.c
src/compose.c
src/config.c
@@ -98,12 +101,18 @@ set(server_SOURCES
src/parse.c
src/posix/threading.c
src/python_embedded.c
+ src/router_agent.c
src/router_node.c
+ src/router_pynode.c
src/server.c
src/timer.c
src/work_queue.c
)
+set_property(SOURCE src/python_embedded.c src/router_pynode.c
+ PROPERTY COMPILE_FLAGS -Wno-strict-aliasing
+ )
+
add_library(qpid-dispatch SHARED ${server_SOURCES})
target_link_libraries(qpid-dispatch ${proton_lib} ${pthread_lib} ${rt_lib} ${PYTHON_LIBRARIES})
set_target_properties(qpid-dispatch PROPERTIES
@@ -116,7 +125,7 @@ install(TARGETS qpid-dispatch
file(GLOB headers "include/qpid/dispatch/*.h")
install(FILES ${headers} DESTINATION ${INCLUDE_INSTALL_DIR}/qpid/dispatch)
install(FILES include/qpid/dispatch.h DESTINATION ${INCLUDE_INSTALL_DIR}/qpid)
-install(FILES etc/qpid-dispatch.conf DESTINATION ${SYSCONF_INSTALL_DIR})
+install(FILES etc/qpid-dispatch.conf DESTINATION ${SYSCONF_INSTALL_DIR}/qpid)
##
## Python modules installation
@@ -149,6 +158,13 @@ set(PYTHON_CONFIG_SOURCES
python/qpid/dispatch/__init__.py
)
+set(DOC_FILES
+ ChangeLog
+ LICENSE
+ README.md
+ TODO
+)
+
install(FILES ${PYTHON_STUBS_SOURCES}
DESTINATION ${PYTHON_SITELIB_PACKAGES}/qpid/dispatch/stubs)
@@ -163,6 +179,9 @@ install(FILES python/qpid/__init__.py
install(FILES python/qpid/dispatch/__init__.py
DESTINATION ${PYTHON_SITELIB_PACKAGES}/qpid/dispatch)
+
+install(FILES ${DOC_FILES}
+ DESTINATION ${DOC_INSTALL_DIR}/qpid-dispatch)
##
## Build Tests
##
diff --git a/qpid/extras/dispatch/ChangeLog b/qpid/extras/dispatch/ChangeLog
index a171f0eb11..2a5b4bc4b7 100644
--- a/qpid/extras/dispatch/ChangeLog
+++ b/qpid/extras/dispatch/ChangeLog
@@ -1,2 +1,26 @@
Version 0.1:
* Initial development work.
+ * QPID-5186: Install documentation files.
+ * QPID-5185: Install qpid-dispatch.conf to /etc/qpid
+ * QPID-4612: Dispatch - Change server and container pattern to be consistent with other objects
+ * QPID-4613: Dispatch Message API Improvement
+ * QPID-4614: CTEST for Dispatch
+ * QPID-4913: Dispatch - Add a configuration file reader to configure the service
+ * QPID-4968: Dispatch - Generalized framework for embedded Python modules
+ * QPID-4974: Dispatch - Improve the API for parsing and composing AMQP-typed fields
+ * QPID-5066: Dispatch - move Python code into the qpid.dispatch package
+ * QPID-5068: Dispatch - Internal feature to easily add and update Delivery Annotations
+ * QPID-5096: Dispatch - Install the configuration file
+ * QPID-5097: Dispatch - create a source tarball
+ * QPID-5181: Dispatch - Assign temporary source addresses for dynamic listener links
+ * QPID-5185: Move the qpid-dispatch.conf file to /etc/qpid
+ * QPID-5186: Installing Dispatch should also install the LICENSE, TODO and related files
+ * QPID-5189: Add a config.sh file for Qpid Dispatch to set an environment for running the router
+
+ * QPID-4788: Dispatch - Re-schedule of an "immediate" timer causes crash
+ * QPID-4816: dispatch-router crashes when incomplete (but valid) url specified by client.
+ * QPID-4997: Dispatch - Thread safety issues in the usage of Proton
+ * QPID-5064: Dispatch - make-install doesn't install the Python artifacts
+ * QPID-5173: [dispatch] cmake ignores overrides to CMAKE_INCLUDE_PATH and CMAKE_LIBRARY_PATH
+ * QPID-5201: Dispatch - Fix build errors in Release mode
+ * QPID-5218: [dispatch] Crash when outgoing window > 0 and multiple subscribed Messenger clients
diff --git a/qpid/extras/dispatch/README.md b/qpid/extras/dispatch/README.md
index 22e1a68570..1a4438fdf9 100644
--- a/qpid/extras/dispatch/README.md
+++ b/qpid/extras/dispatch/README.md
@@ -13,8 +13,18 @@ $ mkdir build
$ cd build
$ cmake ..
$ make
-$ make test
+$ make test # see below
Note: Your PYTHONPATH _must_ include <dispatch>/python in its list of paths in order
to test and run Dispatch.
+Running The Tests
+=================
+
+Prior to running the unit tests, you should source the file config.sh which is
+found in the root directory.
+
+$ . config.sh
+
+The file sets up the environment so that the tests can find the Python
+libraries, etc.
diff --git a/qpid/extras/dispatch/TODO b/qpid/extras/dispatch/TODO
index ab0c1a951e..5bb334a6ca 100644
--- a/qpid/extras/dispatch/TODO
+++ b/qpid/extras/dispatch/TODO
@@ -5,3 +5,50 @@ enhancements to be fixed by going to the Apache Qpid JIRA instance:
http://issues.apache.org/jira/browse/QPID
==============================================================================
+
+- Router Mode:
+ o Stand-Alone-Router - Does not participate in routing protocol, does not permit inter-router
+ links, acts as a normal interior-router otherwise.
+ o Interior-Router - Participates in the routing protocol
+ o Edge-Concentrator - Does not participate in routing protocol, requires uplink connection(s)
+ This mode should be used when Dispatch is integrated into an endpoint
+ application or when it is acting as a connection concentrator.
+ Proxy and access-protocol functions will be available in this mode.
+
+- Connection Annotation:
+ o Type: Inter-router, uplink, endpoint, etc. This formal annotation can be accessed internally
+ by the connection handlers to guide Dispatch's handling of new connections.
+ o Weight-{in,out}: Weight/Cost metrics for inter-router links
+
+- Statistics for Instrumentation:
+ o Link
+ . delivery count {unsettled, pre-settled}
+ . deliveries {accepted, rejected, released, modified}
+ . octets of delivery {accepted, rejected, released, modified}
+ . flow frame count
+ . disposition frame count {forward, backward}
+ o Address
+ . deliveries {ingress, egress, transit}
+ . octets of delivery {ingress, egress, transit}
+
+- Infrastructure
+ o Router_Link - Buffer and Iterator for a copy of the link's target address (for use
+ as an address for messages with no 'to' field).
+ o Router Event Queue - Event queue to feed alerts to the Python router code.
+ Neighbor-link-loss is a valuable event because it accelerates the
+ detection of topology change.
+ o All PyRouter stimulus through a work queue.
+ o Router Code Updates
+ . Report address mappings to routers
+ . Generate RA immediately after updating routing tables
+ . Generate unsolicited updates for mobile addresses?
+ o Expose idle-timeout/keepalive on connectors and listeners
+
+- Major Roadmap Features
+ o Security Policy Enforcement
+ o Proxy (Translation Node) Capability
+ o Address Provisioning with variable semantics
+ o Link Routing
+ o Management, Instrumentation, and Accounting
+ o Link Cost
+ o Area Routing
diff --git a/qpid/extras/dispatch/config.sh b/qpid/extras/dispatch/config.sh
new file mode 100644
index 0000000000..3c96464ea9
--- /dev/null
+++ b/qpid/extras/dispatch/config.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+#
+
+cd $(dirname ${BASH_SOURCE[0]}) > /dev/null
+export QPID_DISPATCH_HOME=$(pwd)
+cd - > /dev/null
+
+export PYTHONPATH=$QPID_DISPATCH_HOME/python:$QPID_DISPATCH_HOME/tools/src/py:$PYTHONPATH
+export PATH=$QPID_DISPATCH_HOME/tools/src/py:$PATH
+
diff --git a/qpid/extras/dispatch/etc/qpid-dispatch.conf b/qpid/extras/dispatch/etc/qpid-dispatch.conf
index ce7f5ed4c1..83fa98af60 100644
--- a/qpid/extras/dispatch/etc/qpid-dispatch.conf
+++ b/qpid/extras/dispatch/etc/qpid-dispatch.conf
@@ -102,6 +102,7 @@ listener {
listener {
label: Router Interconnect Access
+ role: inter-router
addr: 0.0.0.0
port: 5671
sasl-mechanisms: EXTERNAL
@@ -112,6 +113,7 @@ listener {
connector {
label: Router Uplink
+ role: inter-router
addr: backbone.enterprise.com
port: amqps
sasl-mechanisms: EXTERNAL
@@ -125,49 +127,27 @@ connector {
##
router {
##
- ## area and router-id - Each router is assigned an area name and a
- ## router-id that is unique within the area.
- ##
- area: all
- router-id: Router.A
-
- ##
- ## hello-interval - The interval, in seconds, between HELLO messages
- ## sent between connected routers. This interval can be relatively
- ## fast because HELLO messages do not propagate beyond one hop on the
- ## network. A good value is 1 second.
- ##
- hello-interval: 1
-
+ ## Router Mode:
##
- ## hello-max-age - The maximum time, in seconds, that can elapse
- ## without receiving a HELLO message from a neighbor before that
- ## neighbor is declared absent. A good value is 3 seconds.
+ ## standalone - Standalone router. In standalone mode, the router operates as
+ ## a single component. It does not participate in the routing protocol
+ ## and therefore will not coorperate with other routers.
+ ## interior - Interior router. The router operates in cooreration with other
+ ## interior routers in an interconnected network.
+ ## edge - Edge router. The router operates with an uplink into an interior
+ ## router network. Edge routers are typically used as connection concentrators
+ ## or as security firewalls for access into the interior network.
##
- hello-max-age: 3
+ mode: standalone
##
- ## ra-interval - The interval, in seconds, between RA (Router
- ## Advertisement) messages sent by this router. This interval should
- ## be considerably longer than the hello interval because RA messages
- ## propagate across the area. A good value is 30 seconds.
+ ## For Interior router mode only.
##
- ra-interval: 30
-
- ##
- ## remote-ls-max-age - The maximum age, in seconds, of a remote
- ## link-state record. Any such records that are not updated and time
- ## out shall be removed from the set and the topology shall be
- ## recomputed. A good value is 60 seconds.
- ##
- remote-ls-max-age: 60
-
- ##
- ## mobile-addr-max-age - The maximum age, in seconds, of a remote
- ## mobile address record. Any record that exceeds this age shall be
- ## removed from the routing table. A good value is 60 seconds.
+ ## area and router-id - Each router is assigned an area name and a
+ ## router-id that is unique within the area.
##
- mobile-addr-max-age: 60
+ area: all
+ router-id: Router.A
}
diff --git a/qpid/extras/dispatch/include/qpid/dispatch.h b/qpid/extras/dispatch/include/qpid/dispatch.h
index 72b3456099..6b3d6a963f 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch.h
@@ -20,6 +20,7 @@
*/
#include <qpid/dispatch/alloc.h>
+#include <qpid/dispatch/bitmask.h>
#include <qpid/dispatch/buffer.h>
#include <qpid/dispatch/ctools.h>
#include <qpid/dispatch/hash.h>
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/amqp.h b/qpid/extras/dispatch/include/qpid/dispatch/amqp.h
index 9babeb930d..c27eec6589 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/amqp.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/amqp.h
@@ -76,5 +76,23 @@
#define DX_AMQP_ARRAY8 0xe0
#define DX_AMQP_ARRAY32 0xf0
+/**
+ * Delivery Annotation Headers
+ */
+const char * const DX_DA_INGRESS; // Ingress Router
+const char * const DX_DA_TRACE; // Trace
+const char * const DX_DA_TO; // To-Override
+
+/**
+ * Link Terminus Capabilities
+ */
+const char * const DX_CAPABILITY_ROUTER;
+
+/**
+ * Miscellaneous Strings
+ */
+const char * const DX_INTERNODE_LINK_NAME_1;
+const char * const DX_INTERNODE_LINK_NAME_2;
+
#endif
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/bitmask.h b/qpid/extras/dispatch/include/qpid/dispatch/bitmask.h
new file mode 100644
index 0000000000..fe436bc19b
--- /dev/null
+++ b/qpid/extras/dispatch/include/qpid/dispatch/bitmask.h
@@ -0,0 +1,37 @@
+#ifndef __dispatch_bitmask_h__
+#define __dispatch_bitmask_h__ 1
+/*
+ * 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.
+ */
+
+typedef struct dx_bitmask_t dx_bitmask_t;
+
+int dx_bitmask_width();
+dx_bitmask_t *dx_bitmask(int initial);
+void dx_bitmask_free(dx_bitmask_t *b);
+void dx_bitmask_set_all(dx_bitmask_t *b);
+void dx_bitmask_clear_all(dx_bitmask_t *b);
+void dx_bitmask_set_bit(dx_bitmask_t *b, int bitnum);
+void dx_bitmask_clear_bit(dx_bitmask_t *b, int bitnum);
+int dx_bitmask_value(dx_bitmask_t *b, int bitnum);
+int dx_bitmask_first_set(dx_bitmask_t *b, int *bitnum);
+
+
+
+#endif
+
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/buffer.h b/qpid/extras/dispatch/include/qpid/dispatch/buffer.h
index 1c372b265d..fadded4d74 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/buffer.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/buffer.h
@@ -36,12 +36,12 @@ void dx_buffer_set_size(size_t size);
/**
*/
-dx_buffer_t *dx_allocate_buffer(void);
+dx_buffer_t *dx_buffer(void);
/**
* @param buf A pointer to an allocated buffer
*/
-void dx_free_buffer(dx_buffer_t *buf);
+void dx_buffer_free(dx_buffer_t *buf);
/**
* @param buf A pointer to an allocated buffer
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/compose.h b/qpid/extras/dispatch/include/qpid/dispatch/compose.h
index 7d668f0ac0..d68f6be746 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/compose.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/compose.h
@@ -167,7 +167,8 @@ void dx_compose_insert_string(dx_composed_field_t *field, const char *value);
* Insert a utf8-encoded string into the field from an iterator
*
* @param field A field created by dx_compose.
- * @param value A pointer to a null-terminated string.
+ * @param iter An iterator for a string value. The caller is responsible for freeing
+ * this iterator after the call is complete.
*/
void dx_compose_insert_string_iterator(dx_composed_field_t *field, dx_field_iterator_t *iter);
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/container.h b/qpid/extras/dispatch/include/qpid/dispatch/container.h
index a306895b50..03fdd0c4cc 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/container.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/container.h
@@ -152,8 +152,21 @@ dx_dist_mode_t dx_container_node_get_dist_modes(const dx_node_t *node);
dx_lifetime_policy_t dx_container_node_get_life_policy(const dx_node_t *node);
dx_link_t *dx_link(dx_node_t *node, dx_connection_t *conn, dx_direction_t dir, const char *name);
+
+/**
+ * Context associated with the link for storing link-specific state.
+ */
void dx_link_set_context(dx_link_t *link, void *link_context);
void *dx_link_get_context(dx_link_t *link);
+
+/**
+ * Link context associated with the link's connection for storing state shared across
+ * all links in a connection.
+ */
+void dx_link_set_conn_context(dx_link_t *link, void *link_context);
+void *dx_link_get_conn_context(dx_link_t *link);
+
+dx_connection_t *dx_link_connection(dx_link_t *link);
pn_link_t *dx_link_pn(dx_link_t *link);
pn_terminus_t *dx_link_source(dx_link_t *link);
pn_terminus_t *dx_link_target(dx_link_t *link);
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/hash.h b/qpid/extras/dispatch/include/qpid/dispatch/hash.h
index bfad142517..4f079f6993 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/hash.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/hash.h
@@ -23,16 +23,23 @@
#include <qpid/dispatch/iterator.h>
#include <qpid/dispatch/error.h>
-typedef struct hash_t hash_t;
+typedef struct dx_hash_t dx_hash_t;
+typedef struct dx_hash_handle_t dx_hash_handle_t;
-hash_t *hash(int bucket_exponent, int batch_size, int value_is_const);
-void hash_free(hash_t *h);
+dx_hash_t *dx_hash(int bucket_exponent, int batch_size, int value_is_const);
+void dx_hash_free(dx_hash_t *h);
+
+size_t dx_hash_size(dx_hash_t *h);
+dx_error_t dx_hash_insert(dx_hash_t *h, dx_field_iterator_t *key, void *val, dx_hash_handle_t **handle);
+dx_error_t dx_hash_insert_const(dx_hash_t *h, dx_field_iterator_t *key, const void *val, dx_hash_handle_t **handle);
+dx_error_t dx_hash_retrieve(dx_hash_t *h, dx_field_iterator_t *key, void **val);
+dx_error_t dx_hash_retrieve_const(dx_hash_t *h, dx_field_iterator_t *key, const void **val);
+dx_error_t dx_hash_remove(dx_hash_t *h, dx_field_iterator_t *key);
+
+void dx_hash_handle_free(dx_hash_handle_t *handle);
+const unsigned char *dx_hash_key_by_handle(const dx_hash_handle_t *handle);
+dx_error_t dx_hash_remove_by_handle(dx_hash_t *h, dx_hash_handle_t *handle);
+dx_error_t dx_hash_remove_by_handle2(dx_hash_t *h, dx_hash_handle_t *handle, unsigned char **key);
-size_t hash_size(hash_t *h);
-dx_error_t hash_insert(hash_t *h, dx_field_iterator_t *key, void *val);
-dx_error_t hash_insert_const(hash_t *h, dx_field_iterator_t *key, const void *val);
-dx_error_t hash_retrieve(hash_t *h, dx_field_iterator_t *key, void **val);
-dx_error_t hash_retrieve_const(hash_t *h, dx_field_iterator_t *key, const void **val);
-dx_error_t hash_remove(hash_t *h, dx_field_iterator_t *key);
#endif
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/message.h b/qpid/extras/dispatch/include/qpid/dispatch/message.h
index 34a7933f9f..8702e509b3 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/message.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/message.h
@@ -96,7 +96,7 @@ typedef enum {
* @return A pointer to a dx_message_t that is the sole reference to a newly allocated
* message.
*/
-dx_message_t *dx_allocate_message(void);
+dx_message_t *dx_message(void);
/**
* Free a message reference. If this is the last reference to the message, free the
@@ -104,7 +104,7 @@ dx_message_t *dx_allocate_message(void);
*
* @param msg A pointer to a dx_message_t that is no longer needed.
*/
-void dx_free_message(dx_message_t *msg);
+void dx_message_free(dx_message_t *msg);
/**
* Make a new reference to an existing message.
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/python_embedded.h b/qpid/extras/dispatch/include/qpid/dispatch/python_embedded.h
index 644885bbd7..aad0453c24 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/python_embedded.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/python_embedded.h
@@ -71,4 +71,11 @@ void dx_py_to_composed(PyObject *value, dx_composed_field_t *field);
*/
PyObject *dx_field_to_py(dx_parsed_field_t *field);
+/**
+ * These are temporary and will eventually be replaced by having an internal python
+ * work queue that feeds a dedicated embedded-python thread.
+ */
+void dx_python_lock();
+void dx_python_unlock();
+
#endif
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/router.h b/qpid/extras/dispatch/include/qpid/dispatch/router.h
index c900d93615..1dadc8d119 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/router.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/router.h
@@ -27,7 +27,7 @@
typedef struct dx_address_t dx_address_t;
-typedef void (*dx_router_message_cb)(void *context, dx_message_t *msg);
+typedef void (*dx_router_message_cb)(void *context, dx_message_t *msg, int link_id);
const char *dx_router_id(const dx_dispatch_t *dx);
diff --git a/qpid/extras/dispatch/include/qpid/dispatch/server.h b/qpid/extras/dispatch/include/qpid/dispatch/server.h
index 7b2d4a2432..e50f9ac75b 100644
--- a/qpid/extras/dispatch/include/qpid/dispatch/server.h
+++ b/qpid/extras/dispatch/include/qpid/dispatch/server.h
@@ -186,7 +186,7 @@ typedef struct dx_connector_t dx_connector_t;
typedef struct dx_connection_t dx_connection_t;
/**
- * Event type for the connection callback.
+ * \brief Event type for the connection callback.
*/
typedef enum {
/// The connection just opened via a listener (inbound).
@@ -204,79 +204,6 @@ typedef enum {
/**
- * \brief Connection Event Handler
- *
- * Callback invoked when processing is needed on a proton connection. This
- * callback shall be invoked on one of the server's worker threads. The
- * server guarantees that no two threads shall be allowed to process a single
- * connection concurrently. The implementation of this handler may assume
- * that it has exclusive access to the connection and its subservient
- * components (sessions, links, deliveries, etc.).
- *
- * @param handler_context The handler context supplied in dx_server_set_conn_handler.
- * @param conn_context The handler context supplied in dx_server_{connect,listen}.
- * @param event The event/reason for the invocation of the handler.
- * @param conn The connection that requires processing by the handler.
- * @return A value greater than zero if the handler did any proton processing for
- * the connection. If no work was done, zero is returned.
- */
-typedef int (*dx_conn_handler_cb_t)(void *handler_context, void* conn_context, dx_conn_event_t event, dx_connection_t *conn);
-
-
-/**
- * \brief Set the connection event handler callback.
- *
- * Set the connection handler callback for the server. This callback is
- * mandatory and must be set prior to the invocation of dx_server_run.
- *
- * @param dx The dispatch handle returned by dx_dispatch.
- * @param conn_hander The handler for processing connection-related events.
- */
-void dx_server_set_conn_handler(dx_dispatch_t *dx, dx_conn_handler_cb_t conn_handler, void *handler_context);
-
-
-/**
- * \brief Set the user context for a connection.
- *
- * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
- * @param context User context to be stored with the connection.
- */
-void dx_connection_set_context(dx_connection_t *conn, void *context);
-
-
-/**
- * \brief Get the user context from a connection.
- *
- * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
- * @return The user context stored with the connection.
- */
-void *dx_connection_get_context(dx_connection_t *conn);
-
-
-/**
- * \brief Activate a connection for output.
- *
- * This function is used to request that the server activate the indicated
- * connection. It is assumed that the connection is one that the caller does
- * not have permission to access (i.e. it may be owned by another thread
- * currently). An activated connection will, when writable, appear in the
- * internal work list and be invoked for processing by a worker thread.
- *
- * @param conn The connection over which the application wishes to send data
- */
-void dx_server_activate(dx_connection_t *conn);
-
-
-/**
- * \brief Get the wrapped proton-engine connection object.
- *
- * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
- * @return The proton connection object.
- */
-pn_connection_t *dx_connection_pn(dx_connection_t *conn);
-
-
-/**
* \brief Configuration block for a connector or a listener.
*/
typedef struct dx_server_config_t {
@@ -367,10 +294,116 @@ typedef struct dx_server_config_t {
* meaningful for outgoing (connector) connections only.
*/
int allow_redirect;
+
+ /**
+ * The specified role of the connection. This can be used to control the behavior and
+ * capabilities of the connections.
+ */
+ const char *role;
} dx_server_config_t;
/**
+ * \brief Connection Event Handler
+ *
+ * Callback invoked when processing is needed on a proton connection. This
+ * callback shall be invoked on one of the server's worker threads. The
+ * server guarantees that no two threads shall be allowed to process a single
+ * connection concurrently. The implementation of this handler may assume
+ * that it has exclusive access to the connection and its subservient
+ * components (sessions, links, deliveries, etc.).
+ *
+ * @param handler_context The handler context supplied in dx_server_set_conn_handler.
+ * @param conn_context The handler context supplied in dx_server_{connect,listen}.
+ * @param event The event/reason for the invocation of the handler.
+ * @param conn The connection that requires processing by the handler.
+ * @return A value greater than zero if the handler did any proton processing for
+ * the connection. If no work was done, zero is returned.
+ */
+typedef int (*dx_conn_handler_cb_t)(void *handler_context, void* conn_context, dx_conn_event_t event, dx_connection_t *conn);
+
+
+/**
+ * \brief Set the connection event handler callback.
+ *
+ * Set the connection handler callback for the server. This callback is
+ * mandatory and must be set prior to the invocation of dx_server_run.
+ *
+ * @param dx The dispatch handle returned by dx_dispatch.
+ * @param conn_hander The handler for processing connection-related events.
+ */
+void dx_server_set_conn_handler(dx_dispatch_t *dx, dx_conn_handler_cb_t conn_handler, void *handler_context);
+
+
+/**
+ * \brief Set the user context for a connection.
+ *
+ * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
+ * @param context User context to be stored with the connection.
+ */
+void dx_connection_set_context(dx_connection_t *conn, void *context);
+
+
+/**
+ * \brief Get the user context from a connection.
+ *
+ * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
+ * @return The user context stored with the connection.
+ */
+void *dx_connection_get_context(dx_connection_t *conn);
+
+
+/**
+ * \brief Set the link context for a connection.
+ *
+ * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
+ * @param context Link context to be stored with the connection.
+ */
+void dx_connection_set_link_context(dx_connection_t *conn, void *context);
+
+
+/**
+ * \brief Get the link context from a connection.
+ *
+ * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
+ * @return The link context stored with the connection.
+ */
+void *dx_connection_get_link_context(dx_connection_t *conn);
+
+
+/**
+ * \brief Activate a connection for output.
+ *
+ * This function is used to request that the server activate the indicated
+ * connection. It is assumed that the connection is one that the caller does
+ * not have permission to access (i.e. it may be owned by another thread
+ * currently). An activated connection will, when writable, appear in the
+ * internal work list and be invoked for processing by a worker thread.
+ *
+ * @param conn The connection over which the application wishes to send data
+ */
+void dx_server_activate(dx_connection_t *conn);
+
+
+/**
+ * \brief Get the wrapped proton-engine connection object.
+ *
+ * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
+ * @return The proton connection object.
+ */
+pn_connection_t *dx_connection_pn(dx_connection_t *conn);
+
+
+/**
+ * \brief Get the configuration that was used in the setup of this connection.
+ *
+ * @param conn Connection object supplied in DX_CONN_EVENT_{LISTENER,CONNETOR}_OPEN
+ * @return A pointer to the server configuration used in the establishment of this connection.
+ */
+const dx_server_config_t *dx_connection_config(const dx_connection_t *conn);
+
+
+/**
* \brief Create a listener for incoming connections.
*
* @param dx The dispatch handle returned by dx_dispatch.
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/config/schema.py b/qpid/extras/dispatch/python/qpid/dispatch/config/schema.py
index 545139f0df..7b00000c99 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/config/schema.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/config/schema.py
@@ -57,6 +57,7 @@ config_schema = {
'addr' : (str, 0, "M"),
'port' : (str, 1, "M"),
'label' : (str, None, "", None),
+ 'role' : (str, None, "", 'normal'),
'sasl-mechanisms' : (str, None, "M"),
'ssl-profile' : (str, None, "E", None),
'require-peer-auth' : (bool, None, "", True),
@@ -66,11 +67,13 @@ config_schema = {
'addr' : (str, 0, "M"),
'port' : (str, 1, "M"),
'label' : (str, None, "", None),
+ 'role' : (str, None, "", 'normal'),
'sasl-mechanisms' : (str, None, "M"),
'ssl-profile' : (str, None, "E", None),
'allow-redirect' : (bool, None, "", True)
}),
'router' : (True, {
+ 'mode' : (str, None, "", 'standalone'),
'router-id' : (str, None, "M"),
'area' : (str, None, "", None),
'hello-interval' : (int, None, "", 1),
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/adapter.py b/qpid/extras/dispatch/python/qpid/dispatch/router/adapter.py
deleted file mode 100644
index 7f7f6c9e8e..0000000000
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/adapter.py
+++ /dev/null
@@ -1,99 +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.
-#
-
-try:
- from dispatch import *
-except ImportError:
- from ..stubs import *
-
-ENTRY_OLD = 1
-ENTRY_CURRENT = 2
-ENTRY_NEW = 3
-
-class AdapterEngine(object):
- """
- This module is responsible for managing the Adapter's key bindings (list of address-subject:next-hop).
- Key binding lists are kept in disjoint key-classes that can come from different parts of the router
- (i.e. topological keys for inter-router communication and mobile keys for end users).
-
- For each key-class, a mirror copy of what the adapter has is kept internally. This allows changes to the
- routing tables to be efficiently communicated to the adapter in the form of table deltas.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.key_classes = {} # map [key_class] => (addr-key, next-hop)
-
-
- def tick(self, now):
- """
- There is no periodic processing needed for this module.
- """
- pass
-
-
- def remote_routes_changed(self, key_class, new_table):
- old_table = []
- if key_class in self.key_classes:
- old_table = self.key_classes[key_class]
-
- # flag all of the old entries
- old_flags = {}
- for a,b in old_table:
- old_flags[(a,b)] = ENTRY_OLD
-
- # flag the new entries
- new_flags = {}
- for a,b in new_table:
- new_flags[(a,b)] = ENTRY_NEW
-
- # calculate the differences from old to new
- for a,b in new_table:
- if old_table.count((a,b)) > 0:
- old_flags[(a,b)] = ENTRY_CURRENT
- new_flags[(a,b)] = ENTRY_CURRENT
-
- # make to_add and to_delete lists
- to_add = []
- to_delete = []
- for (a,b),f in old_flags.items():
- if f == ENTRY_OLD:
- to_delete.append((a,b))
- for (a,b),f in new_flags.items():
- if f == ENTRY_NEW:
- to_add.append((a,b))
-
- # set the routing table to the new contents
- self.key_classes[key_class] = new_table
-
- # update the adapter's routing tables
- # Note: Do deletions before adds to avoid overlapping routes that may cause
- # messages to be duplicated. It's better to have gaps in the routing
- # tables momentarily because unroutable messages are stored for retry.
- for a,b in to_delete:
- self.container.router_adapter.remote_unbind(a, b)
- for a,b in to_add:
- self.container.router_adapter.remote_bind(a, b)
-
- self.container.log(LOG_INFO, "New Routing Table (class=%s):" % key_class)
- for a,b in new_table:
- self.container.log(LOG_INFO, " %s => %s" % (a, b))
-
-
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/binding.py b/qpid/extras/dispatch/python/qpid/dispatch/router/binding.py
deleted file mode 100644
index a37b585e3e..0000000000
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/binding.py
+++ /dev/null
@@ -1,133 +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.
-#
-
-try:
- from dispatch import *
-except ImportError:
- from ..stubs import *
-
-
-class BindingEngine(object):
- """
- This module is responsible for responding to two different events:
- 1) The learning of new remote mobile addresses
- 2) The change of topology (i.e. different next-hops for remote routers)
- When these occur, this module converts the mobile routing table (address => router)
- to a next-hop routing table (address => next-hop), compresses the keys in case there
- are wild-card overlaps, and notifies outbound of changes in the "mobile-key" address class.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.current_keys = {}
-
-
- def tick(self, now):
- pass
-
-
- def mobile_keys_changed(self, keys):
- self.current_keys = keys
- next_hop_keys = self._convert_ids_to_next_hops(keys)
- routing_table = self._compress_keys(next_hop_keys)
- self.container.remote_routes_changed('mobile-key', routing_table)
-
-
- def next_hops_changed(self):
- next_hop_keys = self._convert_ids_to_next_hops(self.current_keys)
- routing_table = self._compress_keys(next_hop_keys)
- self.container.remote_routes_changed('mobile-key', routing_table)
-
-
- def _convert_ids_to_next_hops(self, keys):
- next_hops = self.container.get_next_hops()
- new_keys = {}
- for _id, value in keys.items():
- if _id in next_hops:
- next_hop = next_hops[_id]
- if next_hop not in new_keys:
- new_keys[next_hop] = []
- new_keys[next_hop].extend(value)
- return new_keys
-
-
- def _compress_keys(self, keys):
- trees = {}
- for _id, key_list in keys.items():
- trees[_id] = TopicElementList()
- for key in key_list:
- trees[_id].add_key(key)
- routing_table = []
- for _id, tree in trees.items():
- tree_keys = tree.get_list()
- for tk in tree_keys:
- routing_table.append((tk, _id))
- return routing_table
-
-
-class TopicElementList(object):
- """
- """
- def __init__(self):
- self.elements = {} # map text => (terminal, sub-list)
-
- def __repr__(self):
- return "%r" % self.elements
-
- def add_key(self, key):
- self.add_tokens(key.split('.'))
-
- def add_tokens(self, tokens):
- first = tokens.pop(0)
- terminal = len(tokens) == 0
-
- if terminal and first == '#':
- ## Optimization #1A (A.B.C.D followed by A.B.#)
- self.elements = {'#':(True, TopicElementList())}
- return
-
- if '#' in self.elements:
- _t,_el = self.elements['#']
- if _t:
- ## Optimization #1B (A.B.# followed by A.B.C.D)
- return
-
- if first not in self.elements:
- self.elements[first] = (terminal, TopicElementList())
- else:
- _t,_el = self.elements[first]
- if terminal and not _t:
- self.elements[first] = (terminal, _el)
-
- if not terminal:
- _t,_el = self.elements[first]
- _el.add_tokens(tokens)
-
- def get_list(self):
- keys = []
- for token, (_t,_el) in self.elements.items():
- if _t: keys.append(token)
- _el.build_list(token, keys)
- return keys
-
- def build_list(self, prefix, keys):
- for token, (_t,_el) in self.elements.items():
- if _t: keys.append("%s.%s" % (prefix, token))
- _el.build_list("%s.%s" % (prefix, token), keys)
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/configuration.py b/qpid/extras/dispatch/python/qpid/dispatch/router/configuration.py
index f87d2ee7d2..e0fd060b82 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/configuration.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/configuration.py
@@ -18,30 +18,30 @@
#
class Configuration(object):
- """
- This module manages and holds the configuration and tuning parameters for a router.
- """
- def __init__(self, overrides={}):
- ##
- ## Load default values
- ##
- self.values = { 'hello_interval' : 1.0,
- 'hello_max_age' : 3.0,
- 'ra_interval' : 30.0,
- 'remote_ls_max_age' : 60.0,
- 'mobile_addr_max_age' : 60.0 }
+ """
+ This module manages and holds the configuration and tuning parameters for a router.
+ """
+ def __init__(self, overrides={}):
+ ##
+ ## Load default values
+ ##
+ self.values = { 'hello_interval' : 1.0,
+ 'hello_max_age' : 3.0,
+ 'ra_interval' : 30.0,
+ 'remote_ls_max_age' : 60.0,
+ 'mobile_addr_max_age' : 60.0 }
- ##
- ## Apply supplied overrides
- ##
- for k, v in overrides.items():
- self.values[k] = v
+ ##
+ ## Apply supplied overrides
+ ##
+ for k, v in overrides.items():
+ self.values[k] = v
- def __getattr__(self, key):
- if key in self.values:
- return self.values[key]
- raise KeyError
+ def __getattr__(self, key):
+ if key in self.values:
+ return self.values[key]
+ raise KeyError
- def __repr__(self):
- return "%r" % self.values
+ def __repr__(self):
+ return "%r" % self.values
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/data.py b/qpid/extras/dispatch/python/qpid/dispatch/router/data.py
index 89e347a29e..812369768f 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/data.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/data.py
@@ -19,257 +19,257 @@
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
def getMandatory(data, key, cls=None):
- """
- Get the value mapped to the requested key. If it's not present, raise an exception.
- """
- if key in data:
- value = data[key]
- if cls and value.__class__ != cls:
- raise Exception("Protocol field has wrong data type: '%s' type=%r expected=%r" % (key, value.__class__, cls))
- return value
- raise Exception("Mandatory protocol field missing: '%s'" % key)
+ """
+ Get the value mapped to the requested key. If it's not present, raise an exception.
+ """
+ if key in data:
+ value = data[key]
+ if cls and value.__class__ != cls:
+ raise Exception("Protocol field has wrong data type: '%s' type=%r expected=%r" % (key, value.__class__, cls))
+ return value
+ raise Exception("Mandatory protocol field missing: '%s'" % key)
def getOptional(data, key, default=None, cls=None):
- """
- Get the value mapped to the requested key. If it's not present, return the default value.
- """
- if key in data:
- value = data[key]
- if cls and value.__class__ != cls:
- raise Exception("Protocol field has wrong data type: '%s' type=%r expected=%r" % (key, value.__class__, cls))
- return value
- return default
+ """
+ Get the value mapped to the requested key. If it's not present, return the default value.
+ """
+ if key in data:
+ value = data[key]
+ if cls and value.__class__ != cls:
+ raise Exception("Protocol field has wrong data type: '%s' type=%r expected=%r" % (key, value.__class__, cls))
+ return value
+ return default
class LinkState(object):
- """
- The link-state of a single router. The link state consists of a list of neighbor routers reachable from
- the reporting router. The link-state-sequence number is incremented each time the link state changes.
- """
- def __init__(self, body, _id=None, _area=None, _ls_seq=None, _peers=None):
- self.last_seen = 0
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- self.ls_seq = getMandatory(body, 'ls_seq', long)
- self.peers = getMandatory(body, 'peers', list)
- else:
- self.id = _id
- self.area = _area
- self.ls_seq = long(_ls_seq)
- self.peers = _peers
-
- def __repr__(self):
- return "LS(id=%s area=%s ls_seq=%d peers=%r)" % (self.id, self.area, self.ls_seq, self.peers)
-
- def to_dict(self):
- return {'id' : self.id,
- 'area' : self.area,
- 'ls_seq' : self.ls_seq,
- 'peers' : self.peers}
-
- def add_peer(self, _id):
- if self.peers.count(_id) == 0:
- self.peers.append(_id)
- return True
- return False
-
- def del_peer(self, _id):
- if self.peers.count(_id) > 0:
- self.peers.remove(_id)
- return True
- return False
-
- def bump_sequence(self):
- self.ls_seq += 1
+ """
+ The link-state of a single router. The link state consists of a list of neighbor routers reachable from
+ the reporting router. The link-state-sequence number is incremented each time the link state changes.
+ """
+ def __init__(self, body, _id=None, _area=None, _ls_seq=None, _peers=None):
+ self.last_seen = 0
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ self.ls_seq = getMandatory(body, 'ls_seq', long)
+ self.peers = getMandatory(body, 'peers', list)
+ else:
+ self.id = _id
+ self.area = _area
+ self.ls_seq = long(_ls_seq)
+ self.peers = _peers
+
+ def __repr__(self):
+ return "LS(id=%s area=%s ls_seq=%d peers=%r)" % (self.id, self.area, self.ls_seq, self.peers)
+
+ def to_dict(self):
+ return {'id' : self.id,
+ 'area' : self.area,
+ 'ls_seq' : self.ls_seq,
+ 'peers' : self.peers}
+
+ def add_peer(self, _id):
+ if self.peers.count(_id) == 0:
+ self.peers.append(_id)
+ return True
+ return False
+
+ def del_peer(self, _id):
+ if self.peers.count(_id) > 0:
+ self.peers.remove(_id)
+ return True
+ return False
+
+ def bump_sequence(self):
+ self.ls_seq += 1
class MessageHELLO(object):
- """
- HELLO Message
- scope: neighbors only - HELLO messages travel at most one hop
- This message is used by directly connected routers to determine with whom they have
- bidirectional connectivity.
- """
- def __init__(self, body, _id=None, _area=None, _seen_peers=None):
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- self.seen_peers = getMandatory(body, 'seen', list)
- else:
- self.id = _id
- self.area = _area
- self.seen_peers = _seen_peers
-
- def __repr__(self):
- return "HELLO(id=%s area=%s seen=%r)" % (self.id, self.area, self.seen_peers)
-
- def get_opcode(self):
- return 'HELLO'
-
- def to_dict(self):
- return {'id' : self.id,
- 'area' : self.area,
- 'seen' : self.seen_peers}
-
- def is_seen(self, _id):
- return self.seen_peers.count(_id) > 0
+ """
+ HELLO Message
+ scope: neighbors only - HELLO messages travel at most one hop
+ This message is used by directly connected routers to determine with whom they have
+ bidirectional connectivity.
+ """
+ def __init__(self, body, _id=None, _area=None, _seen_peers=None):
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ self.seen_peers = getMandatory(body, 'seen', list)
+ else:
+ self.id = _id
+ self.area = _area
+ self.seen_peers = _seen_peers
+
+ def __repr__(self):
+ return "HELLO(id=%s area=%s seen=%r)" % (self.id, self.area, self.seen_peers)
+
+ def get_opcode(self):
+ return 'HELLO'
+
+ def to_dict(self):
+ return {'id' : self.id,
+ 'area' : self.area,
+ 'seen' : self.seen_peers}
+
+ def is_seen(self, _id):
+ return self.seen_peers.count(_id) > 0
class MessageRA(object):
- """
- Router Advertisement (RA) Message
- scope: all routers in the area and all designated routers
- This message is sent periodically to indicate the originating router's sequence numbers
- for link-state and mobile-address-state.
- """
- def __init__(self, body, _id=None, _area=None, _ls_seq=None, _mobile_seq=None):
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- self.ls_seq = getMandatory(body, 'ls_seq', long)
- self.mobile_seq = getMandatory(body, 'mobile_seq', long)
- else:
- self.id = _id
- self.area = _area
- self.ls_seq = long(_ls_seq)
- self.mobile_seq = long(_mobile_seq)
-
- def get_opcode(self):
- return 'RA'
-
- def __repr__(self):
- return "RA(id=%s area=%s ls_seq=%d mobile_seq=%d)" % \
- (self.id, self.area, self.ls_seq, self.mobile_seq)
-
- def to_dict(self):
- return {'id' : self.id,
- 'area' : self.area,
- 'ls_seq' : self.ls_seq,
- 'mobile_seq' : self.mobile_seq}
+ """
+ Router Advertisement (RA) Message
+ scope: all routers in the area and all designated routers
+ This message is sent periodically to indicate the originating router's sequence numbers
+ for link-state and mobile-address-state.
+ """
+ def __init__(self, body, _id=None, _area=None, _ls_seq=None, _mobile_seq=None):
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ self.ls_seq = getMandatory(body, 'ls_seq', long)
+ self.mobile_seq = getMandatory(body, 'mobile_seq', long)
+ else:
+ self.id = _id
+ self.area = _area
+ self.ls_seq = long(_ls_seq)
+ self.mobile_seq = long(_mobile_seq)
+
+ def get_opcode(self):
+ return 'RA'
+
+ def __repr__(self):
+ return "RA(id=%s area=%s ls_seq=%d mobile_seq=%d)" % \
+ (self.id, self.area, self.ls_seq, self.mobile_seq)
+
+ def to_dict(self):
+ return {'id' : self.id,
+ 'area' : self.area,
+ 'ls_seq' : self.ls_seq,
+ 'mobile_seq' : self.mobile_seq}
class MessageLSU(object):
- """
- """
- def __init__(self, body, _id=None, _area=None, _ls_seq=None, _ls=None):
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- self.ls_seq = getMandatory(body, 'ls_seq', long)
- self.ls = LinkState(getMandatory(body, 'ls', dict))
- else:
- self.id = _id
- self.area = _area
- self.ls_seq = long(_ls_seq)
- self.ls = _ls
-
- def get_opcode(self):
- return 'LSU'
-
- def __repr__(self):
- return "LSU(id=%s area=%s ls_seq=%d ls=%r)" % \
- (self.id, self.area, self.ls_seq, self.ls)
-
- def to_dict(self):
- return {'id' : self.id,
- 'area' : self.area,
- 'ls_seq' : self.ls_seq,
- 'ls' : self.ls.to_dict()}
+ """
+ """
+ def __init__(self, body, _id=None, _area=None, _ls_seq=None, _ls=None):
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ self.ls_seq = getMandatory(body, 'ls_seq', long)
+ self.ls = LinkState(getMandatory(body, 'ls', dict))
+ else:
+ self.id = _id
+ self.area = _area
+ self.ls_seq = long(_ls_seq)
+ self.ls = _ls
+
+ def get_opcode(self):
+ return 'LSU'
+
+ def __repr__(self):
+ return "LSU(id=%s area=%s ls_seq=%d ls=%r)" % \
+ (self.id, self.area, self.ls_seq, self.ls)
+
+ def to_dict(self):
+ return {'id' : self.id,
+ 'area' : self.area,
+ 'ls_seq' : self.ls_seq,
+ 'ls' : self.ls.to_dict()}
class MessageLSR(object):
- """
- """
- def __init__(self, body, _id=None, _area=None):
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- else:
- self.id = _id
- self.area = _area
+ """
+ """
+ def __init__(self, body, _id=None, _area=None):
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ else:
+ self.id = _id
+ self.area = _area
- def get_opcode(self):
- return 'LSR'
+ def get_opcode(self):
+ return 'LSR'
- def __repr__(self):
- return "LSR(id=%s area=%s)" % (self.id, self.area)
+ def __repr__(self):
+ return "LSR(id=%s area=%s)" % (self.id, self.area)
- def to_dict(self):
- return {'id' : self.id,
- 'area' : self.area}
+ def to_dict(self):
+ return {'id' : self.id,
+ 'area' : self.area}
class MessageMAU(object):
- """
- """
- def __init__(self, body, _id=None, _area=None, _seq=None, _add_list=None, _del_list=None, _exist_list=None):
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- self.mobile_seq = getMandatory(body, 'mobile_seq', long)
- self.add_list = getOptional(body, 'add', None, list)
- self.del_list = getOptional(body, 'del', None, list)
- self.exist_list = getOptional(body, 'exist', None, list)
- else:
- self.id = _id
- self.area = _area
- self.mobile_seq = long(_seq)
- self.add_list = _add_list
- self.del_list = _del_list
- self.exist_list = _exist_list
-
- def get_opcode(self):
- return 'MAU'
-
- def __repr__(self):
- _add = ''
- _del = ''
- _exist = ''
- if self.add_list: _add = ' add=%r' % self.add_list
- if self.del_list: _del = ' del=%r' % self.del_list
- if self.exist_list: _exist = ' exist=%r' % self.exist_list
- return "MAU(id=%s area=%s mobile_seq=%d%s%s%s)" % \
- (self.id, self.area, self.mobile_seq, _add, _del, _exist)
-
- def to_dict(self):
- body = { 'id' : self.id,
- 'area' : self.area,
- 'mobile_seq' : self.mobile_seq }
- if self.add_list: body['add'] = self.add_list
- if self.del_list: body['del'] = self.del_list
- if self.exist_list: body['exist'] = self.exist_list
- return body
+ """
+ """
+ def __init__(self, body, _id=None, _area=None, _seq=None, _add_list=None, _del_list=None, _exist_list=None):
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ self.mobile_seq = getMandatory(body, 'mobile_seq', long)
+ self.add_list = getOptional(body, 'add', None, list)
+ self.del_list = getOptional(body, 'del', None, list)
+ self.exist_list = getOptional(body, 'exist', None, list)
+ else:
+ self.id = _id
+ self.area = _area
+ self.mobile_seq = long(_seq)
+ self.add_list = _add_list
+ self.del_list = _del_list
+ self.exist_list = _exist_list
+
+ def get_opcode(self):
+ return 'MAU'
+
+ def __repr__(self):
+ _add = ''
+ _del = ''
+ _exist = ''
+ if self.add_list: _add = ' add=%r' % self.add_list
+ if self.del_list: _del = ' del=%r' % self.del_list
+ if self.exist_list: _exist = ' exist=%r' % self.exist_list
+ return "MAU(id=%s area=%s mobile_seq=%d%s%s%s)" % \
+ (self.id, self.area, self.mobile_seq, _add, _del, _exist)
+
+ def to_dict(self):
+ body = { 'id' : self.id,
+ 'area' : self.area,
+ 'mobile_seq' : self.mobile_seq }
+ if self.add_list: body['add'] = self.add_list
+ if self.del_list: body['del'] = self.del_list
+ if self.exist_list: body['exist'] = self.exist_list
+ return body
class MessageMAR(object):
- """
- """
- def __init__(self, body, _id=None, _area=None, _have_seq=None):
- if body:
- self.id = getMandatory(body, 'id', str)
- self.area = getMandatory(body, 'area', str)
- self.have_seq = getMandatory(body, 'have_seq', long)
- else:
- self.id = _id
- self.area = _area
- self.have_seq = long(_have_seq)
-
- def get_opcode(self):
- return 'MAR'
-
- def __repr__(self):
- return "MAR(id=%s area=%s have_seq=%d)" % (self.id, self.area, self.have_seq)
-
- def to_dict(self):
- return {'id' : self.id,
- 'area' : self.area,
- 'have_seq' : self.have_seq}
+ """
+ """
+ def __init__(self, body, _id=None, _area=None, _have_seq=None):
+ if body:
+ self.id = getMandatory(body, 'id', str)
+ self.area = getMandatory(body, 'area', str)
+ self.have_seq = getMandatory(body, 'have_seq', long)
+ else:
+ self.id = _id
+ self.area = _area
+ self.have_seq = long(_have_seq)
+
+ def get_opcode(self):
+ return 'MAR'
+
+ def __repr__(self):
+ return "MAR(id=%s area=%s have_seq=%d)" % (self.id, self.area, self.have_seq)
+
+ def to_dict(self):
+ return {'id' : self.id,
+ 'area' : self.area,
+ 'have_seq' : self.have_seq}
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/link.py b/qpid/extras/dispatch/python/qpid/dispatch/router/link.py
index fb23177e2f..11307cd079 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/link.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/link.py
@@ -21,123 +21,124 @@ from data import MessageRA, MessageLSU, MessageLSR
from time import time
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class LinkStateEngine(object):
- """
- This module is responsible for running the Link State protocol and maintaining the set
- of link states that are gathered from the domain. It notifies outbound when changes to
- the link-state-collection are detected.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.ra_interval = self.container.config.ra_interval
- self.remote_ls_max_age = self.container.config.remote_ls_max_age
- self.last_ra_time = 0
- self.collection = {}
- self.collection_changed = False
- self.mobile_seq = 0
- self.needed_lsrs = {}
-
-
- def tick(self, now):
- self._expire_ls(now)
- self._send_lsrs()
-
- if now - self.last_ra_time >= self.ra_interval:
- self.last_ra_time = now
- self._send_ra()
-
- if self.collection_changed:
- self.collection_changed = False
- self.container.log(LOG_INFO, "New Link-State Collection:")
- for a,b in self.collection.items():
- self.container.log(LOG_INFO, " %s => %r" % (a, b.peers))
- self.container.ls_collection_changed(self.collection)
-
-
- def handle_ra(self, msg, now):
- if msg.id == self.id:
- return
- if msg.id in self.collection:
- ls = self.collection[msg.id]
- ls.last_seen = now
- if ls.ls_seq < msg.ls_seq:
- self.needed_lsrs[(msg.area, msg.id)] = None
- else:
- self.needed_lsrs[(msg.area, msg.id)] = None
-
-
- def handle_lsu(self, msg, now):
- if msg.id == self.id:
- return
- if msg.id in self.collection:
- ls = self.collection[msg.id]
- if ls.ls_seq < msg.ls_seq:
- ls = msg.ls
- self.collection[msg.id] = ls
+ """
+ This module is responsible for running the Link State protocol and maintaining the set
+ of link states that are gathered from the domain. It notifies outbound when changes to
+ the link-state-collection are detected.
+ """
+ def __init__(self, container):
+ self.container = container
+ self.id = self.container.id
+ self.area = self.container.area
+ self.ra_interval = self.container.config.ra_interval
+ self.remote_ls_max_age = self.container.config.remote_ls_max_age
+ self.last_ra_time = 0
+ self.collection = {}
+ self.collection_changed = False
+ self.mobile_seq = 0
+ self.needed_lsrs = {}
+
+
+ def tick(self, now):
+ self._expire_ls(now)
+ self._send_lsrs()
+
+ if now - self.last_ra_time >= self.ra_interval:
+ self.last_ra_time = now
+ self._send_ra()
+
+ if self.collection_changed:
+ self.collection_changed = False
+ self.container.log(LOG_INFO, "New Link-State Collection:")
+ for a,b in self.collection.items():
+ self.container.log(LOG_INFO, " %s => %r" % (a, b.peers))
+ self.container.ls_collection_changed(self.collection)
+
+
+ def handle_ra(self, msg, now):
+ if msg.id == self.id:
+ return
+ if msg.id in self.collection:
+ ls = self.collection[msg.id]
+ ls.last_seen = now
+ if ls.ls_seq < msg.ls_seq:
+ self.needed_lsrs[(msg.area, msg.id)] = None
+ else:
+ self.needed_lsrs[(msg.area, msg.id)] = None
+
+
+ def handle_lsu(self, msg, now):
+ if msg.id == self.id:
+ return
+ if msg.id in self.collection:
+ ls = self.collection[msg.id]
+ if ls.ls_seq < msg.ls_seq:
+ ls = msg.ls
+ self.collection[msg.id] = ls
+ self.collection_changed = True
+ ls.last_seen = now
+ else:
+ ls = msg.ls
+ self.collection[msg.id] = ls
+ self.collection_changed = True
+ ls.last_seen = now
+ self.container.new_node(msg.id)
+ self.container.log(LOG_INFO, "Learned link-state from new router: %s" % msg.id)
+ # Schedule LSRs for any routers referenced in this LS that we don't know about
+ for _id in msg.ls.peers:
+ if _id not in self.collection:
+ self.container.new_node(_id)
+ self.needed_lsrs[(msg.area, _id)] = None
+
+
+ def handle_lsr(self, msg, now):
+ if msg.id == self.id:
+ return
+ if self.id not in self.collection:
+ return
+ my_ls = self.collection[self.id]
+ self.container.send('amqp:/_topo/%s/%s/qdxrouter' % (msg.area, msg.id), MessageLSU(None, self.id, self.area, my_ls.ls_seq, my_ls))
+
+
+ def new_local_link_state(self, link_state):
+ self.collection[self.id] = link_state
self.collection_changed = True
- ls.last_seen = now
- else:
- ls = msg.ls
- self.collection[msg.id] = ls
- self.collection_changed = True
- ls.last_seen = now
- self.container.new_node(msg.id)
- self.container.log(LOG_INFO, "Learned link-state from new router: %s" % msg.id)
- # Schedule LSRs for any routers referenced in this LS that we don't know about
- for _id in msg.ls.peers:
- if _id not in self.collection:
- self.needed_lsrs[(msg.area, _id)] = None
-
-
- def handle_lsr(self, msg, now):
- if msg.id == self.id:
- return
- if self.id not in self.collection:
- return
- my_ls = self.collection[self.id]
- self.container.send('_topo/%s/%s' % (msg.area, msg.id), MessageLSU(None, self.id, self.area, my_ls.ls_seq, my_ls))
-
-
- def new_local_link_state(self, link_state):
- self.collection[self.id] = link_state
- self.collection_changed = True
- self._send_ra()
-
-
- def set_mobile_sequence(self, seq):
- self.mobile_seq = seq
-
-
- def get_collection(self):
- return self.collection
-
-
- def _expire_ls(self, now):
- to_delete = []
- for key, ls in self.collection.items():
- if key != self.id and now - ls.last_seen > self.remote_ls_max_age:
- to_delete.append(key)
- for key in to_delete:
- ls = self.collection.pop(key)
- self.collection_changed = True
- self.container.lost_node(key)
- self.container.log(LOG_INFO, "Expired link-state from router: %s" % key)
-
-
- def _send_lsrs(self):
- for (_area, _id) in self.needed_lsrs.keys():
- self.container.send('_topo/%s/%s' % (_area, _id), MessageLSR(None, self.id, self.area))
- self.needed_lsrs = {}
-
-
- def _send_ra(self):
- ls_seq = 0
- if self.id in self.collection:
- ls_seq = self.collection[self.id].ls_seq
- self.container.send('_topo/%s/all' % self.area, MessageRA(None, self.id, self.area, ls_seq, self.mobile_seq))
+ self._send_ra()
+
+
+ def set_mobile_sequence(self, seq):
+ self.mobile_seq = seq
+
+
+ def get_collection(self):
+ return self.collection
+
+
+ def _expire_ls(self, now):
+ to_delete = []
+ for key, ls in self.collection.items():
+ if key != self.id and now - ls.last_seen > self.remote_ls_max_age:
+ to_delete.append(key)
+ for key in to_delete:
+ ls = self.collection.pop(key)
+ self.collection_changed = True
+ self.container.lost_node(key)
+ self.container.log(LOG_INFO, "Expired link-state from router: %s" % key)
+
+
+ def _send_lsrs(self):
+ for (_area, _id) in self.needed_lsrs.keys():
+ self.container.send('amqp:/_topo/%s/%s/qdxrouter' % (_area, _id), MessageLSR(None, self.id, self.area))
+ self.needed_lsrs = {}
+
+
+ def _send_ra(self):
+ ls_seq = 0
+ if self.id in self.collection:
+ ls_seq = self.collection[self.id].ls_seq
+ self.container.send('amqp:/_topo/%s/all/qdxrouter' % self.area, MessageRA(None, self.id, self.area, ls_seq, self.mobile_seq))
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/mobile.py b/qpid/extras/dispatch/python/qpid/dispatch/router/mobile.py
index ce80f38780..117cf98c22 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/mobile.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/mobile.py
@@ -20,169 +20,170 @@
from data import MessageRA, MessageMAR, MessageMAU
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class MobileAddressEngine(object):
- """
- This module is responsible for maintaining an up-to-date list of mobile addresses in the domain.
- It runs the Mobile-Address protocol and generates an un-optimized routing table for mobile addresses.
- Note that this routing table maps from the mobile address to the remote router where that address
- is directly bound.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.mobile_addr_max_age = self.container.config.mobile_addr_max_age
- self.mobile_seq = 0
- self.local_keys = []
- self.added_keys = []
- self.deleted_keys = []
- self.remote_lists = {} # map router_id => (sequence, list of keys)
- self.remote_last_seen = {} # map router_id => time of last seen advertizement/update
- self.remote_changed = False
- self.needed_mars = {}
-
-
- def tick(self, now):
- self._expire_remotes(now)
- self._send_mars()
-
- ##
- ## If local keys have changed, collect the changes and send a MAU with the diffs
- ## Note: it is important that the differential-MAU be sent before a RA is sent
- ##
- if len(self.added_keys) > 0 or len(self.deleted_keys) > 0:
- self.mobile_seq += 1
- self.container.send('_topo.%s.all' % self.area,
- MessageMAU(None, self.id, self.area, self.mobile_seq, self.added_keys, self.deleted_keys))
- self.local_keys.extend(self.added_keys)
- for key in self.deleted_keys:
- self.local_keys.remove(key)
- self.added_keys = []
- self.deleted_keys = []
- self.container.mobile_sequence_changed(self.mobile_seq)
-
- ##
- ## If remotes have changed, start the process of updating local bindings
- ##
- if self.remote_changed:
- self.remote_changed = False
- self._update_remote_keys()
-
-
- def add_local_address(self, key):
"""
+ This module is responsible for maintaining an up-to-date list of mobile addresses in the domain.
+ It runs the Mobile-Address protocol and generates an un-optimized routing table for mobile addresses.
+ Note that this routing table maps from the mobile address to the remote router where that address
+ is directly bound.
"""
- if self.local_keys.count(key) == 0:
- if self.added_keys.count(key) == 0:
- self.added_keys.append(key)
- else:
- if self.deleted_keys.count(key) > 0:
- self.deleted_keys.remove(key)
-
-
- def del_local_address(self, key):
- """
- """
- if self.local_keys.count(key) > 0:
- if self.deleted_keys.count(key) == 0:
- self.deleted_keys.append(key)
- else:
- if self.added_keys.count(key) > 0:
- self.added_keys.remove(key)
-
-
- def handle_ra(self, msg, now):
- if msg.id == self.id:
- return
-
- if msg.mobile_seq == 0:
- return
-
- if msg.id in self.remote_lists:
- _seq, _list = self.remote_lists[msg.id]
- self.remote_last_seen[msg.id] = now
- if _seq < msg.mobile_seq:
- self.needed_mars[(msg.id, msg.area, _seq)] = None
- else:
- self.needed_mars[(msg.id, msg.area, 0)] = None
-
-
- def handle_mau(self, msg, now):
- ##
- ## If the MAU is differential, we can only use it if its sequence is exactly one greater
- ## than our stored sequence. If not, we will ignore the content and schedule a MAR.
- ##
- ## If the MAU is absolute, we can use it in all cases.
- ##
- if msg.id == self.id:
- return
-
- if msg.exist_list:
- ##
- ## Absolute MAU
- ##
- if msg.id in self.remote_lists:
- _seq, _list = self.remote_lists[msg.id]
- if _seq >= msg.mobile_seq: # ignore duplicates
- return
- self.remote_lists[msg.id] = (msg.mobile_seq, msg.exist_list)
- self.remote_last_seen[msg.id] = now
- self.remote_changed = True
- else:
- ##
- ## Differential MAU
- ##
- if msg.id in self.remote_lists:
- _seq, _list = self.remote_lists[msg.id]
- if _seq == msg.mobile_seq: # ignore duplicates
- return
- self.remote_last_seen[msg.id] = now
- if _seq + 1 == msg.mobile_seq:
- ##
- ## This is one greater than our stored value, incorporate the deltas
- ##
- if msg.add_list and msg.add_list.__class__ == list:
- _list.extend(msg.add_list)
- if msg.del_list and msg.del_list.__class__ == list:
- for key in msg.del_list:
- _list.remove(key)
- self.remote_lists[msg.id] = (msg.mobile_seq, _list)
- self.remote_changed = True
+ def __init__(self, container, node_tracker):
+ self.container = container
+ self.node_tracker = node_tracker
+ self.id = self.container.id
+ self.area = self.container.area
+ self.mobile_addr_max_age = self.container.config.mobile_addr_max_age
+ self.mobile_seq = 0
+ self.local_addrs = []
+ self.added_addrs = []
+ self.deleted_addrs = []
+ self.remote_lists = {} # map router_id => (sequence, list of addrs)
+ self.remote_last_seen = {} # map router_id => time of last seen advertizement/update
+ self.needed_mars = {}
+
+
+ def tick(self, now):
+ self._expire_remotes(now)
+ self._send_mars()
+
+ ##
+ ## If local addrs have changed, collect the changes and send a MAU with the diffs
+ ## Note: it is important that the differential-MAU be sent before a RA is sent
+ ##
+ if len(self.added_addrs) > 0 or len(self.deleted_addrs) > 0:
+ self.mobile_seq += 1
+ self.container.send('amqp:/_topo/%s/all/qdxrouter' % self.area,
+ MessageMAU(None, self.id, self.area, self.mobile_seq, self.added_addrs, self.deleted_addrs))
+ self.local_addrs.extend(self.added_addrs)
+ for addr in self.deleted_addrs:
+ self.local_addrs.remove(addr)
+ self.added_addrs = []
+ self.deleted_addrs = []
+ self.container.mobile_sequence_changed(self.mobile_seq)
+
+
+ def add_local_address(self, addr):
+ """
+ """
+ if self.local_addrs.count(addr) == 0:
+ if self.added_addrs.count(addr) == 0:
+ self.added_addrs.append(addr)
else:
- self.needed_mars[(msg.id, msg.area, _seq)] = None
- else:
- self.needed_mars[(msg.id, msg.area, 0)] = None
-
+ if self.deleted_addrs.count(addr) > 0:
+ self.deleted_addrs.remove(addr)
- def handle_mar(self, msg, now):
- if msg.id == self.id:
- return
- if msg.have_seq < self.mobile_seq:
- self.container.send('_topo.%s.%s' % (msg.area, msg.id),
- MessageMAU(None, self.id, self.area, self.mobile_seq, None, None, self.local_keys))
-
- def _update_remote_keys(self):
- keys = {}
- for _id,(seq,key_list) in self.remote_lists.items():
- keys[_id] = key_list
- self.container.mobile_keys_changed(keys)
+ def del_local_address(self, addr):
+ """
+ """
+ if self.local_addrs.count(addr) > 0:
+ if self.deleted_addrs.count(addr) == 0:
+ self.deleted_addrs.append(addr)
+ else:
+ if self.added_addrs.count(addr) > 0:
+ self.added_addrs.remove(addr)
- def _expire_remotes(self, now):
- for _id, t in self.remote_last_seen.items():
- if now - t > self.mobile_addr_max_age:
- self.remote_lists.pop(_id)
- self.remote_last_seen.pop(_id)
- self.remote_changed = True
+ def handle_ra(self, msg, now):
+ if msg.id == self.id:
+ return
+ if msg.mobile_seq == 0:
+ return
- def _send_mars(self):
- for _id, _area, _seq in self.needed_mars.keys():
- self.container.send('_topo.%s.%s' % (_area, _id), MessageMAR(None, self.id, self.area, _seq))
- self.needed_mars = {}
+ if msg.id in self.remote_lists:
+ _seq, _list = self.remote_lists[msg.id]
+ self.remote_last_seen[msg.id] = now
+ if _seq < msg.mobile_seq:
+ self.needed_mars[(msg.id, msg.area, _seq)] = None
+ else:
+ self.needed_mars[(msg.id, msg.area, 0)] = None
+
+
+ def handle_mau(self, msg, now):
+ ##
+ ## If the MAU is differential, we can only use it if its sequence is exactly one greater
+ ## than our stored sequence. If not, we will ignore the content and schedule a MAR.
+ ##
+ ## If the MAU is absolute, we can use it in all cases.
+ ##
+ if msg.id == self.id:
+ return
+
+ if msg.exist_list:
+ ##
+ ## Absolute MAU
+ ##
+ if msg.id in self.remote_lists:
+ _seq, _list = self.remote_lists[msg.id]
+ if _seq >= msg.mobile_seq: # ignore duplicates
+ return
+ self.remote_lists[msg.id] = (msg.mobile_seq, msg.exist_list)
+ self.remote_last_seen[msg.id] = now
+ (add_list, del_list) = self.node_tracker.overwrite_addresses(msg.id, msg.exist_list)
+ self._activate_remotes(msg.id, add_list, del_list)
+ else:
+ ##
+ ## Differential MAU
+ ##
+ if msg.id in self.remote_lists:
+ _seq, _list = self.remote_lists[msg.id]
+ if _seq == msg.mobile_seq: # ignore duplicates
+ return
+ self.remote_last_seen[msg.id] = now
+ if _seq + 1 == msg.mobile_seq:
+ ##
+ ## This is one greater than our stored value, incorporate the deltas
+ ##
+ if msg.add_list and msg.add_list.__class__ == list:
+ _list.extend(msg.add_list)
+ if msg.del_list and msg.del_list.__class__ == list:
+ for addr in msg.del_list:
+ _list.remove(addr)
+ self.remote_lists[msg.id] = (msg.mobile_seq, _list)
+ if msg.add_list:
+ self.node_tracker.add_addresses(msg.id, msg.add_list)
+ if msg.del_list:
+ self.node_tracker.del_addresses(msg.id, msg.del_list)
+ self._activate_remotes(msg.id, msg.add_list, msg.del_list)
+ else:
+ self.needed_mars[(msg.id, msg.area, _seq)] = None
+ else:
+ self.needed_mars[(msg.id, msg.area, 0)] = None
+
+
+ def handle_mar(self, msg, now):
+ if msg.id == self.id:
+ return
+ if msg.have_seq < self.mobile_seq:
+ self.container.send('amqp:/_topo/%s/%s/qdxrouter' % (msg.area, msg.id),
+ MessageMAU(None, self.id, self.area, self.mobile_seq, None, None, self.local_addrs))
+
+
+ def _expire_remotes(self, now):
+ for _id, t in self.remote_last_seen.items():
+ if now - t > self.mobile_addr_max_age:
+ self.remote_lists.pop(_id)
+ self.remote_last_seen.pop(_id)
+ self.remote_changed = True
+
+
+ def _send_mars(self):
+ for _id, _area, _seq in self.needed_mars.keys():
+ self.container.send('amqp:/_topo/%s/%s/qdxrouter' % (_area, _id), MessageMAR(None, self.id, self.area, _seq))
+ self.needed_mars = {}
+
+
+ def _activate_remotes(self, _id, added, deleted):
+ bit = self.node_tracker.maskbit_for_node(_id)
+ if added:
+ for a in added:
+ self.container.router_adapter.map_destination(a, bit)
+ if deleted:
+ for d in deleted:
+ self.container.router_adapter.unmap_destination(d, bit)
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/neighbor.py b/qpid/extras/dispatch/python/qpid/dispatch/router/neighbor.py
index ea8bacd660..37b7888efe 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/neighbor.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/neighbor.py
@@ -21,63 +21,63 @@ from data import LinkState, MessageHELLO
from time import time
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class NeighborEngine(object):
- """
- This module is responsible for maintaining this router's link-state. It runs the HELLO protocol
- with the router's neighbors and notifies outbound when the list of neighbors-in-good-standing (the
- link-state) changes.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.last_hello_time = 0.0
- self.hello_interval = container.config.hello_interval
- self.hello_max_age = container.config.hello_max_age
- self.hellos = {}
- self.link_state_changed = False
- self.link_state = LinkState(None, self.id, self.area, 0, [])
+ """
+ This module is responsible for maintaining this router's link-state. It runs the HELLO protocol
+ with the router's neighbors and notifies outbound when the list of neighbors-in-good-standing (the
+ link-state) changes.
+ """
+ def __init__(self, container):
+ self.container = container
+ self.id = self.container.id
+ self.area = self.container.area
+ self.last_hello_time = 0.0
+ self.hello_interval = container.config.hello_interval
+ self.hello_max_age = container.config.hello_max_age
+ self.hellos = {}
+ self.link_state_changed = False
+ self.link_state = LinkState(None, self.id, self.area, 0, [])
- def tick(self, now):
- self._expire_hellos(now)
+ def tick(self, now):
+ self._expire_hellos(now)
- if now - self.last_hello_time >= self.hello_interval:
- self.last_hello_time = now
- self.container.send('_local/qdxrouter', MessageHELLO(None, self.id, self.area, self.hellos.keys()))
+ if now - self.last_hello_time >= self.hello_interval:
+ self.last_hello_time = now
+ self.container.send('amqp:/_local/qdxhello', MessageHELLO(None, self.id, self.area, self.hellos.keys()))
- if self.link_state_changed:
- self.link_state_changed = False
- self.link_state.bump_sequence()
- self.container.local_link_state_changed(self.link_state)
+ if self.link_state_changed:
+ self.link_state_changed = False
+ self.link_state.bump_sequence()
+ self.container.local_link_state_changed(self.link_state)
- def handle_hello(self, msg, now):
- if msg.id == self.id:
- return
- self.hellos[msg.id] = now
- if msg.is_seen(self.id):
- if self.link_state.add_peer(msg.id):
- self.link_state_changed = True
- self.container.new_neighbor(msg.id)
- self.container.log(LOG_INFO, "New neighbor established: %s" % msg.id)
- ##
- ## TODO - Use this function to detect area boundaries
- ##
+ def handle_hello(self, msg, now, link_id):
+ if msg.id == self.id:
+ return
+ self.hellos[msg.id] = now
+ if msg.is_seen(self.id):
+ if self.link_state.add_peer(msg.id):
+ self.link_state_changed = True
+ self.container.new_neighbor(msg.id, link_id)
+ self.container.log(LOG_INFO, "New neighbor established: %s on link: %d" % (msg.id, link_id))
+ ##
+ ## TODO - Use this function to detect area boundaries
+ ##
- def _expire_hellos(self, now):
- to_delete = []
- for key, last_seen in self.hellos.items():
- if now - last_seen > self.hello_max_age:
- to_delete.append(key)
- for key in to_delete:
- self.hellos.pop(key)
- if self.link_state.del_peer(key):
- self.link_state_changed = True
- self.container.lost_neighbor(key)
- self.container.log(LOG_INFO, "Neighbor lost: %s" % key)
+ def _expire_hellos(self, now):
+ to_delete = []
+ for key, last_seen in self.hellos.items():
+ if now - last_seen > self.hello_max_age:
+ to_delete.append(key)
+ for key in to_delete:
+ self.hellos.pop(key)
+ if self.link_state.del_peer(key):
+ self.link_state_changed = True
+ self.container.lost_neighbor(key)
+ self.container.log(LOG_INFO, "Neighbor lost: %s" % key)
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/node.py b/qpid/extras/dispatch/python/qpid/dispatch/router/node.py
index c90f7f4232..ef697428da 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/node.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/node.py
@@ -18,86 +18,157 @@
#
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class NodeTracker(object):
- """
- This module is responsible for tracking the set of router nodes that are known to this
- router. It tracks whether they are neighbor or remote and whether they are reachable.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.nodes = {} # id => RemoteNode
-
-
- def tick(self, now):
- pass
-
-
- def new_neighbor(self, node_id):
- if node_id not in self.nodes:
- self.nodes[node_id] = RemoteNode(node_id)
- self.nodes[node_id].set_neighbor()
- self._notify(self.nodes[node_id])
-
-
- def lost_neighbor(self, node_id):
- node = self.nodes[node_id]
- node.clear_neighbor()
- self._notify(node)
- if node.to_delete():
- self.nodes.pop(node_id)
-
-
- def new_node(self, node_id):
- if node_id not in self.nodes:
- self.nodes[node_id] = RemoteNode(node_id)
- self.nodes[node_id].set_remote()
- self._notify(self.nodes[node_id])
-
-
- def lost_node(self, node_id):
- node = self.nodes[node_id]
- node.clear_remote()
- self._notify(node)
- if node.to_delete():
- self.nodes.pop(node_id)
-
-
- def _notify(self, node):
- if node.to_delete():
- self.container.adapter.node_updated("R%s" % node.id, 0, 0)
- else:
- is_neighbor = 0
- if node.neighbor:
- is_neighbor = 1
- self.container.adapter.node_updated("R%s" % node.id, 1, is_neighbor)
+ """
+ This module is responsible for tracking the set of router nodes that are known to this
+ router. It tracks whether they are neighbor or remote and whether they are reachable.
+
+ This module is also responsible for assigning a unique mask bit value to each router.
+ The mask bit is used in the main router to represent sets of valid destinations for addresses.
+ """
+ def __init__(self, container, max_routers):
+ self.container = container
+ self.max_routers = max_routers
+ self.nodes = {} # id => RemoteNode
+ self.maskbits = []
+ self.next_maskbit = 1 # Reserve bit '0' to represent this router
+ for i in range(max_routers):
+ self.maskbits.append(None)
+ self.maskbits[0] = True
+
+
+ def tick(self, now):
+ pass
+
+
+ def new_neighbor(self, node_id, link_maskbit):
+ """
+ A node, designated by node_id, has been discovered as a neighbor over a link with
+ a maskbit of link_maskbit.
+ """
+ if node_id in self.nodes:
+ node = self.nodes[node_id]
+ if node.neighbor:
+ return
+ self.container.del_remote_router(node.maskbit)
+ node.neighbor = True
+ else:
+ node = RemoteNode(node_id, self._allocate_maskbit(), True)
+ self.nodes[node_id] = node
+ self.container.add_neighbor_router(self._address(node_id), node.maskbit, link_maskbit)
+
+
+ def lost_neighbor(self, node_id):
+ """
+ We have lost contact with a neighboring node node_id.
+ """
+ node = self.nodes[node_id]
+ node.neighbor = False
+ self.container.del_neighbor_router(node.maskbit)
+ if node.remote:
+ self.container.add_remote_router(self._address(node.id), node.maskbit)
+ else:
+ self._free_maskbit(node.maskbit)
+ self.nodes.pop(node_id)
+
+
+ def new_node(self, node_id):
+ """
+ A node, designated by node_id, has been discovered through the an advertisement from a
+ remote peer.
+ """
+ if node_id not in self.nodes:
+ node = RemoteNode(node_id, self._allocate_maskbit(), False)
+ self.nodes[node_id] = node
+ self.container.add_remote_router(self._address(node.id), node.maskbit)
+ else:
+ node = self.nodes[node_id]
+ node.remote = True
+
+
+ def lost_node(self, node_id):
+ """
+ A remote node, node_id, has not been heard from for too long and is being deemed lost.
+ """
+ node = self.nodes[node_id]
+ if node.remote:
+ node.remote = False
+ if not node.neighbor:
+ self.container.del_remote_router(node.maskbit)
+ self._free_maskbit(node.maskbit)
+ self.nodes.pop(node_id)
+
+
+ def maskbit_for_node(self, node_id):
+ """
+ """
+ node = self.nodes[node_id]
+ if node:
+ return node.maskbit
+ return None
+
+
+ def add_addresses(self, node_id, addrs):
+ node = self.nodes[node_id]
+ for a in addrs:
+ node.addrs[a] = 1
+
+
+ def del_addresses(self, node_id, addrs):
+ node = self.nodes[node_id]
+ for a in addrs:
+ node.addrs.pop(a)
+
+
+ def overwrite_addresses(self, node_id, addrs):
+ node = self.nodes[node_id]
+ added = []
+ deleted = []
+ for a in addrs:
+ if a not in node.addrs.keys():
+ added.append(a)
+ for a in node.addrs.keys():
+ if a not in addrs:
+ deleted.append(a)
+ for a in addrs:
+ node.addrs[a] = 1
+ return (added, deleted)
+
+
+ def _allocate_maskbit(self):
+ if self.next_maskbit == None:
+ raise Exception("Exceeded Maximum Router Count")
+ result = self.next_maskbit
+ self.next_maskbit = None
+ self.maskbits[result] = True
+ for n in range(result + 1, self.max_routers):
+ if self.maskbits[n] == None:
+ self.next_maskbit = n
+ break
+ return result
+
+
+ def _free_maskbit(self, i):
+ self.maskbits[i] = None
+ if self.next_maskbit == None or i < self.next_maskbit:
+ self.next_maskbit = i
+
+
+ def _address(self, node_id):
+ return "amqp:/_topo/%s/%s" % (self.container.area, node_id)
class RemoteNode(object):
- def __init__(self, node_id):
- self.id = node_id
- self.neighbor = None
- self.remote = None
-
- def set_neighbor(self):
- self.neighbor = True
-
- def set_remote(self):
- self.remote = True
-
- def clear_neighbor(self):
- self.neighbor = None
-
- def clear_remote(self):
- self.remote = None
-
- def to_delete(self):
- return self.neighbor or self.remote
+ def __init__(self, node_id, maskbit, neighbor):
+ self.id = node_id
+ self.maskbit = maskbit
+ self.neighbor = neighbor
+ self.remote = not neighbor
+ self.addrs = {} # Address => Count at Node (1 only for the present)
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/path.py b/qpid/extras/dispatch/python/qpid/dispatch/router/path.py
index c051dbe7fc..da04474e75 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/path.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/path.py
@@ -18,185 +18,218 @@
#
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class PathEngine(object):
- """
- This module is responsible for computing the next-hop for every router/area in the domain
- based on the collection of link states that have been gathered.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.recalculate = False
- self.collection = None
-
-
- def tick(self, now_unused):
- if self.recalculate:
- self.recalculate = False
- self._calculate_routes()
-
-
- def ls_collection_changed(self, collection):
- self.recalculate = True
- self.collection = collection
-
-
- def _calculate_tree_from_root(self, root):
- ##
- ## Make a copy of the current collection of link-states that contains
- ## an empty link-state for nodes that are known-peers but are not in the
- ## collection currently. This is needed to establish routes to those nodes
- ## so we can trade link-state information with them.
- ##
- link_states = {}
- for _id, ls in self.collection.items():
- link_states[_id] = ls.peers
- for p in ls.peers:
- if p not in link_states:
- link_states[p] = []
-
- ##
- ## Setup Dijkstra's Algorithm
- ##
- cost = {}
- prev = {}
- for _id in link_states:
- cost[_id] = None # infinite
- prev[_id] = None # undefined
- cost[root] = 0 # no cost to the root node
- unresolved = NodeSet(cost)
-
- ##
- ## Process unresolved nodes until lowest cost paths to all reachable nodes have been found.
- ##
- while not unresolved.empty():
- u = unresolved.lowest_cost()
- if cost[u] == None:
- # There are no more reachable nodes in unresolved
- break
- for v in link_states[u]:
- if unresolved.contains(v):
- alt = cost[u] + 1 # TODO - Use link cost instead of 1
- if cost[v] == None or alt < cost[v]:
- cost[v] = alt
- prev[v] = u
- unresolved.set_cost(v, alt)
-
- ##
- ## Remove unreachable nodes from the map. Note that this will also remove the
- ## root node (has no previous node) from the map.
- ##
- for u, val in prev.items():
- if not val:
- prev.pop(u)
-
- ##
- ## Return previous-node map. This is a map of all reachable, remote nodes to
- ## their predecessor node.
- ##
- return prev
-
-
- def _calculate_routes(self):
- ##
- ## Generate the shortest-path tree with the local node as root
- ##
- prev = self._calculate_tree_from_root(self.id)
- nodes = prev.keys()
-
- ##
- ## Distill the path tree into a map of next hops for each node
- ##
- next_hops = {}
- while len(nodes) > 0:
- u = nodes[0] # pick any destination
- path = [u]
- nodes.remove(u)
- v = prev[u]
- while v != self.id: # build a list of nodes in the path back to the root
- if v in nodes:
- path.append(v)
- nodes.remove(v)
- u = v
- v = prev[u]
- for w in path: # mark each node in the path as reachable via the next hop
- next_hops[w] = u
-
- ##
- ## TODO - Calculate the tree from each origin, determine the set of origins-per-dest
- ## for which the path from origin to dest passes through us. This is the set
- ## of valid origins for forwarding to the destination.
- ##
-
- self.container.next_hops_changed(next_hops)
+ """
+ This module is responsible for computing the next-hop for every router/area in the domain
+ based on the collection of link states that have been gathered.
+ """
+ def __init__(self, container):
+ self.container = container
+ self.id = self.container.id
+ self.area = self.container.area
+ self.recalculate = False
+ self.collection = None
-class NodeSet(object):
- """
- This data structure is an ordered list of node IDs, sorted in increasing order by their cost.
- Equal cost nodes are secondarily sorted by their ID in order to provide deterministic and
- repeatable ordering.
- """
- def __init__(self, cost_map):
- self.nodes = []
- for _id, cost in cost_map.items():
- ##
- ## Assume that nodes are either unreachable (cost = None) or local (cost = 0)
- ## during this initialization.
- ##
- if cost == 0:
- self.nodes.insert(0, (_id, cost))
- else:
+ def tick(self, now_unused):
+ if self.recalculate:
+ self.recalculate = False
+ self._calculate_routes()
+
+
+ def ls_collection_changed(self, collection):
+ self.recalculate = True
+ self.collection = collection
+
+
+ def _calculate_tree_from_root(self, root):
##
- ## There is no need to sort unreachable nodes by ID
+ ## Make a copy of the current collection of link-states that contains
+ ## a fake link-state for nodes that are known-peers but are not in the
+ ## collection currently. This is needed to establish routes to those nodes
+ ## so we can trade link-state information with them.
##
- self.nodes.append((_id, cost))
+ link_states = {}
+ for _id, ls in self.collection.items():
+ link_states[_id] = ls.peers
+ for p in ls.peers:
+ if p not in link_states:
+ link_states[p] = [_id]
+ ##
+ ## Setup Dijkstra's Algorithm
+ ##
+ cost = {}
+ prev = {}
+ for _id in link_states:
+ cost[_id] = None # infinite
+ prev[_id] = None # undefined
+ cost[root] = 0 # no cost to the root node
+ unresolved = NodeSet(cost)
- def __repr__(self):
- return self.nodes.__repr__()
+ ##
+ ## Process unresolved nodes until lowest cost paths to all reachable nodes have been found.
+ ##
+ while not unresolved.empty():
+ u = unresolved.lowest_cost()
+ if cost[u] == None:
+ # There are no more reachable nodes in unresolved
+ break
+ for v in link_states[u]:
+ if unresolved.contains(v):
+ alt = cost[u] + 1 # TODO - Use link cost instead of 1
+ if cost[v] == None or alt < cost[v]:
+ cost[v] = alt
+ prev[v] = u
+ unresolved.set_cost(v, alt)
+
+ ##
+ ## Remove unreachable nodes from the map. Note that this will also remove the
+ ## root node (has no previous node) from the map.
+ ##
+ for u, val in prev.items():
+ if not val:
+ prev.pop(u)
+ ##
+ ## Return previous-node map. This is a map of all reachable, remote nodes to
+ ## their predecessor node.
+ ##
+ return prev
- def empty(self):
- return len(self.nodes) == 0
+ def _calculate_valid_origins(self, nodeset):
+ ##
+ ## Calculate the tree from each origin, determine the set of origins-per-dest
+ ## for which the path from origin to dest passes through us. This is the set
+ ## of valid origins for forwarding to the destination.
+ ##
+ valid_origin = {} # Map of destination => List of Valid Origins
+ for node in nodeset:
+ if node != self.id:
+ valid_origin[node] = []
+
+ for root in valid_origin.keys():
+ prev = self._calculate_tree_from_root(root)
+ nodes = prev.keys()
+ while len(nodes) > 0:
+ u = nodes[0]
+ path = [u]
+ nodes.remove(u)
+ v = prev[u]
+ while v != root:
+ if v in nodes:
+ if v != self.id:
+ path.append(v)
+ nodes.remove(v)
+ if v == self.id:
+ valid_origin[root].extend(path)
+ u = v
+ v = prev[u]
+ return valid_origin
+
+
+ def _calculate_routes(self):
+ ##
+ ## Generate the shortest-path tree with the local node as root
+ ##
+ prev = self._calculate_tree_from_root(self.id)
+ nodes = prev.keys()
- def contains(self, _id):
- for a, b in self.nodes:
- if a == _id:
- return True
- return False
+ ##
+ ## Distill the path tree into a map of next hops for each node
+ ##
+ next_hops = {}
+ while len(nodes) > 0:
+ u = nodes[0] # pick any destination
+ path = [u]
+ nodes.remove(u)
+ v = prev[u]
+ while v != self.id: # build a list of nodes in the path back to the root
+ if v in nodes:
+ path.append(v)
+ nodes.remove(v)
+ u = v
+ v = prev[u]
+ for w in path: # mark each node in the path as reachable via the next hop
+ next_hops[w] = u
+
+ self.container.next_hops_changed(next_hops)
+ ##
+ ## Calculate the valid origins for remote routers
+ ##
+ valid_origin = self._calculate_valid_origins(prev.keys())
+ self.container.valid_origins_changed(valid_origin)
- def lowest_cost(self):
- """
- Remove and return the lowest cost node ID.
- """
- _id, cost = self.nodes.pop(0)
- return _id
- def set_cost(self, _id, new_cost):
+class NodeSet(object):
"""
- Set the cost for an ID in the NodeSet and re-insert the ID so that the list
- remains sorted in increasing cost order.
+ This data structure is an ordered list of node IDs, sorted in increasing order by their cost.
+ Equal cost nodes are secondarily sorted by their ID in order to provide deterministic and
+ repeatable ordering.
"""
- index = 0
- for i, c in self.nodes:
- if i == _id:
- break
- index += 1
- self.nodes.pop(index)
-
- index = 0
- for i, c in self.nodes:
- if c == None or new_cost < c or (new_cost == c and _id < i):
- break
- index += 1
-
- self.nodes.insert(index, (_id, new_cost))
+ def __init__(self, cost_map):
+ self.nodes = []
+ for _id, cost in cost_map.items():
+ ##
+ ## Assume that nodes are either unreachable (cost = None) or local (cost = 0)
+ ## during this initialization.
+ ##
+ if cost == 0:
+ self.nodes.insert(0, (_id, cost))
+ else:
+ ##
+ ## There is no need to sort unreachable nodes by ID
+ ##
+ self.nodes.append((_id, cost))
+
+
+ def __repr__(self):
+ return self.nodes.__repr__()
+
+
+ def empty(self):
+ return len(self.nodes) == 0
+
+
+ def contains(self, _id):
+ for a, b in self.nodes:
+ if a == _id:
+ return True
+ return False
+
+
+ def lowest_cost(self):
+ """
+ Remove and return the lowest cost node ID.
+ """
+ _id, cost = self.nodes.pop(0)
+ return _id
+
+
+ def set_cost(self, _id, new_cost):
+ """
+ Set the cost for an ID in the NodeSet and re-insert the ID so that the list
+ remains sorted in increasing cost order.
+ """
+ index = 0
+ for i, c in self.nodes:
+ if i == _id:
+ break
+ index += 1
+ self.nodes.pop(index)
+
+ index = 0
+ for i, c in self.nodes:
+ if c == None or new_cost < c or (new_cost == c and _id < i):
+ break
+ index += 1
+
+ self.nodes.insert(index, (_id, new_cost))
+
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/router_engine.py b/qpid/extras/dispatch/python/qpid/dispatch/router/router_engine.py
index 18b48379c5..f8b2dd9c94 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/router_engine.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/router_engine.py
@@ -27,254 +27,267 @@ from link import LinkStateEngine
from path import PathEngine
from mobile import MobileAddressEngine
from routing import RoutingTableEngine
-from binding import BindingEngine
-from adapter import AdapterEngine
from node import NodeTracker
+import sys
+import traceback
+
##
## Import the Dispatch adapters from the environment. If they are not found
## (i.e. we are in a test bench, etc.), load the stub versions.
##
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class RouterEngine:
- """
- """
-
- def __init__(self, router_adapter, router_id=None, area='area', config_override={}):
- """
- Initialize an instance of a router for a domain.
- """
- ##
- ## Record important information about this router instance
- ##
- self.domain = "domain"
- self.router_adapter = router_adapter
- self.log_adapter = LogAdapter("dispatch.router")
- self.io_adapter = IoAdapter(self, "qdxrouter")
-
- if router_id:
- self.id = router_id
- else:
- self.id = str(uuid4())
- self.area = area
- self.log(LOG_INFO, "Router Engine Instantiated: area=%s id=%s" % (self.area, self.id))
-
- ##
- ## Setup configuration
- ##
- self.config = Configuration(config_override)
- self.log(LOG_INFO, "Config: %r" % self.config)
-
- ##
- ## Launch the sub-module engines
- ##
- self.neighbor_engine = NeighborEngine(self)
- self.link_state_engine = LinkStateEngine(self)
- self.path_engine = PathEngine(self)
- self.mobile_address_engine = MobileAddressEngine(self)
- self.routing_table_engine = RoutingTableEngine(self)
- self.binding_engine = BindingEngine(self)
- self.adapter_engine = AdapterEngine(self)
- self.node_tracker = NodeTracker(self)
-
-
-
- ##========================================================================================
- ## Adapter Entry Points - invoked from the adapter
- ##========================================================================================
- def getId(self):
- """
- Return the router's ID
- """
- return self.id
-
-
- def addLocalAddress(self, key):
- """
- """
- try:
- if key.find('_topo') == 0 or key.find('_local') == 0:
- return
- self.mobile_address_engine.add_local_address(key)
- except Exception, e:
- self.log(LOG_ERROR, "Exception in new-address processing: exception=%r" % e)
-
- def delLocalAddress(self, key):
- """
- """
- try:
- if key.find('_topo') == 0 or key.find('_local') == 0:
- return
- self.mobile_address_engine.del_local_address(key)
- except Exception, e:
- self.log(LOG_ERROR, "Exception in del-address processing: exception=%r" % e)
-
-
- def handleTimerTick(self):
- """
- """
- try:
- now = time()
- self.neighbor_engine.tick(now)
- self.link_state_engine.tick(now)
- self.path_engine.tick(now)
- self.mobile_address_engine.tick(now)
- self.routing_table_engine.tick(now)
- self.binding_engine.tick(now)
- self.adapter_engine.tick(now)
- self.node_tracker.tick(now)
- except Exception, e:
- self.log(LOG_ERROR, "Exception in timer processing: exception=%r" % e)
-
-
- def handleControlMessage(self, opcode, body):
- """
- """
- try:
- now = time()
- if opcode == 'HELLO':
- msg = MessageHELLO(body)
- self.log(LOG_TRACE, "RCVD: %r" % msg)
- self.neighbor_engine.handle_hello(msg, now)
-
- elif opcode == 'RA':
- msg = MessageRA(body)
- self.log(LOG_TRACE, "RCVD: %r" % msg)
- self.link_state_engine.handle_ra(msg, now)
- self.mobile_address_engine.handle_ra(msg, now)
-
- elif opcode == 'LSU':
- msg = MessageLSU(body)
- self.log(LOG_TRACE, "RCVD: %r" % msg)
- self.link_state_engine.handle_lsu(msg, now)
-
- elif opcode == 'LSR':
- msg = MessageLSR(body)
- self.log(LOG_TRACE, "RCVD: %r" % msg)
- self.link_state_engine.handle_lsr(msg, now)
-
- elif opcode == 'MAU':
- msg = MessageMAU(body)
- self.log(LOG_TRACE, "RCVD: %r" % msg)
- self.mobile_address_engine.handle_mau(msg, now)
-
- elif opcode == 'MAR':
- msg = MessageMAR(body)
- self.log(LOG_TRACE, "RCVD: %r" % msg)
- self.mobile_address_engine.handle_mar(msg, now)
-
- except Exception, e:
- self.log(LOG_ERROR, "Exception in message processing: opcode=%s body=%r exception=%r" % (opcode, body, e))
-
-
- def receive(self, message_properties, body):
- """
- This is the IoAdapter message-receive handler
- """
- try:
- self.handleControlMessage(message_properties['opcode'], body)
- except Exception, e:
- self.log(LOG_ERROR, "Exception in raw message processing: properties=%r body=%r exception=%r" %
- (message_properties, body, e))
-
- def getRouterData(self, kind):
"""
"""
- if kind == 'help':
- return { 'help' : "Get list of supported values for kind",
- 'link-state' : "This router's link state",
- 'link-state-set' : "The set of link states from known routers",
- 'next-hops' : "Next hops to each known router",
- 'topo-table' : "Topological routing table",
- 'mobile-table' : "Mobile key routing table"
- }
- if kind == 'link-state' : return self.neighbor_engine.link_state.to_dict()
- if kind == 'next-hops' : return self.routing_table_engine.next_hops
- if kind == 'topo-table' : return {'table': self.adapter_engine.key_classes['topological']}
- if kind == 'mobile-table' : return {'table': self.adapter_engine.key_classes['mobile-key']}
- if kind == 'link-state-set' :
- copy = {}
- for _id,_ls in self.link_state_engine.collection.items():
- copy[_id] = _ls.to_dict()
- return copy
-
- return {'notice':'Use kind="help" to get a list of possibilities'}
-
-
- ##========================================================================================
- ## Adapter Calls - outbound calls to Dispatch
- ##========================================================================================
- def log(self, level, text):
- """
- Emit a log message to the host's event log
- """
- self.log_adapter.log(level, text)
-
-
- def send(self, dest, msg):
- """
- Send a control message to another router.
- """
- app_props = {'opcode' : msg.get_opcode() }
- self.io_adapter.send(dest, app_props, msg.to_dict())
- self.log(LOG_TRACE, "SENT: %r dest=%s" % (msg, dest))
-
-
- def node_updated(self, addr, reachable, neighbor):
- """
- """
- self.router_adapter(addr, reachable, neighbor)
-
-
- ##========================================================================================
- ## Interconnect between the Sub-Modules
- ##========================================================================================
- def local_link_state_changed(self, link_state):
- self.log(LOG_DEBUG, "Event: local_link_state_changed: %r" % link_state)
- self.link_state_engine.new_local_link_state(link_state)
-
- def ls_collection_changed(self, collection):
- self.log(LOG_DEBUG, "Event: ls_collection_changed: %r" % collection)
- self.path_engine.ls_collection_changed(collection)
-
- def next_hops_changed(self, next_hop_table):
- self.log(LOG_DEBUG, "Event: next_hops_changed: %r" % next_hop_table)
- self.routing_table_engine.next_hops_changed(next_hop_table)
- self.binding_engine.next_hops_changed()
-
- def mobile_sequence_changed(self, mobile_seq):
- self.log(LOG_DEBUG, "Event: mobile_sequence_changed: %d" % mobile_seq)
- self.link_state_engine.set_mobile_sequence(mobile_seq)
-
- def mobile_keys_changed(self, keys):
- self.log(LOG_DEBUG, "Event: mobile_keys_changed: %r" % keys)
- self.binding_engine.mobile_keys_changed(keys)
-
- def get_next_hops(self):
- return self.routing_table_engine.get_next_hops()
-
- def remote_routes_changed(self, key_class, routes):
- self.log(LOG_DEBUG, "Event: remote_routes_changed: class=%s routes=%r" % (key_class, routes))
- self.adapter_engine.remote_routes_changed(key_class, routes)
-
- def new_neighbor(self, rid):
- self.log(LOG_DEBUG, "Event: new_neighbor: id=%s" % rid)
- self.node_tracker.new_neighbor(rid)
-
- def lost_neighbor(self, rid):
- self.log(LOG_DEBUG, "Event: lost_neighbor: id=%s" % rid)
- self.node_tracker.lost_neighbor(rid)
-
- def new_node(self, rid):
- self.log(LOG_DEBUG, "Event: new_node: id=%s" % rid)
- self.node_tracker.new_node(rid)
- def lost_node(self, rid):
- self.log(LOG_DEBUG, "Event: lost_node: id=%s" % rid)
- self.node_tracker.lost_node(rid)
+ def __init__(self, router_adapter, router_id, area, max_routers, config_override={}):
+ """
+ Initialize an instance of a router for a domain.
+ """
+ ##
+ ## Record important information about this router instance
+ ##
+ self.domain = "domain"
+ self.router_adapter = router_adapter
+ self.log_adapter = LogAdapter("dispatch.router")
+ self.io_adapter = IoAdapter(self, ("qdxrouter", "qdxhello"))
+ self.max_routers = max_routers
+ self.id = router_id
+ self.area = area
+ self.log(LOG_INFO, "Router Engine Instantiated: area=%s id=%s max_routers=%d" %
+ (self.area, self.id, self.max_routers))
+
+ ##
+ ## Setup configuration
+ ##
+ self.config = Configuration(config_override)
+ self.log(LOG_INFO, "Config: %r" % self.config)
+
+ ##
+ ## Launch the sub-module engines
+ ##
+ self.node_tracker = NodeTracker(self, self.max_routers)
+ self.neighbor_engine = NeighborEngine(self)
+ self.link_state_engine = LinkStateEngine(self)
+ self.path_engine = PathEngine(self)
+ self.mobile_address_engine = MobileAddressEngine(self, self.node_tracker)
+ self.routing_table_engine = RoutingTableEngine(self, self.node_tracker)
+
+
+
+ ##========================================================================================
+ ## Adapter Entry Points - invoked from the adapter
+ ##========================================================================================
+ def getId(self):
+ """
+ Return the router's ID
+ """
+ return self.id
+
+
+ def addressAdded(self, addr):
+ """
+ """
+ try:
+ if addr.find('Mtemp.') == 0:
+ return
+ if addr.find('M') == 0:
+ self.mobile_address_engine.add_local_address(addr[1:])
+ except Exception, e:
+ self.log(LOG_ERROR, "Exception in new-address processing: exception=%r" % e)
+
+
+ def addressRemoved(self, addr):
+ """
+ """
+ try:
+ if addr.find('Mtemp.') == 0:
+ return
+ if addr.find('M') == 0:
+ self.mobile_address_engine.del_local_address(addr[1:])
+ except Exception, e:
+ self.log(LOG_ERROR, "Exception in del-address processing: exception=%r" % e)
+
+
+ def handleTimerTick(self):
+ """
+ """
+ try:
+ now = time()
+ self.neighbor_engine.tick(now)
+ self.link_state_engine.tick(now)
+ self.path_engine.tick(now)
+ self.mobile_address_engine.tick(now)
+ self.routing_table_engine.tick(now)
+ self.node_tracker.tick(now)
+ except Exception, e:
+ self.log(LOG_ERROR, "Exception in timer processing: exception=%r" % e)
+
+
+ def handleControlMessage(self, opcode, body, link_id):
+ """
+ """
+ try:
+ now = time()
+ if opcode == 'HELLO':
+ msg = MessageHELLO(body)
+ self.log(LOG_TRACE, "RCVD: %r" % msg)
+ self.neighbor_engine.handle_hello(msg, now, link_id)
+
+ elif opcode == 'RA':
+ msg = MessageRA(body)
+ self.log(LOG_DEBUG, "RCVD: %r" % msg)
+ self.link_state_engine.handle_ra(msg, now)
+ self.mobile_address_engine.handle_ra(msg, now)
+
+ elif opcode == 'LSU':
+ msg = MessageLSU(body)
+ self.log(LOG_DEBUG, "RCVD: %r" % msg)
+ self.link_state_engine.handle_lsu(msg, now)
+
+ elif opcode == 'LSR':
+ msg = MessageLSR(body)
+ self.log(LOG_DEBUG, "RCVD: %r" % msg)
+ self.link_state_engine.handle_lsr(msg, now)
+
+ elif opcode == 'MAU':
+ msg = MessageMAU(body)
+ self.log(LOG_DEBUG, "RCVD: %r" % msg)
+ self.mobile_address_engine.handle_mau(msg, now)
+
+ elif opcode == 'MAR':
+ msg = MessageMAR(body)
+ self.log(LOG_DEBUG, "RCVD: %r" % msg)
+ self.mobile_address_engine.handle_mar(msg, now)
+
+ except Exception, e:
+ self.log(LOG_ERROR, "Exception in message processing: opcode=%s body=%r exception=%r" % (opcode, body, e))
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ traceback.print_tb(exc_traceback)
+
+
+ def receive(self, message_properties, body, link_id):
+ """
+ This is the IoAdapter message-receive handler
+ """
+ try:
+ #self.log(LOG_DEBUG, "Raw Receive: mp=%r body=%r link_id=%r" % (message_properties, body, link_id))
+ self.handleControlMessage(message_properties['opcode'], body, link_id)
+ except Exception, e:
+ self.log(LOG_ERROR, "Exception in raw message processing: properties=%r body=%r exception=%r" %
+ (message_properties, body, e))
+
+
+ def getRouterData(self, kind):
+ """
+ """
+ if kind == 'help':
+ return { 'help' : "Get list of supported values for kind",
+ 'link-state' : "This router's link state",
+ 'link-state-set' : "The set of link states from known routers",
+ 'next-hops' : "Next hops to each known router"
+ }
+ if kind == 'link-state' : return self.neighbor_engine.link_state.to_dict()
+ if kind == 'next-hops' : return self.routing_table_engine.next_hops
+ if kind == 'link-state-set' :
+ copy = {}
+ for _id,_ls in self.link_state_engine.collection.items():
+ copy[_id] = _ls.to_dict()
+ return copy
+
+ return {'notice':'Use kind="help" to get a list of possibilities'}
+
+
+ ##========================================================================================
+ ## Adapter Calls - outbound calls to Dispatch
+ ##========================================================================================
+ def log(self, level, text):
+ """
+ Emit a log message to the host's event log
+ """
+ self.log_adapter.log(level, text)
+
+
+ def send(self, dest, msg):
+ """
+ Send a control message to another router.
+ """
+ app_props = {'opcode' : msg.get_opcode() }
+ self.io_adapter.send(dest, app_props, msg.to_dict())
+ if "qdxhello" in dest:
+ self.log(LOG_TRACE, "SENT: %r dest=%s" % (msg, dest))
+ else:
+ self.log(LOG_DEBUG, "SENT: %r dest=%s" % (msg, dest))
+
+
+ def node_updated(self, addr, reachable, neighbor):
+ """
+ """
+ self.router_adapter(addr, reachable, neighbor)
+
+
+ ##========================================================================================
+ ## Interconnect between the Sub-Modules
+ ##========================================================================================
+ def local_link_state_changed(self, link_state):
+ self.log(LOG_DEBUG, "Event: local_link_state_changed: %r" % link_state)
+ self.link_state_engine.new_local_link_state(link_state)
+
+ def ls_collection_changed(self, collection):
+ self.log(LOG_DEBUG, "Event: ls_collection_changed: %r" % collection)
+ self.path_engine.ls_collection_changed(collection)
+
+ def next_hops_changed(self, next_hop_table):
+ self.log(LOG_DEBUG, "Event: next_hops_changed: %r" % next_hop_table)
+ self.routing_table_engine.next_hops_changed(next_hop_table)
+
+ def valid_origins_changed(self, valid_origins):
+ self.log(LOG_DEBUG, "Event: valid_origins_changed: %r" % valid_origins)
+ self.routing_table_engine.valid_origins_changed(valid_origins)
+
+ def mobile_sequence_changed(self, mobile_seq):
+ self.log(LOG_DEBUG, "Event: mobile_sequence_changed: %d" % mobile_seq)
+ self.link_state_engine.set_mobile_sequence(mobile_seq)
+
+ def get_next_hops(self):
+ return self.routing_table_engine.get_next_hops()
+
+ def new_neighbor(self, rid, link_id):
+ self.log(LOG_DEBUG, "Event: new_neighbor: id=%s link_id=%d" % (rid, link_id))
+ self.node_tracker.new_neighbor(rid, link_id)
+
+ def lost_neighbor(self, rid):
+ self.log(LOG_DEBUG, "Event: lost_neighbor: id=%s" % rid)
+ self.node_tracker.lost_neighbor(rid)
+
+ def new_node(self, rid):
+ self.log(LOG_DEBUG, "Event: new_node: id=%s" % rid)
+ self.node_tracker.new_node(rid)
+
+ def lost_node(self, rid):
+ self.log(LOG_DEBUG, "Event: lost_node: id=%s" % rid)
+ self.node_tracker.lost_node(rid)
+
+ def add_neighbor_router(self, address, router_bit, link_bit):
+ self.log(LOG_DEBUG, "Event: add_neighbor_router: address=%s, router_bit=%d, link_bit=%d" % \
+ (address, router_bit, link_bit))
+ self.router_adapter.add_neighbor_router(address, router_bit, link_bit)
+
+ def del_neighbor_router(self, router_bit):
+ self.log(LOG_DEBUG, "Event: del_neighbor_router: router_bit=%d" % router_bit)
+ self.router_adapter.del_neighbor_router(router_bit)
+
+ def add_remote_router(self, address, router_bit):
+ self.log(LOG_DEBUG, "Event: add_remote_router: address=%s, router_bit=%d" % (address, router_bit))
+ self.router_adapter.add_remote_router(address, router_bit)
+
+ def del_remote_router(self, router_bit):
+ self.log(LOG_DEBUG, "Event: del_remote_router: router_bit=%d" % router_bit)
+ self.router_adapter.del_remote_router(router_bit)
diff --git a/qpid/extras/dispatch/python/qpid/dispatch/router/routing.py b/qpid/extras/dispatch/python/qpid/dispatch/router/routing.py
index 8030582177..a4b3e5484a 100644
--- a/qpid/extras/dispatch/python/qpid/dispatch/router/routing.py
+++ b/qpid/extras/dispatch/python/qpid/dispatch/router/routing.py
@@ -18,39 +18,45 @@
#
try:
- from dispatch import *
+ from dispatch import *
except ImportError:
- from ..stubs import *
+ from ..stubs import *
class RoutingTableEngine(object):
- """
- This module is responsible for converting the set of next hops to remote routers to a routing
- table in the "topological" address class.
- """
- def __init__(self, container):
- self.container = container
- self.id = self.container.id
- self.area = self.container.area
- self.next_hops = {}
-
-
- def tick(self, now):
- pass
-
-
- def next_hops_changed(self, next_hops):
- # Convert next_hops into routing table
- self.next_hops = next_hops
- new_table = []
- for _id, next_hop in next_hops.items():
- new_table.append(('_topo.%s.%s.#' % (self.area, _id), next_hop))
- pair = ('_topo.%s.all' % (self.area), next_hop)
- if new_table.count(pair) == 0:
- new_table.append(pair)
-
- self.container.remote_routes_changed('topological', new_table)
-
-
- def get_next_hops(self):
- return self.next_hops
+ """
+ This module is responsible for converting the set of next hops to remote routers to a routing
+ table in the "topological" address class.
+ """
+ def __init__(self, container, node_tracker):
+ self.container = container
+ self.node_tracker = node_tracker
+ self.id = self.container.id
+ self.area = self.container.area
+ self.next_hops = {}
+
+
+ def tick(self, now):
+ pass
+
+
+ def next_hops_changed(self, next_hops):
+ # Convert next_hops into routing table
+ self.next_hops = next_hops
+ for _id, next_hop in next_hops.items():
+ mb_id = self.node_tracker.maskbit_for_node(_id)
+ mb_nh = self.node_tracker.maskbit_for_node(next_hop)
+ self.container.router_adapter.set_next_hop(mb_id, mb_nh)
+
+
+ def valid_origins_changed(self, valid_origins):
+ for _id, vo in valid_origins.items():
+ mb_id = self.node_tracker.maskbit_for_node(_id)
+ mb_vo = []
+ for o in vo:
+ mb_vo.append(self.node_tracker.maskbit_for_node(o))
+ self.container.router_adapter.set_valid_origins(mb_id, mb_vo)
+
+
+ def get_next_hops(self):
+ return self.next_hops
diff --git a/qpid/extras/dispatch/router/CMakeLists.txt b/qpid/extras/dispatch/router/CMakeLists.txt
index ee6d1934cf..388e0d7119 100644
--- a/qpid/extras/dispatch/router/CMakeLists.txt
+++ b/qpid/extras/dispatch/router/CMakeLists.txt
@@ -18,7 +18,7 @@
##
-set(DEFAULT_CONFIG_PATH "/etc/qpid-dispatch.conf" CACHE string "Default Config File Path")
+set(DEFAULT_CONFIG_PATH "/etc/qpid/qpid-dispatch.conf" CACHE string "Default Config File Path")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/qpid/extras/dispatch/router/src/main.c b/qpid/extras/dispatch/router/src/main.c
index 62e3a2beb6..e0d6849d1b 100644
--- a/qpid/extras/dispatch/router/src/main.c
+++ b/qpid/extras/dispatch/router/src/main.c
@@ -117,7 +117,7 @@ int main(int argc, char **argv)
}
}
- dx_log_set_mask(0xFFFFFFFF);
+ dx_log_set_mask(0xFFFFFFFE);
dispatch = dx_dispatch(config_path);
diff --git a/qpid/extras/dispatch/src/agent.c b/qpid/extras/dispatch/src/agent.c
index 557410910c..a7475888b2 100644
--- a/qpid/extras/dispatch/src/agent.c
+++ b/qpid/extras/dispatch/src/agent.c
@@ -37,7 +37,7 @@
struct dx_agent_t {
dx_dispatch_t *dx;
- hash_t *class_hash;
+ dx_hash_t *class_hash;
dx_message_list_t in_fifo;
dx_message_list_t out_fifo;
sys_mutex_t *lock;
@@ -71,7 +71,7 @@ static void dx_agent_process_get(dx_agent_t *agent, dx_parsed_field_t *map, dx_f
dx_field_iterator_t *cls_string = dx_parse_raw(cls);
const dx_agent_class_t *cls_record;
- hash_retrieve_const(agent->class_hash, cls_string, (const void**) &cls_record);
+ dx_hash_retrieve_const(agent->class_hash, cls_string, (const void**) &cls_record);
if (cls_record == 0)
return;
@@ -138,11 +138,11 @@ static void dx_agent_process_get(dx_agent_t *agent, dx_parsed_field_t *map, dx_f
//
// Create a message and send it.
//
- dx_message_t *msg = dx_allocate_message();
+ dx_message_t *msg = dx_message();
dx_message_compose_2(msg, field);
dx_router_send(agent->dx, reply_to, msg);
- dx_free_message(msg);
+ dx_message_free(msg);
dx_compose_free(field);
}
@@ -234,13 +234,13 @@ static void dx_agent_deferred_handler(void *context)
if (msg) {
dx_agent_process_request(agent, msg);
- dx_free_message(msg);
+ dx_message_free(msg);
}
} while (msg);
}
-static void dx_agent_rx_handler(void *context, dx_message_t *msg)
+static void dx_agent_rx_handler(void *context, dx_message_t *msg, int unused_link_id)
{
dx_agent_t *agent = (dx_agent_t*) context;
dx_message_t *copy = dx_message_copy(msg);
@@ -257,12 +257,12 @@ dx_agent_t *dx_agent(dx_dispatch_t *dx)
{
dx_agent_t *agent = NEW(dx_agent_t);
agent->dx = dx;
- agent->class_hash = hash(6, 10, 1);
+ agent->class_hash = dx_hash(6, 10, 1);
DEQ_INIT(agent->in_fifo);
DEQ_INIT(agent->out_fifo);
agent->lock = sys_mutex();
agent->timer = dx_timer(dx, dx_agent_deferred_handler, agent);
- agent->address = dx_router_register_address(dx, "agent", dx_agent_rx_handler, agent);
+ agent->address = dx_router_register_address(dx, "$management", dx_agent_rx_handler, agent);
return agent;
}
@@ -273,7 +273,7 @@ void dx_agent_free(dx_agent_t *agent)
dx_router_unregister_address(agent->address);
sys_mutex_free(agent->lock);
dx_timer_free(agent->timer);
- hash_free(agent->class_hash);
+ dx_hash_free(agent->class_hash);
free(agent);
}
@@ -295,7 +295,7 @@ dx_agent_class_t *dx_agent_register_class(dx_dispatch_t *dx,
cls->query_handler = query_handler;
dx_field_iterator_t *iter = dx_field_iterator_string(fqname, ITER_VIEW_ALL);
- int result = hash_insert_const(agent->class_hash, iter, cls);
+ int result = dx_hash_insert_const(agent->class_hash, iter, cls, 0);
dx_field_iterator_free(iter);
if (result < 0)
assert(false);
diff --git a/qpid/extras/dispatch/src/amqp.c b/qpid/extras/dispatch/src/amqp.c
new file mode 100644
index 0000000000..6a8545b757
--- /dev/null
+++ b/qpid/extras/dispatch/src/amqp.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <qpid/dispatch/amqp.h>
+
+const char * const DX_DA_INGRESS = "qdx.ingress";
+const char * const DX_DA_TRACE = "qdx.trace";
+const char * const DX_DA_TO = "qdx.to";
+
+const char * const DX_CAPABILITY_ROUTER = "qdx.router";
+
+const char * const DX_INTERNODE_LINK_NAME_1 = "qdx.internode.1";
+const char * const DX_INTERNODE_LINK_NAME_2 = "qdx.internode.2";
+
diff --git a/qpid/extras/dispatch/src/bitmask.c b/qpid/extras/dispatch/src/bitmask.c
new file mode 100644
index 0000000000..88ba69dde1
--- /dev/null
+++ b/qpid/extras/dispatch/src/bitmask.c
@@ -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.
+ */
+
+#include <qpid/dispatch/bitmask.h>
+#include <qpid/dispatch/alloc.h>
+#include <assert.h>
+
+#define DX_BITMASK_LONGS 16
+#define DX_BITMASK_BITS (DX_BITMASK_LONGS * 64)
+
+struct dx_bitmask_t {
+ uint64_t array[DX_BITMASK_LONGS];
+ int first_set;
+};
+
+ALLOC_DECLARE(dx_bitmask_t);
+ALLOC_DEFINE(dx_bitmask_t);
+
+#define MASK_INDEX(num) (num / 64)
+#define MASK_ONEHOT(num) (((uint64_t) 1) << (num % 64))
+#define FIRST_NONE -1
+#define FIRST_UNKNOWN -2
+
+
+int dx_bitmask_width()
+{
+ return DX_BITMASK_BITS;
+}
+
+
+dx_bitmask_t *dx_bitmask(int initial)
+{
+ dx_bitmask_t *b = new_dx_bitmask_t();
+ if (initial)
+ dx_bitmask_set_all(b);
+ else
+ dx_bitmask_clear_all(b);
+ return b;
+}
+
+
+void dx_bitmask_free(dx_bitmask_t *b)
+{
+ free_dx_bitmask_t(b);
+}
+
+
+void dx_bitmask_set_all(dx_bitmask_t *b)
+{
+ for (int i = 0; i < DX_BITMASK_LONGS; i++)
+ b->array[i] = 0xFFFFFFFFFFFFFFFF;
+ b->first_set = 0;
+}
+
+
+void dx_bitmask_clear_all(dx_bitmask_t *b)
+{
+ for (int i = 0; i < DX_BITMASK_LONGS; i++)
+ b->array[i] = 0;
+ b->first_set = FIRST_NONE;
+}
+
+
+void dx_bitmask_set_bit(dx_bitmask_t *b, int bitnum)
+{
+ assert(bitnum < DX_BITMASK_BITS);
+ b->array[MASK_INDEX(bitnum)] |= MASK_ONEHOT(bitnum);
+ if (b->first_set > bitnum || b->first_set < 0)
+ b->first_set = bitnum;
+}
+
+
+void dx_bitmask_clear_bit(dx_bitmask_t *b, int bitnum)
+{
+ assert(bitnum < DX_BITMASK_BITS);
+ b->array[MASK_INDEX(bitnum)] &= ~(MASK_ONEHOT(bitnum));
+ if (b->first_set == bitnum)
+ b->first_set = FIRST_UNKNOWN;
+}
+
+
+int dx_bitmask_value(dx_bitmask_t *b, int bitnum)
+{
+ return (b->array[MASK_INDEX(bitnum)] & MASK_ONEHOT(bitnum)) ? 1 : 0;
+}
+
+
+int dx_bitmask_first_set(dx_bitmask_t *b, int *bitnum)
+{
+ if (b->first_set == FIRST_UNKNOWN) {
+ b->first_set = FIRST_NONE;
+ for (int i = 0; i < DX_BITMASK_LONGS; i++)
+ if (b->array[i]) {
+ for (int j = 0; j < 64; j++)
+ if ((((uint64_t) 1) << j) & b->array[i]) {
+ b->first_set = i * 64 + j;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (b->first_set == FIRST_NONE)
+ return 0;
+ *bitnum = b->first_set;
+ return 1;
+}
+
diff --git a/qpid/extras/dispatch/src/buffer.c b/qpid/extras/dispatch/src/buffer.c
index a5e609b996..d0bbd13cb3 100644
--- a/qpid/extras/dispatch/src/buffer.c
+++ b/qpid/extras/dispatch/src/buffer.c
@@ -34,7 +34,7 @@ void dx_buffer_set_size(size_t size)
}
-dx_buffer_t *dx_allocate_buffer(void)
+dx_buffer_t *dx_buffer(void)
{
size_locked = 1;
dx_buffer_t *buf = new_dx_buffer_t();
@@ -45,7 +45,7 @@ dx_buffer_t *dx_allocate_buffer(void)
}
-void dx_free_buffer(dx_buffer_t *buf)
+void dx_buffer_free(dx_buffer_t *buf)
{
free_dx_buffer_t(buf);
}
diff --git a/qpid/extras/dispatch/src/compose.c b/qpid/extras/dispatch/src/compose.c
index 6eb2ab1c73..66b2336e06 100644
--- a/qpid/extras/dispatch/src/compose.c
+++ b/qpid/extras/dispatch/src/compose.c
@@ -43,7 +43,7 @@ static void dx_insert(dx_composed_field_t *field, const uint8_t *seq, size_t len
while (len > 0) {
if (buf == 0 || dx_buffer_capacity(buf) == 0) {
- buf = dx_allocate_buffer();
+ buf = dx_buffer();
if (buf == 0)
return;
DEQ_INSERT_TAIL(field->buffers, buf);
@@ -115,8 +115,8 @@ static void dx_overwrite_32(dx_field_location_t *field, uint32_t value)
size_t cursor = field->offset;
dx_overwrite(&buf, &cursor, (uint8_t) ((value & 0xFF000000) >> 24));
- dx_overwrite(&buf, &cursor, (uint8_t) ((value & 0x00FF0000) >> 24));
- dx_overwrite(&buf, &cursor, (uint8_t) ((value & 0x0000FF00) >> 24));
+ dx_overwrite(&buf, &cursor, (uint8_t) ((value & 0x00FF0000) >> 16));
+ dx_overwrite(&buf, &cursor, (uint8_t) ((value & 0x0000FF00) >> 8));
dx_overwrite(&buf, &cursor, (uint8_t) (value & 0x000000FF));
}
@@ -212,7 +212,7 @@ void dx_compose_free(dx_composed_field_t *field)
dx_buffer_t *buf = DEQ_HEAD(field->buffers);
while (buf) {
DEQ_REMOVE_HEAD(field->buffers);
- dx_free_buffer(buf);
+ dx_buffer_free(buf);
buf = DEQ_HEAD(field->buffers);
}
diff --git a/qpid/extras/dispatch/src/container.c b/qpid/extras/dispatch/src/container.c
index cc46f3ce77..3ae24d81b0 100644
--- a/qpid/extras/dispatch/src/container.c
+++ b/qpid/extras/dispatch/src/container.c
@@ -88,8 +88,8 @@ typedef struct container_class_t {
struct dx_container_t {
dx_dispatch_t *dx;
dx_server_t *server;
- hash_t *node_type_map;
- hash_t *node_map;
+ dx_hash_t *node_type_map;
+ dx_hash_t *node_map;
sys_mutex_t *lock;
dx_node_t *default_node;
dxc_node_type_list_t node_type_list;
@@ -108,7 +108,7 @@ static void setup_outgoing_link(dx_container_t *container, pn_link_t *pn_link)
if (source) {
iter = dx_field_iterator_string(source, ITER_VIEW_NODE_ID);
- hash_retrieve(container->node_map, iter, (void*) &node);
+ dx_hash_retrieve(container->node_map, iter, (void*) &node);
dx_field_iterator_free(iter);
}
sys_mutex_unlock(container->lock);
@@ -149,7 +149,7 @@ static void setup_incoming_link(dx_container_t *container, pn_link_t *pn_link)
if (target) {
iter = dx_field_iterator_string(target, ITER_VIEW_NODE_ID);
- hash_retrieve(container->node_map, iter, (void*) &node);
+ dx_hash_retrieve(container->node_map, iter, (void*) &node);
dx_field_iterator_free(iter);
}
sys_mutex_unlock(container->lock);
@@ -429,8 +429,8 @@ static void container_query_handler(void* context, const char *id, void *correla
container_class_t *cls = (container_class_t*) context;
if (cls->class_id == DX_CONTAINER_CLASS_CONTAINER) {
- dx_agent_value_uint(correlator, "node_type_count", hash_size(cls->container->node_type_map));
- dx_agent_value_uint(correlator, "node_count", hash_size(cls->container->node_map));
+ dx_agent_value_uint(correlator, "node_type_count", dx_hash_size(cls->container->node_type_map));
+ dx_agent_value_uint(correlator, "node_count", dx_hash_size(cls->container->node_map));
if (cls->container->default_node)
dx_agent_value_string(correlator, "default_node_type", cls->container->default_node->ntype->type_name);
else
@@ -463,8 +463,8 @@ dx_container_t *dx_container(dx_dispatch_t *dx)
container->dx = dx;
container->server = dx->server;
- container->node_type_map = hash(6, 4, 1); // 64 buckets, item batches of 4
- container->node_map = hash(10, 32, 0); // 1K buckets, item batches of 32
+ container->node_type_map = dx_hash(6, 4, 1); // 64 buckets, item batches of 4
+ container->node_map = dx_hash(10, 32, 0); // 1K buckets, item batches of 32
container->lock = sys_mutex();
container->default_node = 0;
DEQ_INIT(container->node_type_list);
@@ -507,7 +507,7 @@ int dx_container_register_node_type(dx_dispatch_t *dx, const dx_node_type_t *nt)
nt_item->ntype = nt;
sys_mutex_lock(container->lock);
- result = hash_insert_const(container->node_type_map, iter, nt);
+ result = dx_hash_insert_const(container->node_type_map, iter, nt, 0);
DEQ_INSERT_TAIL(container->node_type_list, nt_item);
sys_mutex_unlock(container->lock);
@@ -565,7 +565,7 @@ dx_node_t *dx_container_create_node(dx_dispatch_t *dx,
if (name) {
dx_field_iterator_t *iter = dx_field_iterator_string(name, ITER_VIEW_ALL);
sys_mutex_lock(container->lock);
- result = hash_insert(container->node_map, iter, node);
+ result = dx_hash_insert(container->node_map, iter, node, 0);
sys_mutex_unlock(container->lock);
dx_field_iterator_free(iter);
if (result < 0) {
@@ -591,7 +591,7 @@ void dx_container_destroy_node(dx_node_t *node)
if (node->name) {
dx_field_iterator_t *iter = dx_field_iterator_string(node->name, ITER_VIEW_ALL);
sys_mutex_lock(container->lock);
- hash_remove(container->node_map, iter);
+ dx_hash_remove(container->node_map, iter);
sys_mutex_unlock(container->lock);
dx_field_iterator_free(iter);
free(node->name);
@@ -651,12 +651,63 @@ void *dx_link_get_context(dx_link_t *link)
}
+void dx_link_set_conn_context(dx_link_t *link, void *context)
+{
+ pn_session_t *pn_sess = pn_link_session(link->pn_link);
+ if (!pn_sess)
+ return;
+ pn_connection_t *pn_conn = pn_session_connection(pn_sess);
+ if (!pn_conn)
+ return;
+ dx_connection_t *conn = (dx_connection_t*) pn_connection_get_context(pn_conn);
+ if (!conn)
+ return;
+ dx_connection_set_link_context(conn, context);
+}
+
+
+void *dx_link_get_conn_context(dx_link_t *link)
+{
+ pn_session_t *pn_sess = pn_link_session(link->pn_link);
+ if (!pn_sess)
+ return 0;
+ pn_connection_t *pn_conn = pn_session_connection(pn_sess);
+ if (!pn_conn)
+ return 0;
+ dx_connection_t *conn = (dx_connection_t*) pn_connection_get_context(pn_conn);
+ if (!conn)
+ return 0;
+ return dx_connection_get_link_context(conn);
+}
+
+
pn_link_t *dx_link_pn(dx_link_t *link)
{
return link->pn_link;
}
+dx_connection_t *dx_link_connection(dx_link_t *link)
+{
+ if (!link || !link->pn_link)
+ return 0;
+
+ pn_session_t *sess = pn_link_session(link->pn_link);
+ if (!sess)
+ return 0;
+
+ pn_connection_t *conn = pn_session_connection(sess);
+ if (!conn)
+ return 0;
+
+ dx_connection_t *ctx = pn_connection_get_context(conn);
+ if (!ctx)
+ return 0;
+
+ return ctx;
+}
+
+
pn_terminus_t *dx_link_source(dx_link_t *link)
{
return pn_link_source(link->pn_link);
diff --git a/qpid/extras/dispatch/src/dispatch.c b/qpid/extras/dispatch/src/dispatch.c
index a1a659fd74..20ba84afe0 100644
--- a/qpid/extras/dispatch/src/dispatch.c
+++ b/qpid/extras/dispatch/src/dispatch.c
@@ -24,6 +24,7 @@
#include "dispatch_private.h"
#include "alloc_private.h"
#include "log_private.h"
+#include "router_private.h"
/**
* Private Function Prototypes
@@ -34,8 +35,8 @@ void dx_server_free(dx_server_t *server);
dx_container_t *dx_container(dx_dispatch_t *dx);
void dx_container_setup_agent(dx_dispatch_t *dx);
void dx_container_free(dx_container_t *container);
-dx_router_t *dx_router(dx_dispatch_t *dx, const char *area, const char *id);
-void dx_router_setup_agent(dx_dispatch_t *dx);
+dx_router_t *dx_router(dx_dispatch_t *dx, dx_router_mode_t mode, const char *area, const char *id);
+void dx_router_setup_late(dx_dispatch_t *dx);
void dx_router_free(dx_router_t *router);
dx_agent_t *dx_agent(dx_dispatch_t *dx);
void dx_agent_free(dx_agent_t *agent);
@@ -53,10 +54,13 @@ dx_dispatch_t *dx_dispatch(const char *config_path)
{
dx_dispatch_t *dx = NEW(dx_dispatch_t);
- int thread_count = 0;
- const char *container_name = 0;
- const char *router_area = 0;
- const char *router_id = 0;
+ int thread_count = 0;
+ const char *container_name = 0;
+ const char *router_mode_str = 0;
+ const char *router_area = 0;
+ const char *router_id = 0;
+
+ dx_router_mode_t router_mode = DX_ROUTER_MODE_STANDALONE;
DEQ_INIT(dx->config_listeners);
DEQ_INIT(dx->config_connectors);
@@ -78,8 +82,9 @@ dx_dispatch_t *dx_dispatch(const char *config_path)
count = dx_config_item_count(dx->config, CONF_ROUTER);
if (count == 1) {
- router_area = dx_config_item_value_string(dx->config, CONF_ROUTER, 0, "area");
- router_id = dx_config_item_value_string(dx->config, CONF_ROUTER, 0, "router-id");
+ router_mode_str = dx_config_item_value_string(dx->config, CONF_ROUTER, 0, "mode");
+ router_area = dx_config_item_value_string(dx->config, CONF_ROUTER, 0, "area");
+ router_id = dx_config_item_value_string(dx->config, CONF_ROUTER, 0, "router-id");
}
}
@@ -89,6 +94,12 @@ dx_dispatch_t *dx_dispatch(const char *config_path)
if (!container_name)
container_name = "00000000-0000-0000-0000-000000000000"; // TODO - gen a real uuid
+ if (router_mode_str && strcmp(router_mode_str, "interior") == 0)
+ router_mode = DX_ROUTER_MODE_INTERIOR;
+
+ if (router_mode_str && strcmp(router_mode_str, "edge") == 0)
+ router_mode = DX_ROUTER_MODE_EDGE;
+
if (!router_area)
router_area = "area";
@@ -97,13 +108,13 @@ dx_dispatch_t *dx_dispatch(const char *config_path)
dx->server = dx_server(thread_count, container_name);
dx->container = dx_container(dx);
- dx->router = dx_router(dx, router_area, router_id);
+ dx->router = dx_router(dx, router_mode, router_area, router_id);
dx->agent = dx_agent(dx);
dx_alloc_setup_agent(dx);
dx_server_setup_agent(dx);
dx_container_setup_agent(dx);
- dx_router_setup_agent(dx);
+ dx_router_setup_late(dx);
return dx;
}
@@ -126,6 +137,7 @@ static void load_server_config(dx_dispatch_t *dx, dx_server_config_t *config, co
{
config->host = dx_config_item_value_string(dx->config, section, i, "addr");
config->port = dx_config_item_value_string(dx->config, section, i, "port");
+ config->role = dx_config_item_value_string(dx->config, section, i, "role");
config->sasl_mechanisms =
dx_config_item_value_string(dx->config, section, i, "sasl-mechanisms");
config->ssl_enabled =
diff --git a/qpid/extras/dispatch/src/hash.c b/qpid/extras/dispatch/src/hash.c
index 1f7d8aa3f5..0cb32acd05 100644
--- a/qpid/extras/dispatch/src/hash.c
+++ b/qpid/extras/dispatch/src/hash.c
@@ -23,18 +23,18 @@
#include <stdio.h>
#include <string.h>
-typedef struct hash_item_t {
- DEQ_LINKS(struct hash_item_t);
+typedef struct dx_hash_item_t {
+ DEQ_LINKS(struct dx_hash_item_t);
unsigned char *key;
union {
void *val;
const void *val_const;
} v;
-} hash_item_t;
+} dx_hash_item_t;
-ALLOC_DECLARE(hash_item_t);
-ALLOC_DEFINE(hash_item_t);
-DEQ_DECLARE(hash_item_t, items_t);
+ALLOC_DECLARE(dx_hash_item_t);
+ALLOC_DEFINE(dx_hash_item_t);
+DEQ_DECLARE(dx_hash_item_t, items_t);
typedef struct bucket_t {
@@ -42,7 +42,7 @@ typedef struct bucket_t {
} bucket_t;
-struct hash_t {
+struct dx_hash_t {
bucket_t *buckets;
unsigned int bucket_count;
unsigned int bucket_mask;
@@ -52,8 +52,17 @@ struct hash_t {
};
+struct dx_hash_handle_t {
+ bucket_t *bucket;
+ dx_hash_item_t *item;
+};
+
+ALLOC_DECLARE(dx_hash_handle_t);
+ALLOC_DEFINE(dx_hash_handle_t);
+
+
// djb2 hash algorithm
-static unsigned long hash_function(dx_field_iterator_t *iter)
+static unsigned long dx_hash_function(dx_field_iterator_t *iter)
{
unsigned long hash = 5381;
int c;
@@ -68,10 +77,10 @@ static unsigned long hash_function(dx_field_iterator_t *iter)
}
-hash_t *hash(int bucket_exponent, int batch_size, int value_is_const)
+dx_hash_t *dx_hash(int bucket_exponent, int batch_size, int value_is_const)
{
int i;
- hash_t *h = NEW(hash_t);
+ dx_hash_t *h = NEW(dx_hash_t);
if (!h)
return 0;
@@ -90,22 +99,22 @@ hash_t *hash(int bucket_exponent, int batch_size, int value_is_const)
}
-void hash_free(hash_t *h)
+void dx_hash_free(dx_hash_t *h)
{
// TODO - Implement this
}
-size_t hash_size(hash_t *h)
+size_t dx_hash_size(dx_hash_t *h)
{
return h ? h->size : 0;
}
-static hash_item_t *hash_internal_insert(hash_t *h, dx_field_iterator_t *key, int *exists)
+static dx_hash_item_t *dx_hash_internal_insert(dx_hash_t *h, dx_field_iterator_t *key, int *exists, dx_hash_handle_t **handle)
{
- unsigned long idx = hash_function(key) & h->bucket_mask;
- hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
+ unsigned long idx = dx_hash_function(key) & h->bucket_mask;
+ dx_hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
if (dx_field_iterator_equal(key, item->key))
@@ -115,10 +124,12 @@ static hash_item_t *hash_internal_insert(hash_t *h, dx_field_iterator_t *key, in
if (item) {
*exists = 1;
+ if (handle)
+ *handle = 0;
return item;
}
- item = new_hash_item_t();
+ item = new_dx_hash_item_t();
if (!item)
return 0;
@@ -128,14 +139,24 @@ static hash_item_t *hash_internal_insert(hash_t *h, dx_field_iterator_t *key, in
DEQ_INSERT_TAIL(h->buckets[idx].items, item);
h->size++;
*exists = 0;
+
+ //
+ // If a pointer to a handle-pointer was supplied, create a handle for this item.
+ //
+ if (handle) {
+ *handle = new_dx_hash_handle_t();
+ (*handle)->bucket = &h->buckets[idx];
+ (*handle)->item = item;
+ }
+
return item;
}
-dx_error_t hash_insert(hash_t *h, dx_field_iterator_t *key, void *val)
+dx_error_t dx_hash_insert(dx_hash_t *h, dx_field_iterator_t *key, void *val, dx_hash_handle_t **handle)
{
- int exists = 0;
- hash_item_t *item = hash_internal_insert(h, key, &exists);
+ int exists = 0;
+ dx_hash_item_t *item = dx_hash_internal_insert(h, key, &exists, handle);
if (!item)
return DX_ERROR_ALLOC;
@@ -149,12 +170,12 @@ dx_error_t hash_insert(hash_t *h, dx_field_iterator_t *key, void *val)
}
-dx_error_t hash_insert_const(hash_t *h, dx_field_iterator_t *key, const void *val)
+dx_error_t dx_hash_insert_const(dx_hash_t *h, dx_field_iterator_t *key, const void *val, dx_hash_handle_t **handle)
{
assert(h->is_const);
- int error = 0;
- hash_item_t *item = hash_internal_insert(h, key, &error);
+ int error = 0;
+ dx_hash_item_t *item = dx_hash_internal_insert(h, key, &error, handle);
if (item)
item->v.val_const = val;
@@ -162,10 +183,10 @@ dx_error_t hash_insert_const(hash_t *h, dx_field_iterator_t *key, const void *va
}
-static hash_item_t *hash_internal_retrieve(hash_t *h, dx_field_iterator_t *key)
+static dx_hash_item_t *dx_hash_internal_retrieve(dx_hash_t *h, dx_field_iterator_t *key)
{
- unsigned long idx = hash_function(key) & h->bucket_mask;
- hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
+ unsigned long idx = dx_hash_function(key) & h->bucket_mask;
+ dx_hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
if (dx_field_iterator_equal(key, item->key))
@@ -177,9 +198,9 @@ static hash_item_t *hash_internal_retrieve(hash_t *h, dx_field_iterator_t *key)
}
-dx_error_t hash_retrieve(hash_t *h, dx_field_iterator_t *key, void **val)
+dx_error_t dx_hash_retrieve(dx_hash_t *h, dx_field_iterator_t *key, void **val)
{
- hash_item_t *item = hash_internal_retrieve(h, key);
+ dx_hash_item_t *item = dx_hash_internal_retrieve(h, key);
if (item)
*val = item->v.val;
else
@@ -189,11 +210,11 @@ dx_error_t hash_retrieve(hash_t *h, dx_field_iterator_t *key, void **val)
}
-dx_error_t hash_retrieve_const(hash_t *h, dx_field_iterator_t *key, const void **val)
+dx_error_t dx_hash_retrieve_const(dx_hash_t *h, dx_field_iterator_t *key, const void **val)
{
assert(h->is_const);
- hash_item_t *item = hash_internal_retrieve(h, key);
+ dx_hash_item_t *item = dx_hash_internal_retrieve(h, key);
if (item)
*val = item->v.val_const;
else
@@ -203,10 +224,10 @@ dx_error_t hash_retrieve_const(hash_t *h, dx_field_iterator_t *key, const void *
}
-dx_error_t hash_remove(hash_t *h, dx_field_iterator_t *key)
+dx_error_t dx_hash_remove(dx_hash_t *h, dx_field_iterator_t *key)
{
- unsigned long idx = hash_function(key) & h->bucket_mask;
- hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
+ unsigned long idx = dx_hash_function(key) & h->bucket_mask;
+ dx_hash_item_t *item = DEQ_HEAD(h->buckets[idx].items);
while (item) {
if (dx_field_iterator_equal(key, item->key))
@@ -217,7 +238,7 @@ dx_error_t hash_remove(hash_t *h, dx_field_iterator_t *key)
if (item) {
free(item->key);
DEQ_REMOVE(h->buckets[idx].items, item);
- free_hash_item_t(item);
+ free_dx_hash_item_t(item);
h->size--;
return DX_ERROR_NONE;
}
@@ -225,3 +246,40 @@ dx_error_t hash_remove(hash_t *h, dx_field_iterator_t *key)
return DX_ERROR_NOT_FOUND;
}
+
+void dx_hash_handle_free(dx_hash_handle_t *handle)
+{
+ if (handle)
+ free_dx_hash_handle_t(handle);
+}
+
+
+const unsigned char *dx_hash_key_by_handle(const dx_hash_handle_t *handle)
+{
+ if (handle)
+ return handle->item->key;
+ return 0;
+}
+
+
+dx_error_t dx_hash_remove_by_handle(dx_hash_t *h, dx_hash_handle_t *handle)
+{
+ unsigned char *key = 0;
+ dx_error_t error = dx_hash_remove_by_handle2(h, handle, &key);
+ if (key)
+ free(key);
+ return error;
+}
+
+
+dx_error_t dx_hash_remove_by_handle2(dx_hash_t *h, dx_hash_handle_t *handle, unsigned char **key)
+{
+ if (!handle)
+ return DX_ERROR_NOT_FOUND;
+ *key = handle->item->key;
+ DEQ_REMOVE(handle->bucket->items, handle->item);
+ free_dx_hash_item_t(handle->item);
+ h->size--;
+ return DX_ERROR_NONE;
+}
+
diff --git a/qpid/extras/dispatch/src/message.c b/qpid/extras/dispatch/src/message.c
index 772c0d4e16..86ffcefcee 100644
--- a/qpid/extras/dispatch/src/message.c
+++ b/qpid/extras/dispatch/src/message.c
@@ -376,7 +376,7 @@ static dx_field_location_t *dx_message_field_location(dx_message_t *msg, dx_mess
}
-dx_message_t *dx_allocate_message()
+dx_message_t *dx_message()
{
dx_message_pvt_t *msg = (dx_message_pvt_t*) new_dx_message_t();
if (!msg)
@@ -400,7 +400,7 @@ dx_message_t *dx_allocate_message()
}
-void dx_free_message(dx_message_t *in_msg)
+void dx_message_free(dx_message_t *in_msg)
{
uint32_t rc;
dx_message_pvt_t *msg = (dx_message_pvt_t*) in_msg;
@@ -417,14 +417,14 @@ void dx_free_message(dx_message_t *in_msg)
dx_buffer_t *buf = DEQ_HEAD(content->buffers);
while (buf) {
DEQ_REMOVE_HEAD(content->buffers);
- dx_free_buffer(buf);
+ dx_buffer_free(buf);
buf = DEQ_HEAD(content->buffers);
}
buf = DEQ_HEAD(content->new_delivery_annotations);
while (buf) {
DEQ_REMOVE_HEAD(content->new_delivery_annotations);
- dx_free_buffer(buf);
+ dx_buffer_free(buf);
buf = DEQ_HEAD(content->new_delivery_annotations);
}
@@ -474,9 +474,11 @@ dx_parsed_field_t *dx_message_delivery_annotations(dx_message_t *in_msg)
!dx_parse_is_map(content->parsed_delivery_annotations)) {
dx_field_iterator_free(da);
dx_parse_free(content->parsed_delivery_annotations);
+ content->parsed_delivery_annotations = 0;
return 0;
}
+ dx_field_iterator_free(da);
return content->parsed_delivery_annotations;
}
@@ -506,7 +508,7 @@ dx_message_t *dx_message_receive(dx_delivery_t *delivery)
// link it and the delivery together.
//
if (!msg) {
- msg = (dx_message_pvt_t*) dx_allocate_message();
+ msg = (dx_message_pvt_t*) dx_message();
dx_delivery_set_context(delivery, (void*) msg);
}
@@ -517,7 +519,7 @@ dx_message_t *dx_message_receive(dx_delivery_t *delivery)
//
buf = DEQ_TAIL(msg->content->buffers);
if (!buf) {
- buf = dx_allocate_buffer();
+ buf = dx_buffer();
DEQ_INSERT_TAIL(msg->content->buffers, buf);
}
@@ -538,7 +540,7 @@ dx_message_t *dx_message_receive(dx_delivery_t *delivery)
//
if (dx_buffer_size(buf) == 0) {
DEQ_REMOVE_TAIL(msg->content->buffers);
- dx_free_buffer(buf);
+ dx_buffer_free(buf);
}
dx_delivery_set_context(delivery, 0);
return (dx_message_t*) msg;
@@ -556,7 +558,7 @@ dx_message_t *dx_message_receive(dx_delivery_t *delivery)
// tail of the message's list.
//
if (dx_buffer_capacity(buf) == 0) {
- buf = dx_allocate_buffer();
+ buf = dx_buffer();
DEQ_INSERT_TAIL(msg->content->buffers, buf);
}
} else
diff --git a/qpid/extras/dispatch/src/message_private.h b/qpid/extras/dispatch/src/message_private.h
index 27b81bbb4c..c57cea5f0d 100644
--- a/qpid/extras/dispatch/src/message_private.h
+++ b/qpid/extras/dispatch/src/message_private.h
@@ -67,7 +67,7 @@ typedef struct {
sys_mutex_t *lock;
uint32_t ref_count; // The number of messages referencing this
dx_buffer_list_t buffers; // The buffer chain containing the message
- dx_buffer_list_t new_delivery_annotations; // The buffer chain containing the new delivery annotations
+ dx_buffer_list_t new_delivery_annotations; // The buffer chain containing the new delivery annotations (MOVE TO MSG_PVT)
dx_field_location_t section_message_header; // The message header list
dx_field_location_t section_delivery_annotation; // The delivery annotation map
dx_field_location_t section_message_annotation; // The message annotation map
diff --git a/qpid/extras/dispatch/src/python_embedded.c b/qpid/extras/dispatch/src/python_embedded.c
index 0b0cc11025..a6e35018c6 100644
--- a/qpid/extras/dispatch/src/python_embedded.c
+++ b/qpid/extras/dispatch/src/python_embedded.c
@@ -398,11 +398,12 @@ typedef struct {
PyObject *handler;
PyObject *handler_rx_call;
dx_dispatch_t *dx;
- dx_address_t *address;
+ Py_ssize_t addr_count;
+ dx_address_t **addrs;
} IoAdapter;
-static void dx_io_rx_handler(void *context, dx_message_t *msg)
+static void dx_io_rx_handler(void *context, dx_message_t *msg, int link_id)
{
IoAdapter *self = (IoAdapter*) context;
@@ -451,42 +452,60 @@ static void dx_io_rx_handler(void *context, dx_message_t *msg)
return;
}
+ sys_mutex_lock(lock);
PyObject *pAP = dx_field_to_py(ap_map);
PyObject *pBody = dx_field_to_py(body_map);
- PyObject *pArgs = PyTuple_New(2);
+ PyObject *pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, pAP);
PyTuple_SetItem(pArgs, 1, pBody);
+ PyTuple_SetItem(pArgs, 2, PyInt_FromLong((long) link_id));
PyObject *pValue = PyObject_CallObject(self->handler_rx_call, pArgs);
Py_DECREF(pArgs);
if (pValue) {
Py_DECREF(pValue);
}
+ sys_mutex_unlock(lock);
+
+ dx_field_iterator_free(ap);
+ dx_field_iterator_free(body);
+ dx_parse_free(ap_map);
+ dx_parse_free(body_map);
}
static int IoAdapter_init(IoAdapter *self, PyObject *args, PyObject *kwds)
{
- const char *address;
- if (!PyArg_ParseTuple(args, "Os", &self->handler, &address))
+ PyObject *addrs;
+ if (!PyArg_ParseTuple(args, "OO", &self->handler, &addrs))
return -1;
self->handler_rx_call = PyObject_GetAttrString(self->handler, "receive");
if (!self->handler_rx_call || !PyCallable_Check(self->handler_rx_call))
return -1;
+ if (!PyTuple_Check(addrs))
+ return -1;
+
Py_INCREF(self->handler);
Py_INCREF(self->handler_rx_call);
- self->dx = dispatch;
- self->address = dx_router_register_address(self->dx, address, dx_io_rx_handler, self);
+ self->dx = dispatch;
+ self->addr_count = PyTuple_Size(addrs);
+ self->addrs = NEW_PTR_ARRAY(dx_address_t, self->addr_count);
+ for (Py_ssize_t idx = 0; idx < self->addr_count; idx++)
+ self->addrs[idx] = dx_router_register_address(self->dx,
+ PyString_AS_STRING(PyTuple_GetItem(addrs, idx)),
+ dx_io_rx_handler, self);
return 0;
}
static void IoAdapter_dealloc(IoAdapter* self)
{
- dx_router_unregister_address(self->address);
+ for (Py_ssize_t idx = 0; idx < self->addr_count; idx++)
+ dx_router_unregister_address(self->addrs[idx]);
+ free(self->addrs);
Py_DECREF(self->handler);
Py_DECREF(self->handler_rx_call);
self->ob_type->tp_free((PyObject*)self);
@@ -507,10 +526,10 @@ static PyObject* dx_python_send(PyObject *self, PyObject *args)
field = dx_compose(DX_PERFORMATIVE_DELIVERY_ANNOTATIONS, field);
dx_compose_start_map(field);
- dx_compose_insert_string(field, "qdx.ingress");
+ dx_compose_insert_string(field, DX_DA_INGRESS);
dx_compose_insert_string(field, dx_router_id(ioa->dx));
- dx_compose_insert_string(field, "qdx.trace");
+ dx_compose_insert_string(field, DX_DA_TRACE);
dx_compose_start_list(field);
dx_compose_insert_string(field, dx_router_id(ioa->dx));
dx_compose_end_list(field);
@@ -530,10 +549,10 @@ static PyObject* dx_python_send(PyObject *self, PyObject *args)
field = dx_compose(DX_PERFORMATIVE_BODY_AMQP_VALUE, field);
dx_py_to_composed(body, field);
- dx_message_t *msg = dx_allocate_message();
+ dx_message_t *msg = dx_message();
dx_message_compose_2(msg, field);
dx_router_send2(ioa->dx, address, msg);
- dx_free_message(msg);
+ dx_message_free(msg);
dx_compose_free(field);
Py_INCREF(Py_None);
@@ -625,7 +644,8 @@ static void dx_python_setup()
//
// Add LogAdapter
//
- Py_INCREF(&LogAdapterType);
+ PyTypeObject *laType = &LogAdapterType;
+ Py_INCREF(laType);
PyModule_AddObject(m, "LogAdapter", (PyObject*) &LogAdapterType);
dx_register_log_constant(m, "LOG_TRACE", LOG_TRACE);
@@ -637,10 +657,22 @@ static void dx_python_setup()
dx_register_log_constant(m, "LOG_CRITICAL", LOG_CRITICAL);
//
- Py_INCREF(&IoAdapterType);
+ PyTypeObject *ioaType = &IoAdapterType;
+ Py_INCREF(ioaType);
PyModule_AddObject(m, "IoAdapter", (PyObject*) &IoAdapterType);
Py_INCREF(m);
dispatch_module = m;
}
}
+
+void dx_python_lock()
+{
+ sys_mutex_lock(lock);
+}
+
+void dx_python_unlock()
+{
+ sys_mutex_unlock(lock);
+}
+
diff --git a/qpid/extras/dispatch/src/router_agent.c b/qpid/extras/dispatch/src/router_agent.c
new file mode 100644
index 0000000000..d4b719732c
--- /dev/null
+++ b/qpid/extras/dispatch/src/router_agent.c
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#include <qpid/dispatch/python_embedded.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <qpid/dispatch.h>
+#include <qpid/dispatch/agent.h>
+#include "dispatch_private.h"
+#include "router_private.h"
+
+//static char *module = "router.agent";
+
+#define DX_ROUTER_CLASS_ROUTER 1
+#define DX_ROUTER_CLASS_LINK 2
+#define DX_ROUTER_CLASS_NODE 3
+#define DX_ROUTER_CLASS_ADDRESS 4
+
+typedef struct dx_router_class_t {
+ dx_router_t *router;
+ int class_id;
+} dx_router_class_t;
+
+
+static void dx_router_schema_handler(void *context, void *correlator)
+{
+}
+
+
+static const char *dx_router_addr_text(dx_address_t *addr)
+{
+ if (addr) {
+ const unsigned char *text = dx_hash_key_by_handle(addr->hash_handle);
+ if (text)
+ return (const char*) text;
+ }
+ return 0;
+}
+
+
+static void dx_router_query_router(dx_router_t *router, void *cor)
+{
+ dx_agent_value_string(cor, "area", router->router_area);
+ dx_agent_value_string(cor, "router_id", router->router_id);
+
+ sys_mutex_lock(router->lock);
+ dx_agent_value_uint(cor, "addr_count", DEQ_SIZE(router->addrs));
+ dx_agent_value_uint(cor, "link_count", DEQ_SIZE(router->links));
+ dx_agent_value_uint(cor, "node_count", DEQ_SIZE(router->routers));
+ sys_mutex_unlock(router->lock);
+
+ dx_agent_value_complete(cor, 0);
+}
+
+
+static void dx_router_query_link(dx_router_t *router, void *cor)
+{
+ sys_mutex_lock(router->lock);
+ dx_router_link_t *link = DEQ_HEAD(router->links);
+ const char *link_type = "?";
+ const char *link_dir;
+
+ while (link) {
+ dx_agent_value_uint(cor, "index", link->mask_bit);
+ switch (link->link_type) {
+ case DX_LINK_ENDPOINT: link_type = "endpoint"; break;
+ case DX_LINK_ROUTER: link_type = "inter-router"; break;
+ case DX_LINK_AREA: link_type = "inter-area"; break;
+ }
+ dx_agent_value_string(cor, "link-type", link_type);
+
+ if (link->link_direction == DX_INCOMING)
+ link_dir = "in";
+ else
+ link_dir = "out";
+ dx_agent_value_string(cor, "link-dir", link_dir);
+
+ const char *text = dx_router_addr_text(link->owning_addr);
+ if (text)
+ dx_agent_value_string(cor, "owning-addr", text);
+ else
+ dx_agent_value_null(cor, "owning-addr");
+
+ link = DEQ_NEXT(link);
+ dx_agent_value_complete(cor, link != 0);
+ }
+ sys_mutex_unlock(router->lock);
+}
+
+
+static void dx_router_query_node(dx_router_t *router, void *cor)
+{
+ sys_mutex_lock(router->lock);
+ dx_router_node_t *node = DEQ_HEAD(router->routers);
+ while (node) {
+ dx_agent_value_uint(cor, "index", node->mask_bit);
+ dx_agent_value_string(cor, "addr", dx_router_addr_text(node->owning_addr));
+ if (node->next_hop)
+ dx_agent_value_uint(cor, "next-hop", node->next_hop->mask_bit);
+ else
+ dx_agent_value_null(cor, "next-hop");
+ if (node->peer_link)
+ dx_agent_value_uint(cor, "router-link", node->peer_link->mask_bit);
+ else
+ dx_agent_value_null(cor, "router-link");
+ node = DEQ_NEXT(node);
+ dx_agent_value_complete(cor, node != 0);
+ }
+ sys_mutex_unlock(router->lock);
+}
+
+
+static void dx_router_query_address(dx_router_t *router, void *cor)
+{
+ sys_mutex_lock(router->lock);
+ dx_address_t *addr = DEQ_HEAD(router->addrs);
+ while (addr) {
+ dx_agent_value_string(cor, "addr", dx_router_addr_text(addr));
+ dx_agent_value_boolean(cor, "in-process", addr->handler != 0);
+ dx_agent_value_uint(cor, "subscriber-count", DEQ_SIZE(addr->rlinks));
+ dx_agent_value_uint(cor, "remote-count", DEQ_SIZE(addr->rnodes));
+ dx_agent_value_uint(cor, "deliveries-ingress", addr->deliveries_ingress);
+ dx_agent_value_uint(cor, "deliveries-egress", addr->deliveries_egress);
+ dx_agent_value_uint(cor, "deliveries-transit", addr->deliveries_transit);
+ dx_agent_value_uint(cor, "deliveries-to-container", addr->deliveries_to_container);
+ dx_agent_value_uint(cor, "deliveries-from-container", addr->deliveries_from_container);
+ addr = DEQ_NEXT(addr);
+ dx_agent_value_complete(cor, addr != 0);
+ }
+ sys_mutex_unlock(router->lock);
+}
+
+
+static void dx_router_query_handler(void* context, const char *id, void *correlator)
+{
+ dx_router_class_t *cls = (dx_router_class_t*) context;
+ switch (cls->class_id) {
+ case DX_ROUTER_CLASS_ROUTER: dx_router_query_router(cls->router, correlator); break;
+ case DX_ROUTER_CLASS_LINK: dx_router_query_link(cls->router, correlator); break;
+ case DX_ROUTER_CLASS_NODE: dx_router_query_node(cls->router, correlator); break;
+ case DX_ROUTER_CLASS_ADDRESS: dx_router_query_address(cls->router, correlator); break;
+ }
+}
+
+
+static dx_agent_class_t *dx_router_setup_class(dx_router_t *router, const char *fqname, int id)
+{
+ dx_router_class_t *cls = NEW(dx_router_class_t);
+ cls->router = router;
+ cls->class_id = id;
+
+ return dx_agent_register_class(router->dx, fqname, cls,
+ dx_router_schema_handler,
+ dx_router_query_handler);
+}
+
+
+void dx_router_agent_setup(dx_router_t *router)
+{
+ router->class_router =
+ dx_router_setup_class(router, "org.apache.qpid.dispatch.router", DX_ROUTER_CLASS_ROUTER);
+ router->class_link =
+ dx_router_setup_class(router, "org.apache.qpid.dispatch.router.link", DX_ROUTER_CLASS_LINK);
+ router->class_node =
+ dx_router_setup_class(router, "org.apache.qpid.dispatch.router.node", DX_ROUTER_CLASS_NODE);
+ router->class_address =
+ dx_router_setup_class(router, "org.apache.qpid.dispatch.router.address", DX_ROUTER_CLASS_ADDRESS);
+}
+
diff --git a/qpid/extras/dispatch/src/router_node.c b/qpid/extras/dispatch/src/router_node.c
index d2704c4bd5..931f4551bb 100644
--- a/qpid/extras/dispatch/src/router_node.c
+++ b/qpid/extras/dispatch/src/router_node.c
@@ -21,17 +21,17 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
+#include <stdlib.h>
#include <qpid/dispatch.h>
#include "dispatch_private.h"
+#include "router_private.h"
static char *module = "ROUTER";
-static void dx_router_python_setup(dx_router_t *router);
-static void dx_pyrouter_tick(dx_router_t *router);
-
-static char *router_address = "_local/qdxrouter";
-static char *local_prefix = "_local/";
-//static char *topo_prefix = "_topo/";
+static char *router_role = "inter-router";
+static char *local_prefix = "_local/";
+static char *topo_prefix = "_topo/";
+static char *direct_prefix;
/**
* Address Types and Processing:
@@ -48,86 +48,197 @@ static char *local_prefix = "_local/";
* <mobile> M<mobile> forward handler
*/
+ALLOC_DEFINE(dx_routed_event_t);
+ALLOC_DEFINE(dx_router_link_t);
+ALLOC_DEFINE(dx_router_node_t);
+ALLOC_DEFINE(dx_router_ref_t);
+ALLOC_DEFINE(dx_router_link_ref_t);
+ALLOC_DEFINE(dx_address_t);
+ALLOC_DEFINE(dx_router_conn_t);
-typedef struct dx_router_link_t dx_router_link_t;
-typedef struct dx_router_node_t dx_router_node_t;
+void dx_router_add_link_ref_LH(dx_router_link_ref_list_t *ref_list, dx_router_link_t *link)
+{
+ dx_router_link_ref_t *ref = new_dx_router_link_ref_t();
+ DEQ_ITEM_INIT(ref);
+ ref->link = link;
+ link->ref = ref;
+ DEQ_INSERT_TAIL(*ref_list, ref);
+}
-typedef enum {
- DX_LINK_ENDPOINT, // A link to a connected endpoint
- DX_LINK_ROUTER, // A link to a peer router in the same area
- DX_LINK_AREA // A link to a peer router in a different area (area boundary)
-} dx_link_type_t;
+void dx_router_del_link_ref_LH(dx_router_link_ref_list_t *ref_list, dx_router_link_t *link)
+{
+ if (link->ref) {
+ DEQ_REMOVE(*ref_list, link->ref);
+ free_dx_router_link_ref_t(link->ref);
+ link->ref = 0;
+ }
+}
-typedef struct dx_routed_event_t {
- DEQ_LINKS(struct dx_routed_event_t);
- dx_delivery_t *delivery;
- dx_message_t *message;
- bool settle;
- uint64_t disposition;
-} dx_routed_event_t;
-ALLOC_DECLARE(dx_routed_event_t);
-ALLOC_DEFINE(dx_routed_event_t);
-DEQ_DECLARE(dx_routed_event_t, dx_routed_event_list_t);
-
-
-struct dx_router_link_t {
- DEQ_LINKS(dx_router_link_t);
- dx_direction_t link_direction;
- dx_link_type_t link_type;
- dx_address_t *owning_addr; // [ref] Address record that owns this link
- dx_link_t *link; // [own] Link pointer
- dx_router_link_t *connected_link; // [ref] If this is a link-route, reference the connected link
- dx_router_link_t *peer_link; // [ref] If this is a bidirectional link-route, reference the peer link
- dx_routed_event_list_t event_fifo; // FIFO of outgoing delivery/link events (no messages)
- dx_routed_event_list_t msg_fifo; // FIFO of outgoing message deliveries
-};
-
-ALLOC_DECLARE(dx_router_link_t);
-ALLOC_DEFINE(dx_router_link_t);
-DEQ_DECLARE(dx_router_link_t, dx_router_link_list_t);
+void dx_router_add_node_ref_LH(dx_router_ref_list_t *ref_list, dx_router_node_t *rnode)
+{
+ dx_router_ref_t *ref = new_dx_router_ref_t();
+ DEQ_ITEM_INIT(ref);
+ ref->router = rnode;
+ rnode->ref_count++;
+ DEQ_INSERT_TAIL(*ref_list, ref);
+}
-struct dx_router_node_t {
- DEQ_LINKS(dx_router_node_t);
- const char *id;
- dx_router_node_t *next_hop; // Next hop node _if_ this is not a neighbor node
- dx_router_link_t *peer_link; // Outgoing link _if_ this is a neighbor node
- // list of valid origins (pointers to router_node) - (bit masks?)
-};
-ALLOC_DECLARE(dx_router_node_t);
-ALLOC_DEFINE(dx_router_node_t);
-DEQ_DECLARE(dx_router_node_t, dx_router_node_list_t);
+void dx_router_del_node_ref_LH(dx_router_ref_list_t *ref_list, dx_router_node_t *rnode)
+{
+ dx_router_ref_t *ref = DEQ_HEAD(*ref_list);
+ while (ref) {
+ if (ref->router == rnode) {
+ DEQ_REMOVE(*ref_list, ref);
+ free_dx_router_ref_t(ref);
+ rnode->ref_count--;
+ break;
+ }
+ ref = DEQ_NEXT(ref);
+ }
+}
-struct dx_address_t {
- dx_router_message_cb handler; // In-Process Consumer
- void *handler_context;
- dx_router_link_list_t rlinks; // Locally-Connected Consumers
- dx_router_node_list_t rnodes; // Remotely-Connected Consumers
-};
+/**
+ * Check an address to see if it no longer has any associated destinations.
+ * Depending on its policy, the address may be eligible for being closed out
+ * (i.e. Logging its terminal statistics and freeing its resources).
+ */
+void dx_router_check_addr(dx_router_t *router, dx_address_t *addr, int was_local)
+{
+ if (addr == 0)
+ return;
-ALLOC_DECLARE(dx_address_t);
-ALLOC_DEFINE(dx_address_t);
+ unsigned char *key = 0;
+ int to_delete = 0;
+ int no_more_locals = 0;
+
+ sys_mutex_lock(router->lock);
+
+ //
+ // If the address has no handlers or destinations, it should be deleted.
+ //
+ if (addr->handler == 0 && DEQ_SIZE(addr->rlinks) == 0 && DEQ_SIZE(addr->rnodes) == 0)
+ to_delete = 1;
+
+ //
+ // If we have just removed a local linkage and it was the last local linkage,
+ // we need to notify the router module that there is no longer a local
+ // presence of this address.
+ //
+ if (was_local && DEQ_SIZE(addr->rlinks) == 0)
+ no_more_locals = 1;
+
+ if (to_delete) {
+ //
+ // Delete the address but grab the hash key so we can use it outside the
+ // critical section.
+ //
+ dx_hash_remove_by_handle2(router->addr_hash, addr->hash_handle, &key);
+ DEQ_REMOVE(router->addrs, addr);
+ dx_hash_handle_free(addr->hash_handle);
+ free_dx_address_t(addr);
+ }
+
+ //
+ // If we're not deleting but there are no more locals, get a copy of the hash key.
+ //
+ if (!to_delete && no_more_locals) {
+ const unsigned char *key_const = dx_hash_key_by_handle(addr->hash_handle);
+ key = (unsigned char*) malloc(strlen((const char*) key_const) + 1);
+ strcpy((char*) key, (const char*) key_const);
+ }
+
+ sys_mutex_unlock(router->lock);
+ //
+ // If the address is mobile-class and it was just removed from a local link,
+ // tell the router module that it is no longer attached locally.
+ //
+ if (no_more_locals && key && key[0] == 'M')
+ dx_router_mobile_removed(router, (const char*) key);
-struct dx_router_t {
- dx_dispatch_t *dx;
- const char *router_area;
- const char *router_id;
- dx_node_t *node;
- dx_router_link_list_t in_links;
- dx_router_node_list_t routers;
- dx_message_list_t in_fifo;
- sys_mutex_t *lock;
- dx_timer_t *timer;
- hash_t *out_hash;
- uint64_t dtag;
- PyObject *pyRouter;
- PyObject *pyTick;
-};
+ //
+ // Free the key that was not freed by the hash table.
+ //
+ if (key)
+ free(key);
+}
+
+
+/**
+ * Determine whether a connection is configured in the inter-router role.
+ */
+static int dx_router_connection_is_inter_router(const dx_connection_t *conn)
+{
+ if (!conn)
+ return 0;
+
+ const dx_server_config_t *cf = dx_connection_config(conn);
+ if (cf && strcmp(cf->role, router_role) == 0)
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ * Determine whether a terminus has router capability
+ */
+static int dx_router_terminus_is_router(pn_terminus_t *term)
+{
+ pn_data_t *cap = pn_terminus_capabilities(term);
+
+ pn_data_rewind(cap);
+ pn_data_next(cap);
+ if (cap && pn_data_type(cap) == PN_SYMBOL) {
+ pn_bytes_t sym = pn_data_get_symbol(cap);
+ if (sym.size == strlen(DX_CAPABILITY_ROUTER) &&
+ strcmp(sym.start, DX_CAPABILITY_ROUTER) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void dx_router_generate_temp_addr(dx_router_t *router, char *buffer, size_t length)
+{
+ static const char *table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+_";
+ char discriminator[11];
+ long int rnd = random();
+ int idx;
+
+ for (idx = 0; idx < 6; idx++)
+ discriminator[idx] = table[(rnd >> (idx * 6)) & 63];
+ discriminator[idx] = '\0';
+
+ snprintf(buffer, length, "amqp:/%s%s/%s/temp.%s", topo_prefix, router->router_area, router->router_id, discriminator);
+}
+
+
+static int dx_router_find_mask_bit_LH(dx_router_t *router, dx_link_t *link)
+{
+ dx_router_conn_t *shared = (dx_router_conn_t*) dx_link_get_conn_context(link);
+ if (shared)
+ return shared->mask_bit;
+
+ int mask_bit;
+ if (dx_bitmask_first_set(router->neighbor_free_mask, &mask_bit)) {
+ dx_bitmask_clear_bit(router->neighbor_free_mask, mask_bit);
+ } else {
+ dx_log(module, LOG_CRITICAL, "Exceeded maximum inter-router link count");
+ return -1;
+ }
+
+ shared = new_dx_router_conn_t();
+ shared->mask_bit = mask_bit;
+ dx_link_set_conn_context(link, shared);
+ return mask_bit;
+}
/**
@@ -191,7 +302,7 @@ static int router_writable_link_handler(void* context, dx_link_t *link)
DEQ_REMOVE_HEAD(to_send);
//
- // Get a delivery for the send. This will be the current deliver on the link.
+ // Get a delivery for the send. This will be the current delivery on the link.
//
tag++;
delivery = dx_delivery(link, pn_dtag((char*) &tag, 8));
@@ -215,7 +326,7 @@ static int router_writable_link_handler(void* context, dx_link_t *link)
pn_link_advance(pn_link);
event_count++;
- dx_free_message(re->message);
+ dx_message_free(re->message);
free_dx_routed_event_t(re);
re = DEQ_HEAD(to_send);
}
@@ -250,17 +361,18 @@ static int router_writable_link_handler(void* context, dx_link_t *link)
}
-static void router_annotate_message(dx_router_t *router, dx_message_t *msg)
+static dx_field_iterator_t *router_annotate_message(dx_router_t *router, dx_message_t *msg, int *drop)
{
- dx_parsed_field_t *in_da = dx_message_delivery_annotations(msg);
- dx_composed_field_t *out_da = dx_compose(DX_PERFORMATIVE_DELIVERY_ANNOTATIONS, 0);
+ dx_parsed_field_t *in_da = dx_message_delivery_annotations(msg);
+ dx_composed_field_t *out_da = dx_compose(DX_PERFORMATIVE_DELIVERY_ANNOTATIONS, 0);
+ dx_field_iterator_t *ingress_iter = 0;
dx_parsed_field_t *trace = 0;
dx_parsed_field_t *ingress = 0;
if (in_da) {
- trace = dx_parse_value_by_key(in_da, "qdx.trace");
- ingress = dx_parse_value_by_key(in_da, "qdx.ingress");
+ trace = dx_parse_value_by_key(in_da, DX_DA_TRACE);
+ ingress = dx_parse_value_by_key(in_da, DX_DA_INGRESS);
}
dx_compose_start_map(out_da);
@@ -268,38 +380,48 @@ static void router_annotate_message(dx_router_t *router, dx_message_t *msg)
//
// If there is a trace field, append this router's ID to the trace.
//
- if (trace && dx_parse_is_list(trace)) {
- dx_compose_insert_string(out_da, "qdx.trace");
- dx_compose_start_list(out_da);
-
- uint32_t idx = 0;
- dx_parsed_field_t *trace_item = dx_parse_sub_value(trace, idx);
- while (trace_item) {
- dx_field_iterator_t *iter = dx_parse_raw(trace_item);
- dx_compose_insert_string_iterator(out_da, iter);
- idx++;
- trace_item = dx_parse_sub_value(trace, idx);
+ dx_compose_insert_string(out_da, DX_DA_TRACE);
+ dx_compose_start_list(out_da);
+ if (trace) {
+ if (dx_parse_is_list(trace)) {
+ uint32_t idx = 0;
+ dx_parsed_field_t *trace_item = dx_parse_sub_value(trace, idx);
+ while (trace_item) {
+ dx_field_iterator_t *iter = dx_parse_raw(trace_item);
+ if (dx_field_iterator_equal(iter, (unsigned char*) direct_prefix))
+ *drop = 1;
+ dx_field_iterator_reset(iter);
+ dx_compose_insert_string_iterator(out_da, iter);
+ idx++;
+ trace_item = dx_parse_sub_value(trace, idx);
+ }
}
-
- dx_compose_insert_string(out_da, router->router_id);
- dx_compose_end_list(out_da);
}
+ dx_compose_insert_string(out_da, direct_prefix);
+ dx_compose_end_list(out_da);
+
//
// If there is no ingress field, annotate the ingress as this router else
// keep the original field.
//
- dx_compose_insert_string(out_da, "qdx.ingress");
+ dx_compose_insert_string(out_da, DX_DA_INGRESS);
if (ingress && dx_parse_is_scalar(ingress)) {
- dx_field_iterator_t *iter = dx_parse_raw(ingress);
- dx_compose_insert_string_iterator(out_da, iter);
+ ingress_iter = dx_parse_raw(ingress);
+ dx_compose_insert_string_iterator(out_da, ingress_iter);
} else
- dx_compose_insert_string(out_da, router->router_id);
+ dx_compose_insert_string(out_da, direct_prefix);
dx_compose_end_map(out_da);
dx_message_set_delivery_annotations(msg, out_da);
dx_compose_free(out_da);
+
+ //
+ // Return the iterator to the ingress field _if_ it was present.
+ // If we added the ingress, return NULL.
+ //
+ return ingress_iter;
}
@@ -318,10 +440,7 @@ static void router_rx_handler(void* context, dx_link_t *link, dx_delivery_t *del
// Receive the message into a local representation. If the returned message
// pointer is NULL, we have not yet received a complete message.
//
- sys_mutex_lock(router->lock);
msg = dx_message_receive(delivery);
- sys_mutex_unlock(router->lock);
-
if (!msg)
return;
@@ -380,84 +499,155 @@ static void router_rx_handler(void* context, dx_link_t *link, dx_delivery_t *del
if (iter) {
dx_field_iterator_reset_view(iter, ITER_VIEW_ADDRESS_HASH);
- hash_retrieve(router->out_hash, iter, (void*) &addr);
+
+ //
+ // Note: This function is going to need to be refactored so we can put an
+ // asynchronous address lookup here. In the event there is a translation
+ // of the address (via namespace), it will have to be done here after
+ // obtaining the iterator and before doing the hash lookup.
+ //
+ // Note that this lookup is only done for global/mobile class addresses.
+ //
+
+ dx_hash_retrieve(router->addr_hash, iter, (void*) &addr);
dx_field_iterator_reset_view(iter, ITER_VIEW_NO_HOST);
- int is_local = dx_field_iterator_prefix(iter, local_prefix);
+ int is_local = dx_field_iterator_prefix(iter, local_prefix);
+ int is_direct = dx_field_iterator_prefix(iter, direct_prefix);
dx_field_iterator_free(iter);
if (addr) {
//
+ // If the incoming link is an endpoint link, count this as an ingress delivery.
+ //
+ if (rlink->link_type == DX_LINK_ENDPOINT)
+ addr->deliveries_ingress++;
+
+ //
// To field is valid and contains a known destination. Handle the various
// cases for forwarding.
//
//
- // Interpret and update the delivery annotations of the message
+ // Interpret and update the delivery annotations of the message. As a convenience,
+ // this function returns the iterator to the ingress field (if it exists).
//
- router_annotate_message(router, msg);
+ int drop = 0;
+ dx_field_iterator_t *ingress_iter = router_annotate_message(router, msg, &drop);
//
// Forward to the in-process handler for this message if there is one. The
// actual invocation of the handler will occur later after we've released
// the lock.
//
- if (addr->handler) {
+ if (!drop && addr->handler) {
in_process_copy = dx_message_copy(msg);
handler = addr->handler;
handler_context = addr->handler_context;
+ addr->deliveries_to_container++;
}
//
// If the address form is local (i.e. is prefixed by _local), don't forward
// outside of the router process.
//
- if (!is_local) {
+ if (!drop && !is_local) {
//
// Forward to all of the local links receiving this address.
//
- dx_router_link_t *dest_link = DEQ_HEAD(addr->rlinks);
- while (dest_link) {
+ dx_router_link_ref_t *dest_link_ref = DEQ_HEAD(addr->rlinks);
+ while (dest_link_ref) {
dx_routed_event_t *re = new_dx_routed_event_t();
DEQ_ITEM_INIT(re);
re->delivery = 0;
re->message = dx_message_copy(msg);
re->settle = 0;
re->disposition = 0;
- DEQ_INSERT_TAIL(dest_link->msg_fifo, re);
+ DEQ_INSERT_TAIL(dest_link_ref->link->msg_fifo, re);
fanout++;
if (fanout == 1 && !dx_delivery_settled(delivery))
re->delivery = delivery;
- dx_link_activate(dest_link->link);
- dest_link = DEQ_NEXT(dest_link);
+ addr->deliveries_egress++;
+ dx_link_activate(dest_link_ref->link->link);
+ dest_link_ref = DEQ_NEXT(dest_link_ref);
}
//
- // Forward to the next-hops for remote destinations.
+ // If the address form is direct to this router node, don't relay it on
+ // to any other part of the network.
//
- dx_router_node_t *dest_node = DEQ_HEAD(addr->rnodes);
- while (dest_node) {
- if (dest_node->next_hop)
- dest_link = dest_node->next_hop->peer_link;
- else
- dest_link = dest_node->peer_link;
- if (dest_link) {
- dx_routed_event_t *re = new_dx_routed_event_t();
- DEQ_ITEM_INIT(re);
- re->delivery = 0;
- re->message = dx_message_copy(msg);
- re->settle = 0;
- re->disposition = 0;
- DEQ_INSERT_TAIL(dest_link->msg_fifo, re);
-
- fanout++;
- if (fanout == 1)
- re->delivery = delivery;
-
- dx_link_activate(dest_link->link);
+ if (!is_direct) {
+ //
+ // Get the mask bit associated with the ingress router for the message.
+ // This will be compared against the "valid_origin" masks for each
+ // candidate destination router.
+ //
+ int origin = -1;
+ if (ingress_iter) {
+ dx_field_iterator_reset_view(ingress_iter, ITER_VIEW_ADDRESS_HASH);
+ dx_address_t *origin_addr;
+ dx_hash_retrieve(router->addr_hash, ingress_iter, (void*) &origin_addr);
+ if (origin_addr && DEQ_SIZE(origin_addr->rnodes) == 1) {
+ dx_router_ref_t *rref = DEQ_HEAD(origin_addr->rnodes);
+ origin = rref->router->mask_bit;
+ }
+ } else
+ origin = 0;
+
+ //
+ // Forward to the next-hops for remote destinations.
+ //
+ if (origin >= 0) {
+ dx_router_ref_t *dest_node_ref = DEQ_HEAD(addr->rnodes);
+ dx_router_link_t *dest_link;
+ dx_bitmask_t *link_set = dx_bitmask(0);
+
+ //
+ // Loop over the target nodes for this address. Build a set of outgoing links
+ // for which there are valid targets. We do this to avoid sending more than one
+ // message down a given link. It's possible that there are multiple destinations
+ // for this address that are all reachable over the same link. In this case, we
+ // will send only one copy of the message over the link and allow a downstream
+ // router to fan the message out.
+ //
+ while (dest_node_ref) {
+ if (dest_node_ref->router->next_hop)
+ dest_link = dest_node_ref->router->next_hop->peer_link;
+ else
+ dest_link = dest_node_ref->router->peer_link;
+ if (dest_link && dx_bitmask_value(dest_node_ref->router->valid_origins, origin))
+ dx_bitmask_set_bit(link_set, dest_link->mask_bit);
+ dest_node_ref = DEQ_NEXT(dest_node_ref);
+ }
+
+ //
+ // Send a copy of the message outbound on each identified link.
+ //
+ int link_bit;
+ while (dx_bitmask_first_set(link_set, &link_bit)) {
+ dx_bitmask_clear_bit(link_set, link_bit);
+ dest_link = router->out_links_by_mask_bit[link_bit];
+ if (dest_link) {
+ dx_routed_event_t *re = new_dx_routed_event_t();
+ DEQ_ITEM_INIT(re);
+ re->delivery = 0;
+ re->message = dx_message_copy(msg);
+ re->settle = 0;
+ re->disposition = 0;
+ DEQ_INSERT_TAIL(dest_link->msg_fifo, re);
+
+ fanout++;
+ if (fanout == 1 && !dx_delivery_settled(delivery))
+ re->delivery = delivery;
+
+ addr->deliveries_transit++;
+ dx_link_activate(dest_link->link);
+ }
+ }
+
+ dx_bitmask_free(link_set);
}
- dest_node = DEQ_NEXT(dest_node);
}
}
}
@@ -470,8 +660,7 @@ static void router_rx_handler(void* context, dx_link_t *link, dx_delivery_t *del
dx_delivery_free(delivery, PN_ACCEPTED);
} else if (fanout == 0) {
dx_delivery_free(delivery, PN_RELEASED);
- } else if (fanout > 1)
- dx_delivery_free(delivery, PN_ACCEPTED);
+ }
}
} else {
//
@@ -481,13 +670,15 @@ static void router_rx_handler(void* context, dx_link_t *link, dx_delivery_t *del
}
sys_mutex_unlock(router->lock);
- dx_free_message(msg);
+ dx_message_free(msg);
//
// Invoke the in-process handler now that the lock is released.
//
- if (handler)
- handler(handler_context, in_process_copy);
+ if (handler) {
+ handler(handler_context, in_process_copy, rlink->mask_bit);
+ dx_message_free(in_process_copy);
+ }
}
@@ -538,28 +729,37 @@ static void router_disp_handler(void* context, dx_link_t *link, dx_delivery_t *d
*/
static int router_incoming_link_handler(void* context, dx_link_t *link)
{
- dx_router_t *router = (dx_router_t*) context;
- dx_router_link_t *rlink = new_dx_router_link_t();
- pn_link_t *pn_link = dx_link_pn(link);
+ dx_router_t *router = (dx_router_t*) context;
+ pn_link_t *pn_link = dx_link_pn(link);
+ int is_router = dx_router_terminus_is_router(dx_link_remote_source(link));
+
+ if (is_router && !dx_router_connection_is_inter_router(dx_link_connection(link))) {
+ dx_log(module, LOG_WARNING, "Incoming link claims router capability but is not on an inter-router connection");
+ pn_link_close(pn_link);
+ return 0;
+ }
+ dx_router_link_t *rlink = new_dx_router_link_t();
DEQ_ITEM_INIT(rlink);
+ rlink->link_type = is_router ? DX_LINK_ROUTER : DX_LINK_ENDPOINT;
rlink->link_direction = DX_INCOMING;
- rlink->link_type = DX_LINK_ENDPOINT;
rlink->owning_addr = 0;
rlink->link = link;
rlink->connected_link = 0;
rlink->peer_link = 0;
+ rlink->ref = 0;
DEQ_INIT(rlink->event_fifo);
DEQ_INIT(rlink->msg_fifo);
dx_link_set_context(link, rlink);
sys_mutex_lock(router->lock);
- DEQ_INSERT_TAIL(router->in_links, rlink);
+ rlink->mask_bit = is_router ? dx_router_find_mask_bit_LH(router, link) : 0;
+ DEQ_INSERT_TAIL(router->links, rlink);
sys_mutex_unlock(router->lock);
- pn_terminus_copy(pn_link_source(pn_link), pn_link_remote_source(pn_link));
- pn_terminus_copy(pn_link_target(pn_link), pn_link_remote_target(pn_link));
+ pn_terminus_copy(dx_link_source(link), dx_link_remote_source(link));
+ pn_terminus_copy(dx_link_target(link), dx_link_remote_target(link));
pn_link_flow(pn_link, 1000);
pn_link_open(pn_link);
@@ -579,52 +779,125 @@ static int router_outgoing_link_handler(void* context, dx_link_t *link)
{
dx_router_t *router = (dx_router_t*) context;
pn_link_t *pn_link = dx_link_pn(link);
- const char *r_tgt = pn_terminus_get_address(pn_link_remote_target(pn_link));
+ const char *r_src = pn_terminus_get_address(dx_link_remote_source(link));
+ int is_dynamic = pn_terminus_is_dynamic(dx_link_remote_source(link));
+ int is_router = dx_router_terminus_is_router(dx_link_remote_target(link));
+ int propagate = 0;
+ dx_field_iterator_t *iter = 0;
+
+ if (is_router && !dx_router_connection_is_inter_router(dx_link_connection(link))) {
+ dx_log(module, LOG_WARNING, "Outgoing link claims router capability but is not on an inter-router connection");
+ pn_link_close(pn_link);
+ return 0;
+ }
- if (!r_tgt) {
+ //
+ // If this link is not a router link and it has no source address, we can't
+ // accept it.
+ //
+ if (r_src == 0 && !is_router && !is_dynamic) {
pn_link_close(pn_link);
return 0;
}
- dx_field_iterator_t *iter = dx_field_iterator_string(r_tgt, ITER_VIEW_NO_HOST);
- dx_router_link_t *rlink = new_dx_router_link_t();
- int is_router = dx_field_iterator_equal(iter, (unsigned char*) router_address);
+ //
+ // If this is an endpoint link with a source address, make sure the address is
+ // appropriate for endpoint links. If it is not mobile address, it cannot be
+ // bound to an endpoint link.
+ //
+ if(r_src && !is_router && !is_dynamic) {
+ iter = dx_field_iterator_string(r_src, ITER_VIEW_ADDRESS_HASH);
+ unsigned char prefix = dx_field_iterator_octet(iter);
+ dx_field_iterator_reset(iter);
+ if (prefix != 'M') {
+ dx_field_iterator_free(iter);
+ pn_link_close(pn_link);
+ dx_log(module, LOG_WARNING, "Rejected an outgoing endpoint link with a router address: %s", r_src);
+ return 0;
+ }
+ }
+
+ //
+ // Create a router_link record for this link. Some of the fields will be
+ // modified in the different cases below.
+ //
+ dx_router_link_t *rlink = new_dx_router_link_t();
DEQ_ITEM_INIT(rlink);
- rlink->link_direction = DX_OUTGOING;
rlink->link_type = is_router ? DX_LINK_ROUTER : DX_LINK_ENDPOINT;
+ rlink->link_direction = DX_OUTGOING;
+ rlink->owning_addr = 0;
rlink->link = link;
rlink->connected_link = 0;
rlink->peer_link = 0;
+ rlink->ref = 0;
DEQ_INIT(rlink->event_fifo);
DEQ_INIT(rlink->msg_fifo);
dx_link_set_context(link, rlink);
-
- dx_field_iterator_reset_view(iter, ITER_VIEW_ADDRESS_HASH);
- dx_address_t *addr;
+ pn_terminus_copy(dx_link_source(link), dx_link_remote_source(link));
+ pn_terminus_copy(dx_link_target(link), dx_link_remote_target(link));
sys_mutex_lock(router->lock);
- hash_retrieve(router->out_hash, iter, (void**) &addr);
- if (!addr) {
- addr = new_dx_address_t();
- addr->handler = 0;
- addr->handler_context = 0;
- DEQ_INIT(addr->rlinks);
- DEQ_INIT(addr->rnodes);
- hash_insert(router->out_hash, iter, addr);
+ rlink->mask_bit = is_router ? dx_router_find_mask_bit_LH(router, link) : 0;
+
+ if (is_router) {
+ //
+ // If this is a router link, put it in the hello_address link-list.
+ //
+ dx_router_add_link_ref_LH(&router->hello_addr->rlinks, rlink);
+ rlink->owning_addr = router->hello_addr;
+ router->out_links_by_mask_bit[rlink->mask_bit] = rlink;
+
+ } else {
+ //
+ // If this is an endpoint link, check the source. If it is dynamic, we will
+ // assign it an ephemeral and routable address. If it has a non-dymanic
+ // address, that address needs to be set up in the address list.
+ //
+ char temp_addr[1000]; // FIXME
+ dx_address_t *addr;
+
+ if (is_dynamic) {
+ dx_router_generate_temp_addr(router, temp_addr, 1000);
+ iter = dx_field_iterator_string(temp_addr, ITER_VIEW_ADDRESS_HASH);
+ pn_terminus_set_address(dx_link_source(link), temp_addr);
+ dx_log(module, LOG_INFO, "Assigned temporary routable address: %s", temp_addr);
+ } else
+ dx_log(module, LOG_INFO, "Registered local address: %s", r_src);
+
+ dx_hash_retrieve(router->addr_hash, iter, (void**) &addr);
+ if (!addr) {
+ addr = new_dx_address_t();
+ memset(addr, 0, sizeof(dx_address_t));
+ DEQ_ITEM_INIT(addr);
+ DEQ_INIT(addr->rlinks);
+ DEQ_INIT(addr->rnodes);
+ dx_hash_insert(router->addr_hash, iter, addr, &addr->hash_handle);
+ DEQ_INSERT_TAIL(router->addrs, addr);
+ }
+
+ rlink->owning_addr = addr;
+ dx_router_add_link_ref_LH(&addr->rlinks, rlink);
+
+ //
+ // If this is not a dynamic address and it is the first local subscription
+ // to the address, supply the address to the router module for propagation
+ // to other nodes.
+ //
+ propagate = (!is_dynamic) && (DEQ_SIZE(addr->rlinks) == 1);
}
- dx_field_iterator_free(iter);
- rlink->owning_addr = addr;
- DEQ_INSERT_TAIL(addr->rlinks, rlink);
+ DEQ_INSERT_TAIL(router->links, rlink);
+ sys_mutex_unlock(router->lock);
+
+ if (propagate)
+ dx_router_mobile_added(router, iter);
- pn_terminus_copy(pn_link_source(pn_link), pn_link_remote_source(pn_link));
- pn_terminus_copy(pn_link_target(pn_link), pn_link_remote_target(pn_link));
+ if (iter)
+ dx_field_iterator_free(iter);
pn_link_open(pn_link);
- sys_mutex_unlock(router->lock);
- dx_log(module, LOG_TRACE, "Registered new local address: %s", r_tgt);
return 0;
}
@@ -634,40 +907,60 @@ static int router_outgoing_link_handler(void* context, dx_link_t *link)
*/
static int router_link_detach_handler(void* context, dx_link_t *link, int closed)
{
- dx_router_t *router = (dx_router_t*) context;
- pn_link_t *pn_link = dx_link_pn(link);
- dx_router_link_t *rlink = (dx_router_link_t*) dx_link_get_context(link);
- const char *r_tgt = pn_terminus_get_address(pn_link_remote_target(pn_link));
+ dx_router_t *router = (dx_router_t*) context;
+ dx_router_link_t *rlink = (dx_router_link_t*) dx_link_get_context(link);
+ dx_router_conn_t *shared = (dx_router_conn_t*) dx_link_get_conn_context(link);
+ dx_address_t *oaddr = 0;
+
+ if (shared) {
+ dx_link_set_conn_context(link, 0);
+ free_dx_router_conn_t(shared);
+ }
if (!rlink)
return 0;
sys_mutex_lock(router->lock);
- if (pn_link_is_sender(pn_link)) {
- DEQ_REMOVE(rlink->owning_addr->rlinks, rlink);
-
- if ((rlink->owning_addr->handler == 0) &&
- (DEQ_SIZE(rlink->owning_addr->rlinks) == 0) &&
- (DEQ_SIZE(rlink->owning_addr->rnodes) == 0)) {
- dx_field_iterator_t *iter = dx_field_iterator_string(r_tgt, ITER_VIEW_ADDRESS_HASH);
- dx_address_t *addr;
- if (iter) {
- hash_retrieve(router->out_hash, iter, (void**) &addr);
- if (addr == rlink->owning_addr) {
- hash_remove(router->out_hash, iter);
- free_dx_router_link_t(rlink);
- free_dx_address_t(addr);
- dx_log(module, LOG_TRACE, "Removed local address: %s", r_tgt);
- }
- dx_field_iterator_free(iter);
- }
- }
- } else {
- DEQ_REMOVE(router->in_links, rlink);
- free_dx_router_link_t(rlink);
+
+ //
+ // If the link is outgoing, we must disassociate it from its address.
+ //
+ if (rlink->link_direction == DX_OUTGOING && rlink->owning_addr) {
+ dx_router_del_link_ref_LH(&rlink->owning_addr->rlinks, rlink);
+ oaddr = rlink->owning_addr;
}
+ //
+ // If this is an outgoing inter-router link, we must remove the by-mask-bit
+ // index reference to this link.
+ //
+ if (rlink->link_type == DX_LINK_ROUTER && rlink->link_direction == DX_OUTGOING) {
+ if (router->out_links_by_mask_bit[rlink->mask_bit] == rlink)
+ router->out_links_by_mask_bit[rlink->mask_bit] = 0;
+ else
+ dx_log(module, LOG_CRITICAL, "Outgoing router link closing but not in index: bit=%d", rlink->mask_bit);
+ }
+
+ //
+ // If this is an incoming inter-router link, we must free the mask_bit.
+ //
+ if (rlink->link_type == DX_LINK_ROUTER && rlink->link_direction == DX_INCOMING)
+ dx_bitmask_set_bit(router->neighbor_free_mask, rlink->mask_bit);
+
+ //
+ // Remove the link from the master list-of-links.
+ //
+ DEQ_REMOVE(router->links, rlink);
sys_mutex_unlock(router->lock);
+
+ //
+ // Check to see if the owning address should be deleted
+ //
+ dx_router_check_addr(router, oaddr, 1);
+
+ // TODO - wrap the free to handle the recursive items
+ free_dx_router_link_t(rlink);
+
return 0;
}
@@ -679,28 +972,47 @@ static void router_inbound_open_handler(void *type_context, dx_connection_t *con
static void router_outbound_open_handler(void *type_context, dx_connection_t *conn)
{
- // TODO - Make sure this connection is annotated as an inter-router transport.
- // Ignore otherwise
+ //
+ // Check the configured role of this connection. If it is not the inter-router
+ // role, ignore it.
+ //
+ if (!dx_router_connection_is_inter_router(conn)) {
+ dx_log(module, LOG_WARNING, "Outbound connection set up without inter-router role");
+ return;
+ }
dx_router_t *router = (dx_router_t*) type_context;
- dx_field_iterator_t *aiter = dx_field_iterator_string(router_address, ITER_VIEW_ADDRESS_HASH);
dx_link_t *sender;
dx_link_t *receiver;
dx_router_link_t *rlink;
+ int mask_bit = 0;
+ size_t clen = strlen(DX_CAPABILITY_ROUTER);
//
- // Create an incoming link and put it in the in-links collection. The address
- // of the remote source of this link is '_local/qdxrouter'.
+ // Allocate a mask bit to designate the pair of links connected to the neighbor router
//
- receiver = dx_link(router->node, conn, DX_INCOMING, "inter-router-rx");
- pn_terminus_set_address(dx_link_remote_source(receiver), router_address);
- pn_terminus_set_address(dx_link_target(receiver), router_address);
+ sys_mutex_lock(router->lock);
+ if (dx_bitmask_first_set(router->neighbor_free_mask, &mask_bit)) {
+ dx_bitmask_clear_bit(router->neighbor_free_mask, mask_bit);
+ } else {
+ sys_mutex_unlock(router->lock);
+ dx_log(module, LOG_CRITICAL, "Exceeded maximum inter-router link count");
+ return;
+ }
- rlink = new_dx_router_link_t();
+ //
+ // Create an incoming link with router source capability
+ //
+ receiver = dx_link(router->node, conn, DX_INCOMING, DX_INTERNODE_LINK_NAME_1);
+ // TODO - We don't want to have to cast away the constness of the literal string here!
+ // See PROTON-429
+ pn_data_put_symbol(pn_terminus_capabilities(dx_link_target(receiver)), pn_bytes(clen, (char*) DX_CAPABILITY_ROUTER));
+ rlink = new_dx_router_link_t();
DEQ_ITEM_INIT(rlink);
- rlink->link_direction = DX_INCOMING;
+ rlink->mask_bit = mask_bit;
rlink->link_type = DX_LINK_ROUTER;
+ rlink->link_direction = DX_INCOMING;
rlink->owning_addr = 0;
rlink->link = receiver;
rlink->connected_link = 0;
@@ -709,53 +1021,46 @@ static void router_outbound_open_handler(void *type_context, dx_connection_t *co
DEQ_INIT(rlink->msg_fifo);
dx_link_set_context(receiver, rlink);
-
- sys_mutex_lock(router->lock);
- DEQ_INSERT_TAIL(router->in_links, rlink);
- sys_mutex_unlock(router->lock);
+ DEQ_INSERT_TAIL(router->links, rlink);
//
- // Create an outgoing link with a local source of '_local/qdxrouter' and place
- // it in the routing table.
+ // Create an outgoing link with router target capability
//
- sender = dx_link(router->node, conn, DX_OUTGOING, "inter-router-tx");
- pn_terminus_set_address(dx_link_remote_target(sender), router_address);
- pn_terminus_set_address(dx_link_source(sender), router_address);
+ sender = dx_link(router->node, conn, DX_OUTGOING, DX_INTERNODE_LINK_NAME_2);
+ // TODO - We don't want to have to cast away the constness of the literal string here!
+ // See PROTON-429
+ pn_data_put_symbol(pn_terminus_capabilities(dx_link_source(sender)), pn_bytes(clen, (char *) DX_CAPABILITY_ROUTER));
rlink = new_dx_router_link_t();
-
DEQ_ITEM_INIT(rlink);
- rlink->link_direction = DX_OUTGOING;
+ rlink->mask_bit = mask_bit;
rlink->link_type = DX_LINK_ROUTER;
+ rlink->link_direction = DX_OUTGOING;
+ rlink->owning_addr = router->hello_addr;
rlink->link = sender;
rlink->connected_link = 0;
rlink->peer_link = 0;
DEQ_INIT(rlink->event_fifo);
DEQ_INIT(rlink->msg_fifo);
- dx_link_set_context(sender, rlink);
-
- dx_address_t *addr;
+ //
+ // Add the new outgoing link to the hello_address's list of links.
+ //
+ dx_router_add_link_ref_LH(&router->hello_addr->rlinks, rlink);
- sys_mutex_lock(router->lock);
- hash_retrieve(router->out_hash, aiter, (void**) &addr);
- if (!addr) {
- addr = new_dx_address_t();
- addr->handler = 0;
- addr->handler_context = 0;
- DEQ_INIT(addr->rlinks);
- DEQ_INIT(addr->rnodes);
- hash_insert(router->out_hash, aiter, addr);
- }
+ //
+ // Index this link from the by-maskbit index so we can later find it quickly
+ // when provided with the mask bit.
+ //
+ router->out_links_by_mask_bit[mask_bit] = rlink;
- rlink->owning_addr = addr;
- DEQ_INSERT_TAIL(addr->rlinks, rlink);
+ dx_link_set_context(sender, rlink);
+ DEQ_INSERT_TAIL(router->links, rlink);
sys_mutex_unlock(router->lock);
pn_link_open(dx_link_pn(receiver));
pn_link_open(dx_link_pn(sender));
pn_link_flow(dx_link_pn(receiver), 1000);
- dx_field_iterator_free(aiter);
}
@@ -767,7 +1072,6 @@ static void dx_router_timer_handler(void *context)
// Periodic processing.
//
dx_pyrouter_tick(router);
-
dx_timer_schedule(router->timer, 1000);
}
@@ -786,31 +1090,61 @@ static dx_node_type_t router_node = {"router", 0, 0,
static int type_registered = 0;
-dx_router_t *dx_router(dx_dispatch_t *dx, const char *area, const char *id)
+dx_router_t *dx_router(dx_dispatch_t *dx, dx_router_mode_t mode, const char *area, const char *id)
{
if (!type_registered) {
type_registered = 1;
dx_container_register_node_type(dx, &router_node);
}
+ size_t dplen = 9 + strlen(area) + strlen(id);
+ direct_prefix = (char*) malloc(dplen);
+ strcpy(direct_prefix, "_topo/");
+ strcat(direct_prefix, area);
+ strcat(direct_prefix, "/");
+ strcat(direct_prefix, id);
+ strcat(direct_prefix, "/");
+
dx_router_t *router = NEW(dx_router_t);
router_node.type_context = router;
+ dx->router = router;
router->dx = dx;
+ router->router_mode = mode;
router->router_area = area;
router->router_id = id;
router->node = dx_container_set_default_node_type(dx, &router_node, (void*) router, DX_DIST_BOTH);
- DEQ_INIT(router->in_links);
+ DEQ_INIT(router->addrs);
+ router->addr_hash = dx_hash(10, 32, 0);
+
+ DEQ_INIT(router->links);
DEQ_INIT(router->routers);
- DEQ_INIT(router->in_fifo);
- router->lock = sys_mutex();
- router->timer = dx_timer(dx, dx_router_timer_handler, (void*) router);
- router->out_hash = hash(10, 32, 0);
- router->dtag = 1;
- router->pyRouter = 0;
- router->pyTick = 0;
+ router->out_links_by_mask_bit = NEW_PTR_ARRAY(dx_router_link_t, dx_bitmask_width());
+ router->routers_by_mask_bit = NEW_PTR_ARRAY(dx_router_node_t, dx_bitmask_width());
+ for (int idx = 0; idx < dx_bitmask_width(); idx++) {
+ router->out_links_by_mask_bit[idx] = 0;
+ router->routers_by_mask_bit[idx] = 0;
+ }
+
+ router->neighbor_free_mask = dx_bitmask(1);
+ router->lock = sys_mutex();
+ router->timer = dx_timer(dx, dx_router_timer_handler, (void*) router);
+ router->dtag = 1;
+ router->pyRouter = 0;
+ router->pyTick = 0;
+ router->pyAdded = 0;
+ router->pyRemoved = 0;
+
+ //
+ // Create addresses for all of the routers in the topology. It will be registered
+ // locally later in the initialization sequence.
+ //
+ if (router->router_mode == DX_ROUTER_MODE_INTERIOR) {
+ router->router_addr = dx_router_register_address(dx, "qdxrouter", 0, 0);
+ router->hello_addr = dx_router_register_address(dx, "qdxhello", 0, 0);
+ }
//
// Inform the field iterator module of this router's id and area. The field iterator
@@ -823,18 +1157,21 @@ dx_router_t *dx_router(dx_dispatch_t *dx, const char *area, const char *id)
//
dx_python_start();
- dx_log(module, LOG_INFO, "Router started, area=%s id=%s", area, id);
+ switch (router->router_mode) {
+ case DX_ROUTER_MODE_STANDALONE: dx_log(module, LOG_INFO, "Router started in Standalone mode"); break;
+ case DX_ROUTER_MODE_INTERIOR: dx_log(module, LOG_INFO, "Router started in Interior mode, area=%s id=%s", area, id); break;
+ case DX_ROUTER_MODE_EDGE: dx_log(module, LOG_INFO, "Router started in Edge mode"); break;
+ }
return router;
}
-void dx_router_setup_agent(dx_dispatch_t *dx)
+void dx_router_setup_late(dx_dispatch_t *dx)
{
+ dx_router_agent_setup(dx->router);
dx_router_python_setup(dx->router);
dx_timer_schedule(dx->router->timer, 1000);
-
- // TODO
}
@@ -849,8 +1186,7 @@ void dx_router_free(dx_router_t *router)
const char *dx_router_id(const dx_dispatch_t *dx)
{
- dx_router_t *router = dx->router;
- return router->router_id;
+ return direct_prefix;
}
@@ -869,14 +1205,16 @@ dx_address_t *dx_router_register_address(dx_dispatch_t *dx,
iter = dx_field_iterator_string(addr_string, ITER_VIEW_NO_HOST);
sys_mutex_lock(router->lock);
- hash_retrieve(router->out_hash, iter, (void**) &addr);
+ dx_hash_retrieve(router->addr_hash, iter, (void**) &addr);
if (!addr) {
addr = new_dx_address_t();
- addr->handler = 0;
- addr->handler_context = 0;
+ memset(addr, 0, sizeof(dx_address_t));
+ DEQ_ITEM_INIT(addr);
DEQ_INIT(addr->rlinks);
DEQ_INIT(addr->rnodes);
- hash_insert(router->out_hash, iter, addr);
+ dx_hash_insert(router->addr_hash, iter, addr, &addr->hash_handle);
+ DEQ_ITEM_INIT(addr);
+ DEQ_INSERT_TAIL(router->addrs, addr);
}
dx_field_iterator_free(iter);
@@ -885,7 +1223,8 @@ dx_address_t *dx_router_register_address(dx_dispatch_t *dx,
sys_mutex_unlock(router->lock);
- dx_log(module, LOG_TRACE, "In-Process Address Registered: %s", address);
+ if (handler)
+ dx_log(module, LOG_INFO, "In-Process Address Registered: %s", address);
return addr;
}
@@ -905,34 +1244,49 @@ void dx_router_send(dx_dispatch_t *dx,
dx_field_iterator_reset_view(address, ITER_VIEW_ADDRESS_HASH);
sys_mutex_lock(router->lock);
- hash_retrieve(router->out_hash, address, (void*) &addr);
+ dx_hash_retrieve(router->addr_hash, address, (void*) &addr);
if (addr) {
//
// Forward to all of the local links receiving this address.
//
- dx_router_link_t *dest_link = DEQ_HEAD(addr->rlinks);
- while (dest_link) {
+ addr->deliveries_from_container++;
+ dx_router_link_ref_t *dest_link_ref = DEQ_HEAD(addr->rlinks);
+ while (dest_link_ref) {
dx_routed_event_t *re = new_dx_routed_event_t();
DEQ_ITEM_INIT(re);
re->delivery = 0;
re->message = dx_message_copy(msg);
re->settle = 0;
re->disposition = 0;
- DEQ_INSERT_TAIL(dest_link->msg_fifo, re);
+ DEQ_INSERT_TAIL(dest_link_ref->link->msg_fifo, re);
- dx_link_activate(dest_link->link);
- dest_link = DEQ_NEXT(dest_link);
+ dx_link_activate(dest_link_ref->link->link);
+ addr->deliveries_egress++;
+
+ dest_link_ref = DEQ_NEXT(dest_link_ref);
}
//
// Forward to the next-hops for remote destinations.
//
- dx_router_node_t *dest_node = DEQ_HEAD(addr->rnodes);
- while (dest_node) {
- if (dest_node->next_hop)
- dest_link = dest_node->next_hop->peer_link;
+ dx_router_ref_t *dest_node_ref = DEQ_HEAD(addr->rnodes);
+ dx_router_link_t *dest_link;
+ dx_bitmask_t *link_set = dx_bitmask(0);
+
+ while (dest_node_ref) {
+ if (dest_node_ref->router->next_hop)
+ dest_link = dest_node_ref->router->next_hop->peer_link;
else
- dest_link = dest_node->peer_link;
+ dest_link = dest_node_ref->router->peer_link;
+ if (dest_link)
+ dx_bitmask_set_bit(link_set, dest_link->mask_bit);
+ dest_node_ref = DEQ_NEXT(dest_node_ref);
+ }
+
+ int link_bit;
+ while (dx_bitmask_first_set(link_set, &link_bit)) {
+ dx_bitmask_clear_bit(link_set, link_bit);
+ dest_link = router->out_links_by_mask_bit[link_bit];
if (dest_link) {
dx_routed_event_t *re = new_dx_routed_event_t();
DEQ_ITEM_INIT(re);
@@ -942,9 +1296,11 @@ void dx_router_send(dx_dispatch_t *dx,
re->disposition = 0;
DEQ_INSERT_TAIL(dest_link->msg_fifo, re);
dx_link_activate(dest_link->link);
+ addr->deliveries_transit++;
}
- dest_node = DEQ_NEXT(dest_node);
}
+
+ dx_bitmask_free(link_set);
}
sys_mutex_unlock(router->lock); // TOINVESTIGATE Move this higher?
}
@@ -959,222 +1315,3 @@ void dx_router_send2(dx_dispatch_t *dx,
dx_field_iterator_free(iter);
}
-
-//===============================================================================
-// Python Router Adapter
-//===============================================================================
-
-typedef struct {
- PyObject_HEAD
- dx_router_t *router;
-} RouterAdapter;
-
-
-static PyObject* dx_router_node_updated(PyObject *self, PyObject *args)
-{
- //RouterAdapter *adapter = (RouterAdapter*) self;
- //dx_router_t *router = adapter->router;
- const char *address;
- int is_reachable;
- int is_neighbor;
-
- if (!PyArg_ParseTuple(args, "sii", &address, &is_reachable, &is_neighbor))
- return 0;
-
- // TODO
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-
-static PyObject* dx_router_add_route(PyObject *self, PyObject *args)
-{
- //RouterAdapter *adapter = (RouterAdapter*) self;
- const char *addr;
- const char *peer;
-
- if (!PyArg_ParseTuple(args, "ss", &addr, &peer))
- return 0;
-
- // TODO
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-
-static PyObject* dx_router_del_route(PyObject *self, PyObject *args)
-{
- //RouterAdapter *adapter = (RouterAdapter*) self;
- const char *addr;
- const char *peer;
-
- if (!PyArg_ParseTuple(args, "ss", &addr, &peer))
- return 0;
-
- // TODO
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-
-static PyMethodDef RouterAdapter_methods[] = {
- {"node_updated", dx_router_node_updated, METH_VARARGS, "Update the status of a remote router node"},
- {"add_route", dx_router_add_route, METH_VARARGS, "Add a newly discovered route"},
- {"del_route", dx_router_del_route, METH_VARARGS, "Delete a route"},
- {0, 0, 0, 0}
-};
-
-static PyTypeObject RouterAdapterType = {
- PyObject_HEAD_INIT(0)
- 0, /* ob_size*/
- "dispatch.RouterAdapter", /* tp_name*/
- sizeof(RouterAdapter), /* tp_basicsize*/
- 0, /* tp_itemsize*/
- 0, /* tp_dealloc*/
- 0, /* tp_print*/
- 0, /* tp_getattr*/
- 0, /* tp_setattr*/
- 0, /* tp_compare*/
- 0, /* tp_repr*/
- 0, /* tp_as_number*/
- 0, /* tp_as_sequence*/
- 0, /* tp_as_mapping*/
- 0, /* tp_hash */
- 0, /* tp_call*/
- 0, /* tp_str*/
- 0, /* tp_getattro*/
- 0, /* tp_setattro*/
- 0, /* tp_as_buffer*/
- Py_TPFLAGS_DEFAULT, /* tp_flags*/
- "Dispatch Router Adapter", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- RouterAdapter_methods, /* tp_methods */
- 0, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
- 0, /* tp_free */
- 0, /* tp_is_gc */
- 0, /* tp_bases */
- 0, /* tp_mro */
- 0, /* tp_cache */
- 0, /* tp_subclasses */
- 0, /* tp_weaklist */
- 0, /* tp_del */
- 0 /* tp_version_tag */
-};
-
-
-static void dx_router_python_setup(dx_router_t *router)
-{
- PyObject *pDispatchModule = dx_python_module();
-
- RouterAdapterType.tp_new = PyType_GenericNew;
- if (PyType_Ready(&RouterAdapterType) < 0) {
- PyErr_Print();
- dx_log(module, LOG_CRITICAL, "Unable to initialize the Python Router Adapter");
- return;
- }
-
- Py_INCREF(&RouterAdapterType);
- PyModule_AddObject(pDispatchModule, "RouterAdapter", (PyObject*) &RouterAdapterType);
-
- //
- // Attempt to import the Python Router module
- //
- PyObject* pName;
- PyObject* pId;
- PyObject* pArea;
- PyObject* pModule;
- PyObject* pClass;
- PyObject* pArgs;
-
- pName = PyString_FromString("qpid.dispatch.router");
- pModule = PyImport_Import(pName);
- Py_DECREF(pName);
- if (!pModule) {
- dx_log(module, LOG_CRITICAL, "Can't Locate 'router' Python module");
- return;
- }
-
- pClass = PyObject_GetAttrString(pModule, "RouterEngine");
- if (!pClass || !PyClass_Check(pClass)) {
- dx_log(module, LOG_CRITICAL, "Can't Locate 'RouterEngine' class in the 'router' module");
- return;
- }
-
- PyObject *adapterType = PyObject_GetAttrString(pDispatchModule, "RouterAdapter");
- PyObject *adapterInstance = PyObject_CallObject(adapterType, 0);
- assert(adapterInstance);
-
- ((RouterAdapter*) adapterInstance)->router = router;
-
- //
- // Constructor Arguments for RouterEngine
- //
- pArgs = PyTuple_New(3);
-
- // arg 0: adapter instance
- PyTuple_SetItem(pArgs, 0, adapterInstance);
-
- // arg 1: router_id
- pId = PyString_FromString(router->router_id);
- PyTuple_SetItem(pArgs, 1, pId);
-
- // arg 2: area id
- pArea = PyString_FromString(router->router_area);
- PyTuple_SetItem(pArgs, 2, pArea);
-
- //
- // Instantiate the router
- //
- router->pyRouter = PyInstance_New(pClass, pArgs, 0);
- Py_DECREF(pArgs);
- Py_DECREF(adapterType);
-
- if (!router->pyRouter) {
- PyErr_Print();
- dx_log(module, LOG_CRITICAL, "'RouterEngine' class cannot be instantiated");
- return;
- }
-
- router->pyTick = PyObject_GetAttrString(router->pyRouter, "handleTimerTick");
- if (!router->pyTick || !PyCallable_Check(router->pyTick)) {
- dx_log(module, LOG_CRITICAL, "'RouterEngine' class has no handleTimerTick method");
- return;
- }
-}
-
-
-static void dx_pyrouter_tick(dx_router_t *router)
-{
- PyObject *pArgs;
- PyObject *pValue;
-
- if (router->pyTick) {
- pArgs = PyTuple_New(0);
- pValue = PyObject_CallObject(router->pyTick, pArgs);
- if (PyErr_Occurred()) {
- PyErr_Print();
- }
- Py_DECREF(pArgs);
- if (pValue) {
- Py_DECREF(pValue);
- }
- }
-}
-
diff --git a/qpid/extras/dispatch/src/router_private.h b/qpid/extras/dispatch/src/router_private.h
new file mode 100644
index 0000000000..066188b3f7
--- /dev/null
+++ b/qpid/extras/dispatch/src/router_private.h
@@ -0,0 +1,177 @@
+#ifndef __router_private_h__
+#define __router_private_h__ 1
+/*
+ * 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.
+ */
+
+typedef struct dx_router_link_t dx_router_link_t;
+typedef struct dx_router_node_t dx_router_node_t;
+typedef struct dx_router_ref_t dx_router_ref_t;
+typedef struct dx_router_link_ref_t dx_router_link_ref_t;
+typedef struct dx_router_conn_t dx_router_conn_t;
+
+void dx_router_python_setup(dx_router_t *router);
+void dx_pyrouter_tick(dx_router_t *router);
+void dx_router_agent_setup(dx_router_t *router);
+
+typedef enum {
+ DX_ROUTER_MODE_STANDALONE, // Standalone router. No routing protocol participation
+ DX_ROUTER_MODE_INTERIOR, // Interior router. Full participation in routing protocol.
+ DX_ROUTER_MODE_EDGE // Edge router. No routing protocol participation, access via other protocols.
+} dx_router_mode_t;
+
+typedef enum {
+ DX_LINK_ENDPOINT, // A link to a connected endpoint
+ DX_LINK_ROUTER, // A link to a peer router in the same area
+ DX_LINK_AREA // A link to a peer router in a different area (area boundary)
+} dx_link_type_t;
+
+
+typedef struct dx_routed_event_t {
+ DEQ_LINKS(struct dx_routed_event_t);
+ dx_delivery_t *delivery;
+ dx_message_t *message;
+ bool settle;
+ uint64_t disposition;
+} dx_routed_event_t;
+
+ALLOC_DECLARE(dx_routed_event_t);
+DEQ_DECLARE(dx_routed_event_t, dx_routed_event_list_t);
+
+
+struct dx_router_link_t {
+ DEQ_LINKS(dx_router_link_t);
+ int mask_bit; // Unique mask bit if this is an inter-router link
+ dx_link_type_t link_type;
+ dx_direction_t link_direction;
+ dx_address_t *owning_addr; // [ref] Address record that owns this link
+ dx_link_t *link; // [own] Link pointer
+ dx_router_link_t *connected_link; // [ref] If this is a link-route, reference the connected link
+ dx_router_link_t *peer_link; // [ref] If this is a bidirectional link-route, reference the peer link
+ dx_router_link_ref_t *ref; // Pointer to a containing reference object
+ dx_routed_event_list_t event_fifo; // FIFO of outgoing delivery/link events (no messages)
+ dx_routed_event_list_t msg_fifo; // FIFO of outgoing message deliveries
+};
+
+ALLOC_DECLARE(dx_router_link_t);
+DEQ_DECLARE(dx_router_link_t, dx_router_link_list_t);
+
+struct dx_router_node_t {
+ DEQ_LINKS(dx_router_node_t);
+ dx_address_t *owning_addr;
+ int mask_bit;
+ dx_router_node_t *next_hop; // Next hop node _if_ this is not a neighbor node
+ dx_router_link_t *peer_link; // Outgoing link _if_ this is a neighbor node
+ uint32_t ref_count;
+ dx_bitmask_t *valid_origins;
+};
+
+ALLOC_DECLARE(dx_router_node_t);
+DEQ_DECLARE(dx_router_node_t, dx_router_node_list_t);
+
+struct dx_router_ref_t {
+ DEQ_LINKS(dx_router_ref_t);
+ dx_router_node_t *router;
+};
+
+ALLOC_DECLARE(dx_router_ref_t);
+DEQ_DECLARE(dx_router_ref_t, dx_router_ref_list_t);
+
+
+struct dx_router_link_ref_t {
+ DEQ_LINKS(dx_router_link_ref_t);
+ dx_router_link_t *link;
+};
+
+ALLOC_DECLARE(dx_router_link_ref_t);
+DEQ_DECLARE(dx_router_link_ref_t, dx_router_link_ref_list_t);
+
+
+struct dx_router_conn_t {
+ int mask_bit;
+};
+
+ALLOC_DECLARE(dx_router_conn_t);
+
+
+struct dx_address_t {
+ DEQ_LINKS(dx_address_t);
+ dx_router_message_cb handler; // In-Process Consumer
+ void *handler_context; // In-Process Consumer context
+ dx_router_link_ref_list_t rlinks; // Locally-Connected Consumers
+ dx_router_ref_list_t rnodes; // Remotely-Connected Consumers
+ dx_hash_handle_t *hash_handle; // Linkage back to the hash table entry
+
+ uint64_t deliveries_ingress;
+ uint64_t deliveries_egress;
+ uint64_t deliveries_transit;
+ uint64_t deliveries_to_container;
+ uint64_t deliveries_from_container;
+};
+
+ALLOC_DECLARE(dx_address_t);
+DEQ_DECLARE(dx_address_t, dx_address_list_t);
+
+
+struct dx_router_t {
+ dx_dispatch_t *dx;
+ dx_router_mode_t router_mode;
+ const char *router_area;
+ const char *router_id;
+ dx_node_t *node;
+
+ dx_address_list_t addrs;
+ dx_hash_t *addr_hash;
+ dx_address_t *router_addr;
+ dx_address_t *hello_addr;
+
+ dx_router_link_list_t links;
+ dx_router_node_list_t routers;
+ dx_router_link_t **out_links_by_mask_bit;
+ dx_router_node_t **routers_by_mask_bit;
+
+ dx_bitmask_t *neighbor_free_mask;
+ sys_mutex_t *lock;
+ dx_timer_t *timer;
+ uint64_t dtag;
+
+ PyObject *pyRouter;
+ PyObject *pyTick;
+ PyObject *pyAdded;
+ PyObject *pyRemoved;
+
+ dx_agent_class_t *class_router;
+ dx_agent_class_t *class_link;
+ dx_agent_class_t *class_node;
+ dx_agent_class_t *class_address;
+};
+
+
+
+void dx_router_check_addr(dx_router_t *router, dx_address_t *addr, int was_local);
+void dx_router_add_link_ref_LH(dx_router_link_ref_list_t *ref_list, dx_router_link_t *link);
+void dx_router_del_link_ref_LH(dx_router_link_ref_list_t *ref_list, dx_router_link_t *link);
+
+void dx_router_add_node_ref_LH(dx_router_ref_list_t *ref_list, dx_router_node_t *rnode);
+void dx_router_del_node_ref_LH(dx_router_ref_list_t *ref_list, dx_router_node_t *rnode);
+
+void dx_router_mobile_added(dx_router_t *router, dx_field_iterator_t *iter);
+void dx_router_mobile_removed(dx_router_t *router, const char *addr);
+
+
+#endif
diff --git a/qpid/extras/dispatch/src/router_pynode.c b/qpid/extras/dispatch/src/router_pynode.c
new file mode 100644
index 0000000000..82b08c0785
--- /dev/null
+++ b/qpid/extras/dispatch/src/router_pynode.c
@@ -0,0 +1,682 @@
+/*
+ * 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.
+ */
+
+#include <qpid/dispatch/python_embedded.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <qpid/dispatch.h>
+#include "dispatch_private.h"
+#include "router_private.h"
+
+static char *module = "router.pynode";
+
+typedef struct {
+ PyObject_HEAD
+ dx_router_t *router;
+} RouterAdapter;
+
+
+static char *dx_add_router(dx_router_t *router, const char *address, int router_maskbit, int link_maskbit)
+{
+ if (router_maskbit >= dx_bitmask_width() || router_maskbit < 0)
+ return "Router bit mask out of range";
+
+ if (link_maskbit >= dx_bitmask_width() || link_maskbit < -1)
+ return "Link bit mask out of range";
+
+ sys_mutex_lock(router->lock);
+ if (router->routers_by_mask_bit[router_maskbit] != 0) {
+ sys_mutex_unlock(router->lock);
+ return "Adding router over already existing router";
+ }
+
+ if (link_maskbit >= 0 && router->out_links_by_mask_bit[link_maskbit] == 0) {
+ sys_mutex_unlock(router->lock);
+ return "Adding neighbor router with invalid link reference";
+ }
+
+ //
+ // Hash lookup the address to ensure there isn't an existing router address.
+ //
+ dx_field_iterator_t *iter = dx_field_iterator_string(address, ITER_VIEW_ADDRESS_HASH);
+ dx_address_t *addr;
+
+ dx_hash_retrieve(router->addr_hash, iter, (void**) &addr);
+ assert(addr == 0);
+
+ //
+ // Create an address record for this router and insert it in the hash table.
+ // This record will be found whenever a "foreign" topological address to this
+ // remote router is looked up.
+ //
+ addr = new_dx_address_t();
+ memset(addr, 0, sizeof(dx_address_t));
+ DEQ_ITEM_INIT(addr);
+ DEQ_INIT(addr->rlinks);
+ DEQ_INIT(addr->rnodes);
+ dx_hash_insert(router->addr_hash, iter, addr, &addr->hash_handle);
+ DEQ_INSERT_TAIL(router->addrs, addr);
+ dx_field_iterator_free(iter);
+
+ //
+ // Create a router-node record to represent the remote router.
+ //
+ dx_router_node_t *rnode = new_dx_router_node_t();
+ DEQ_ITEM_INIT(rnode);
+ rnode->owning_addr = addr;
+ rnode->mask_bit = router_maskbit;
+ rnode->next_hop = 0;
+ rnode->peer_link = 0;
+ rnode->ref_count = 0;
+ rnode->valid_origins = dx_bitmask(0);
+
+ DEQ_INSERT_TAIL(router->routers, rnode);
+
+ //
+ // Link the router record to the address record.
+ //
+ dx_router_add_node_ref_LH(&addr->rnodes, rnode);
+
+ //
+ // Link the router record to the router address record.
+ //
+ dx_router_add_node_ref_LH(&router->router_addr->rnodes, rnode);
+
+ //
+ // Add the router record to the mask-bit index.
+ //
+ router->routers_by_mask_bit[router_maskbit] = rnode;
+
+ //
+ // If this is a neighbor router, add the peer_link reference to the
+ // router record.
+ //
+ if (link_maskbit >= 0)
+ rnode->peer_link = router->out_links_by_mask_bit[link_maskbit];
+
+ sys_mutex_unlock(router->lock);
+ return 0;
+}
+
+
+static char *dx_del_router(dx_router_t *router, int router_maskbit)
+{
+ if (router_maskbit >= dx_bitmask_width() || router_maskbit < 0)
+ return "Router bit mask out of range";
+
+ sys_mutex_lock(router->lock);
+ if (router->routers_by_mask_bit[router_maskbit] == 0) {
+ sys_mutex_unlock(router->lock);
+ return "Deleting nonexistent router";
+ }
+
+ dx_router_node_t *rnode = router->routers_by_mask_bit[router_maskbit];
+ dx_address_t *oaddr = rnode->owning_addr;
+ assert(oaddr);
+
+ //
+ // Unlink the router node from the address record
+ //
+ dx_router_del_node_ref_LH(&oaddr->rnodes, rnode);
+
+ //
+ // While the router node has a non-zero reference count, look for addresses
+ // to unlink the node from.
+ //
+ dx_address_t *addr = DEQ_HEAD(router->addrs);
+ while (addr && rnode->ref_count > 0) {
+ dx_router_del_node_ref_LH(&addr->rnodes, rnode);
+ addr = DEQ_NEXT(addr);
+ }
+ assert(rnode->ref_count == 0);
+
+ //
+ // Free the router node and the owning address records.
+ //
+ dx_bitmask_free(rnode->valid_origins);
+ DEQ_REMOVE(router->routers, rnode);
+ free_dx_router_node_t(rnode);
+
+ dx_hash_remove_by_handle(router->addr_hash, oaddr->hash_handle);
+ DEQ_REMOVE(router->addrs, oaddr);
+ dx_hash_handle_free(oaddr->hash_handle);
+ router->routers_by_mask_bit[router_maskbit] = 0;
+ free_dx_address_t(oaddr);
+
+ sys_mutex_unlock(router->lock);
+ return 0;
+}
+
+
+static PyObject* dx_add_remote_router(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ const char *address;
+ int router_maskbit;
+
+ if (!PyArg_ParseTuple(args, "si", &address, &router_maskbit))
+ return 0;
+
+ char *error = dx_add_router(router, address, router_maskbit, -1);
+ if (error) {
+ PyErr_SetString(PyExc_Exception, error);
+ return 0;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_del_remote_router(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ int router_maskbit;
+
+ if (!PyArg_ParseTuple(args, "i", &router_maskbit))
+ return 0;
+
+ char *error = dx_del_router(router, router_maskbit);
+ if (error) {
+ PyErr_SetString(PyExc_Exception, error);
+ return 0;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_set_next_hop(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ int router_maskbit;
+ int next_hop_maskbit;
+
+ if (!PyArg_ParseTuple(args, "ii", &router_maskbit, &next_hop_maskbit))
+ return 0;
+
+ if (router_maskbit >= dx_bitmask_width() || router_maskbit < 0) {
+ PyErr_SetString(PyExc_Exception, "Router bit mask out of range");
+ return 0;
+ }
+
+ if (next_hop_maskbit >= dx_bitmask_width() || next_hop_maskbit < 0) {
+ PyErr_SetString(PyExc_Exception, "Next Hop bit mask out of range");
+ return 0;
+ }
+
+ if (router->routers_by_mask_bit[router_maskbit] == 0) {
+ PyErr_SetString(PyExc_Exception, "Router Not Found");
+ return 0;
+ }
+
+ if (router->routers_by_mask_bit[next_hop_maskbit] == 0) {
+ PyErr_SetString(PyExc_Exception, "Next Hop Not Found");
+ return 0;
+ }
+
+ if (router_maskbit != next_hop_maskbit) {
+ dx_router_node_t *rnode = router->routers_by_mask_bit[router_maskbit];
+ rnode->next_hop = router->routers_by_mask_bit[next_hop_maskbit];
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_set_valid_origins(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ int router_maskbit;
+ PyObject *origin_list;
+ Py_ssize_t idx;
+
+ if (!PyArg_ParseTuple(args, "iO", &router_maskbit, &origin_list))
+ return 0;
+
+ if (router_maskbit >= dx_bitmask_width() || router_maskbit < 0) {
+ PyErr_SetString(PyExc_Exception, "Router bit mask out of range");
+ return 0;
+ }
+
+ if (router->routers_by_mask_bit[router_maskbit] == 0) {
+ PyErr_SetString(PyExc_Exception, "Router Not Found");
+ return 0;
+ }
+
+ if (!PyList_Check(origin_list)) {
+ PyErr_SetString(PyExc_Exception, "Expected List as argument 2");
+ return 0;
+ }
+
+ Py_ssize_t origin_count = PyList_Size(origin_list);
+ dx_router_node_t *rnode = router->routers_by_mask_bit[router_maskbit];
+ int maskbit;
+
+ for (idx = 0; idx < origin_count; idx++) {
+ maskbit = PyInt_AS_LONG(PyList_GetItem(origin_list, idx));
+
+ if (maskbit >= dx_bitmask_width() || maskbit < 0) {
+ PyErr_SetString(PyExc_Exception, "Origin bit mask out of range");
+ return 0;
+ }
+
+ if (router->routers_by_mask_bit[maskbit] == 0) {
+ PyErr_SetString(PyExc_Exception, "Origin router Not Found");
+ return 0;
+ }
+ }
+
+ dx_bitmask_clear_all(rnode->valid_origins);
+ dx_bitmask_set_bit(rnode->valid_origins, 0); // This router is a valid origin for all destinations
+ for (idx = 0; idx < origin_count; idx++) {
+ maskbit = PyInt_AS_LONG(PyList_GetItem(origin_list, idx));
+ dx_bitmask_set_bit(rnode->valid_origins, maskbit);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_add_neighbor_router(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ const char *address;
+ int router_maskbit;
+ int link_maskbit;
+
+ if (!PyArg_ParseTuple(args, "sii", &address, &router_maskbit, &link_maskbit))
+ return 0;
+
+ char *error = dx_add_router(router, address, router_maskbit, link_maskbit);
+ if (error) {
+ PyErr_SetString(PyExc_Exception, error);
+ return 0;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_del_neighbor_router(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ int router_maskbit;
+
+ if (!PyArg_ParseTuple(args, "i", &router_maskbit))
+ return 0;
+
+ char *error = dx_del_router(router, router_maskbit);
+ if (error) {
+ PyErr_SetString(PyExc_Exception, error);
+ return 0;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_map_destination(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ const char *addr_string;
+ int maskbit;
+ dx_address_t *addr;
+ dx_field_iterator_t *iter;
+
+ if (!PyArg_ParseTuple(args, "si", &addr_string, &maskbit))
+ return 0;
+
+ if (maskbit >= dx_bitmask_width() || maskbit < 0) {
+ PyErr_SetString(PyExc_Exception, "Router bit mask out of range");
+ return 0;
+ }
+
+ if (router->routers_by_mask_bit[maskbit] == 0) {
+ PyErr_SetString(PyExc_Exception, "Router Not Found");
+ return 0;
+ }
+
+ iter = dx_field_iterator_string(addr_string, ITER_VIEW_ADDRESS_HASH);
+
+ sys_mutex_lock(router->lock);
+ dx_hash_retrieve(router->addr_hash, iter, (void**) &addr);
+ if (!addr) {
+ addr = new_dx_address_t();
+ memset(addr, 0, sizeof(dx_address_t));
+ DEQ_ITEM_INIT(addr);
+ DEQ_INIT(addr->rlinks);
+ DEQ_INIT(addr->rnodes);
+ dx_hash_insert(router->addr_hash, iter, addr, &addr->hash_handle);
+ DEQ_ITEM_INIT(addr);
+ DEQ_INSERT_TAIL(router->addrs, addr);
+ }
+ dx_field_iterator_free(iter);
+
+ dx_router_node_t *rnode = router->routers_by_mask_bit[maskbit];
+ dx_router_add_node_ref_LH(&addr->rnodes, rnode);
+
+ sys_mutex_unlock(router->lock);
+
+ dx_log(module, LOG_DEBUG, "Remote Destination '%s' Mapped to router %d", addr_string, maskbit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject* dx_unmap_destination(PyObject *self, PyObject *args)
+{
+ RouterAdapter *adapter = (RouterAdapter*) self;
+ dx_router_t *router = adapter->router;
+ const char *addr_string;
+ int maskbit;
+ dx_address_t *addr;
+
+ if (!PyArg_ParseTuple(args, "si", &addr_string, &maskbit))
+ return 0;
+
+ if (maskbit >= dx_bitmask_width() || maskbit < 0) {
+ PyErr_SetString(PyExc_Exception, "Router bit mask out of range");
+ return 0;
+ }
+
+ if (router->routers_by_mask_bit[maskbit] == 0) {
+ PyErr_SetString(PyExc_Exception, "Router Not Found");
+ return 0;
+ }
+
+ dx_router_node_t *rnode = router->routers_by_mask_bit[maskbit];
+ dx_field_iterator_t *iter = dx_field_iterator_string(addr_string, ITER_VIEW_ADDRESS_HASH);
+
+ sys_mutex_lock(router->lock);
+ dx_hash_retrieve(router->addr_hash, iter, (void**) &addr);
+ dx_field_iterator_free(iter);
+
+ if (!addr) {
+ PyErr_SetString(PyExc_Exception, "Address Not Found");
+ sys_mutex_unlock(router->lock);
+ return 0;
+ }
+
+ dx_router_del_node_ref_LH(&addr->rnodes, rnode);
+ sys_mutex_unlock(router->lock);
+
+ dx_router_check_addr(router, addr, 0);
+
+ dx_log(module, LOG_DEBUG, "Remote Destination '%s' Unmapped from router %d", addr_string, maskbit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyMethodDef RouterAdapter_methods[] = {
+ {"add_remote_router", dx_add_remote_router, METH_VARARGS, "A new remote/reachable router has been discovered"},
+ {"del_remote_router", dx_del_remote_router, METH_VARARGS, "We've lost reachability to a remote router"},
+ {"set_next_hop", dx_set_next_hop, METH_VARARGS, "Set the next hop for a remote router"},
+ {"set_valid_origins", dx_set_valid_origins, METH_VARARGS, "Set the valid origins for a remote router"},
+ {"add_neighbor_router", dx_add_neighbor_router, METH_VARARGS, "A new neighbor router has been discovered"},
+ {"del_neighbor_router", dx_del_neighbor_router, METH_VARARGS, "We've lost reachability to a neighbor router"},
+ {"map_destination", dx_map_destination, METH_VARARGS, "Add a newly discovered destination mapping"},
+ {"unmap_destination", dx_unmap_destination, METH_VARARGS, "Delete a destination mapping"},
+ {0, 0, 0, 0}
+};
+
+static PyTypeObject RouterAdapterType = {
+ PyObject_HEAD_INIT(0)
+ 0, /* ob_size*/
+ "dispatch.RouterAdapter", /* tp_name*/
+ sizeof(RouterAdapter), /* tp_basicsize*/
+ 0, /* tp_itemsize*/
+ 0, /* tp_dealloc*/
+ 0, /* tp_print*/
+ 0, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ 0, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call*/
+ 0, /* tp_str*/
+ 0, /* tp_getattro*/
+ 0, /* tp_setattro*/
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags*/
+ "Dispatch Router Adapter", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ RouterAdapter_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0 /* tp_version_tag */
+};
+
+
+void dx_router_python_setup(dx_router_t *router)
+{
+ //
+ // If we are not operating as an interior router, don't start the
+ // router module.
+ //
+ if (router->router_mode != DX_ROUTER_MODE_INTERIOR)
+ return;
+
+ PyObject *pDispatchModule = dx_python_module();
+
+ RouterAdapterType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&RouterAdapterType) < 0) {
+ PyErr_Print();
+ dx_log(module, LOG_CRITICAL, "Unable to initialize the Python Router Adapter");
+ return;
+ }
+
+ PyTypeObject *raType = &RouterAdapterType;
+ Py_INCREF(raType);
+ PyModule_AddObject(pDispatchModule, "RouterAdapter", (PyObject*) &RouterAdapterType);
+
+ //
+ // Attempt to import the Python Router module
+ //
+ PyObject* pName;
+ PyObject* pId;
+ PyObject* pArea;
+ PyObject* pMaxRouters;
+ PyObject* pModule;
+ PyObject* pClass;
+ PyObject* pArgs;
+
+ pName = PyString_FromString("qpid.dispatch.router");
+ pModule = PyImport_Import(pName);
+ Py_DECREF(pName);
+ if (!pModule) {
+ dx_log(module, LOG_CRITICAL, "Can't Locate 'router' Python module");
+ return;
+ }
+
+ pClass = PyObject_GetAttrString(pModule, "RouterEngine");
+ if (!pClass || !PyClass_Check(pClass)) {
+ dx_log(module, LOG_CRITICAL, "Can't Locate 'RouterEngine' class in the 'router' module");
+ return;
+ }
+
+ PyObject *adapterType = PyObject_GetAttrString(pDispatchModule, "RouterAdapter");
+ PyObject *adapterInstance = PyObject_CallObject(adapterType, 0);
+ assert(adapterInstance);
+
+ ((RouterAdapter*) adapterInstance)->router = router;
+
+ //
+ // Constructor Arguments for RouterEngine
+ //
+ pArgs = PyTuple_New(4);
+
+ // arg 0: adapter instance
+ PyTuple_SetItem(pArgs, 0, adapterInstance);
+
+ // arg 1: router_id
+ pId = PyString_FromString(router->router_id);
+ PyTuple_SetItem(pArgs, 1, pId);
+
+ // arg 2: area_id
+ pArea = PyString_FromString(router->router_area);
+ PyTuple_SetItem(pArgs, 2, pArea);
+
+ // arg 3: max_routers
+ pMaxRouters = PyInt_FromLong((long) dx_bitmask_width());
+ PyTuple_SetItem(pArgs, 3, pMaxRouters);
+
+ //
+ // Instantiate the router
+ //
+ router->pyRouter = PyInstance_New(pClass, pArgs, 0);
+ Py_DECREF(pArgs);
+ Py_DECREF(adapterType);
+
+ if (!router->pyRouter) {
+ PyErr_Print();
+ dx_log(module, LOG_CRITICAL, "'RouterEngine' class cannot be instantiated");
+ return;
+ }
+
+ router->pyTick = PyObject_GetAttrString(router->pyRouter, "handleTimerTick");
+ if (!router->pyTick || !PyCallable_Check(router->pyTick)) {
+ dx_log(module, LOG_CRITICAL, "'RouterEngine' class has no handleTimerTick method");
+ return;
+ }
+
+ router->pyAdded = PyObject_GetAttrString(router->pyRouter, "addressAdded");
+ if (!router->pyAdded || !PyCallable_Check(router->pyAdded)) {
+ dx_log(module, LOG_CRITICAL, "'RouterEngine' class has no addressAdded method");
+ return;
+ }
+
+ router->pyRemoved = PyObject_GetAttrString(router->pyRouter, "addressRemoved");
+ if (!router->pyRemoved || !PyCallable_Check(router->pyRemoved)) {
+ dx_log(module, LOG_CRITICAL, "'RouterEngine' class has no addressRemoved method");
+ return;
+ }
+}
+
+
+void dx_pyrouter_tick(dx_router_t *router)
+{
+ PyObject *pArgs;
+ PyObject *pValue;
+
+ if (router->pyTick && router->router_mode == DX_ROUTER_MODE_INTERIOR) {
+ dx_python_lock();
+ pArgs = PyTuple_New(0);
+ pValue = PyObject_CallObject(router->pyTick, pArgs);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ Py_DECREF(pArgs);
+ if (pValue) {
+ Py_DECREF(pValue);
+ }
+ dx_python_unlock();
+ }
+}
+
+
+void dx_router_mobile_added(dx_router_t *router, dx_field_iterator_t *iter)
+{
+ PyObject *pArgs;
+ PyObject *pValue;
+
+ if (router->pyAdded && router->router_mode == DX_ROUTER_MODE_INTERIOR) {
+ dx_field_iterator_reset_view(iter, ITER_VIEW_ADDRESS_HASH);
+ char *address = (char*) dx_field_iterator_copy(iter);
+
+ dx_python_lock();
+ pArgs = PyTuple_New(1);
+ PyTuple_SetItem(pArgs, 0, PyString_FromString(address));
+ pValue = PyObject_CallObject(router->pyAdded, pArgs);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ Py_DECREF(pArgs);
+ if (pValue) {
+ Py_DECREF(pValue);
+ }
+ dx_python_unlock();
+
+ free(address);
+ }
+}
+
+
+void dx_router_mobile_removed(dx_router_t *router, const char *address)
+{
+ PyObject *pArgs;
+ PyObject *pValue;
+
+ if (router->pyRemoved && router->router_mode == DX_ROUTER_MODE_INTERIOR) {
+ dx_python_lock();
+ pArgs = PyTuple_New(1);
+ PyTuple_SetItem(pArgs, 0, PyString_FromString(address));
+ pValue = PyObject_CallObject(router->pyRemoved, pArgs);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ Py_DECREF(pArgs);
+ if (pValue) {
+ Py_DECREF(pValue);
+ }
+ dx_python_unlock();
+ }
+}
+
diff --git a/qpid/extras/dispatch/src/server.c b/qpid/extras/dispatch/src/server.c
index 5420d3b776..65e181bd2c 100644
--- a/qpid/extras/dispatch/src/server.c
+++ b/qpid/extras/dispatch/src/server.c
@@ -109,6 +109,8 @@ static void thread_process_listeners(dx_server_t *dx_server)
ctx->listener = (dx_listener_t*) pn_listener_context(listener);
ctx->connector = 0;
ctx->context = ctx->listener->context;
+ ctx->user_context = 0;
+ ctx->link_context = 0;
ctx->ufd = 0;
pn_connection_t *conn = pn_connection();
@@ -327,7 +329,6 @@ static void *thread_run(void *arg)
dx_connection_t *ctx;
int error;
int poll_result;
- int timer_holdoff = 0;
if (!thread)
return 0;
@@ -372,35 +373,20 @@ static void *thread_run(void *arg)
//
// Service pending timers.
//
- if (DEQ_SIZE(dx_server->pending_timers) > 0) {
- dx_timer_list_t local_list;
- dx_timer_t *timer = DEQ_HEAD(dx_server->pending_timers);
-
- DEQ_INIT(local_list);
- while (timer) {
- DEQ_REMOVE_HEAD(dx_server->pending_timers);
- DEQ_INSERT_TAIL(local_list, timer);
- timer = DEQ_HEAD(dx_server->pending_timers);
- }
+ dx_timer_t *timer = DEQ_HEAD(dx_server->pending_timers);
+ if (timer) {
+ DEQ_REMOVE_HEAD(dx_server->pending_timers);
//
- // Release the lock and invoke the connection handlers.
+ // Mark the timer as idle in case it reschedules itself.
//
- sys_mutex_unlock(dx_server->lock);
-
- timer = DEQ_HEAD(local_list);
- while (timer) {
- DEQ_REMOVE_HEAD(local_list);
-
- //
- // Mark the timer as idle in case it reschedules itself.
- //
- dx_timer_idle_LH(timer);
-
- timer->handler(timer->context);
- timer = DEQ_HEAD(local_list);
- }
+ dx_timer_idle_LH(timer);
+ //
+ // Release the lock and invoke the connection handler.
+ //
+ sys_mutex_unlock(dx_server->lock);
+ timer->handler(timer->context);
pn_driver_wakeup(dx_server->driver);
continue;
}
@@ -464,13 +450,10 @@ static void *thread_run(void *arg)
//
// Visit the timer module.
//
- if (poll_result == 0 || ++timer_holdoff == 100) {
- struct timespec tv;
- clock_gettime(CLOCK_REALTIME, &tv);
- long milliseconds = tv.tv_sec * 1000 + tv.tv_nsec / 1000000;
- dx_timer_visit_LH(milliseconds);
- timer_holdoff = 0;
- }
+ struct timespec tv;
+ clock_gettime(CLOCK_REALTIME, &tv);
+ long milliseconds = tv.tv_sec * 1000 + tv.tv_nsec / 1000000;
+ dx_timer_visit_LH(milliseconds);
//
// Process listeners (incoming connections).
@@ -630,6 +613,7 @@ static void cxtr_try_open(void *context)
ctx->connector = ct;
ctx->context = ct->context;
ctx->user_context = 0;
+ ctx->link_context = 0;
ctx->ufd = 0;
//
@@ -878,12 +862,32 @@ void *dx_connection_get_context(dx_connection_t *conn)
}
+void dx_connection_set_link_context(dx_connection_t *conn, void *context)
+{
+ conn->link_context = context;
+}
+
+
+void *dx_connection_get_link_context(dx_connection_t *conn)
+{
+ return conn->link_context;
+}
+
+
pn_connection_t *dx_connection_pn(dx_connection_t *conn)
{
return conn->pn_conn;
}
+const dx_server_config_t *dx_connection_config(const dx_connection_t *conn)
+{
+ if (conn->listener)
+ return conn->listener->config;
+ return conn->connector->config;
+}
+
+
dx_listener_t *dx_server_listen(dx_dispatch_t *dx, const dx_server_config_t *config, void *context)
{
dx_server_t *dx_server = dx->server;
@@ -976,6 +980,7 @@ dx_user_fd_t *dx_user_fd(dx_dispatch_t *dx, int fd, void *context)
ctx->connector = 0;
ctx->context = 0;
ctx->user_context = 0;
+ ctx->link_context = 0;
ctx->ufd = ufd;
ufd->context = context;
diff --git a/qpid/extras/dispatch/src/server_private.h b/qpid/extras/dispatch/src/server_private.h
index 5782492f22..86a3ef98f1 100644
--- a/qpid/extras/dispatch/src/server_private.h
+++ b/qpid/extras/dispatch/src/server_private.h
@@ -78,6 +78,7 @@ struct dx_connection_t {
dx_connector_t *connector;
void *context; // Copy of context from listener or connector
void *user_context;
+ void *link_context; // Context shared by this connection's links
dx_user_fd_t *ufd;
};
diff --git a/qpid/extras/dispatch/tests/compose_test.c b/qpid/extras/dispatch/tests/compose_test.c
index 9eaeeaa038..06292c221f 100644
--- a/qpid/extras/dispatch/tests/compose_test.c
+++ b/qpid/extras/dispatch/tests/compose_test.c
@@ -27,6 +27,105 @@
#include "compose_private.h"
+static char *vector0 =
+ "\x00\x53\x77" // amqp-value
+ "\xd0\x00\x00\x01\x26\x00\x00\x00\x0a" // list32 with ten items
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x0a" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x0b" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ "\xd1\x00\x00\x00\x18\x00\x00\x00\x04" // map32 with two pairs
+ "\xa1\x06key001" // str8-utf8
+ "\x52\x14" // smalluint
+ "\xa1\x06key002" // str8-utf8
+ "\x52\x15" // smalluint
+ ;
+
+static int vector0_length = 302;
+
+static char *test_compose_list_of_maps(void *context)
+{
+ dx_composed_field_t *field = dx_compose(DX_PERFORMATIVE_BODY_AMQP_VALUE, 0);
+
+ dx_compose_start_list(field);
+
+ dx_compose_start_map(field);
+ dx_compose_insert_string(field, "key001");
+ dx_compose_insert_uint(field, 10);
+ dx_compose_insert_string(field, "key002");
+ dx_compose_insert_uint(field, 11);
+ dx_compose_end_map(field);
+
+ for (int j = 0; j < 9; j++) {
+ dx_compose_start_map(field);
+ dx_compose_insert_string(field, "key001");
+ dx_compose_insert_uint(field, 20);
+ dx_compose_insert_string(field, "key002");
+ dx_compose_insert_uint(field, 21);
+ dx_compose_end_map(field);
+ }
+
+ dx_compose_end_list(field);
+
+ dx_buffer_t *buf = DEQ_HEAD(field->buffers);
+
+ if (dx_buffer_size(buf) != vector0_length) return "Incorrect Length of Buffer";
+
+ char *left = vector0;
+ char *right = (char*) dx_buffer_base(buf);
+ int idx;
+
+ for (idx = 0; idx < vector0_length; idx++) {
+ if (*left != *right) return "Pattern Mismatch";
+ left++;
+ right++;
+ }
+
+ dx_compose_free(field);
+ return 0;
+}
+
static char *vector1 =
"\x00\x53\x71" // delivery annotations
"\xd1\x00\x00\x00\x3d\x00\x00\x00\x04" // map32 with two item pairs
@@ -184,6 +283,7 @@ int compose_tests()
int result = 0;
dx_log_set_mask(LOG_NONE);
+ TEST_CASE(test_compose_list_of_maps, 0);
TEST_CASE(test_compose_nested_composites, 0);
TEST_CASE(test_compose_scalars, 0);
diff --git a/qpid/extras/dispatch/tests/onerouter.conf b/qpid/extras/dispatch/tests/config-1/A.conf
index 308d86473c..308d86473c 100644
--- a/qpid/extras/dispatch/tests/onerouter.conf
+++ b/qpid/extras/dispatch/tests/config-1/A.conf
diff --git a/qpid/extras/dispatch/tests/config-2/A.conf b/qpid/extras/dispatch/tests/config-2/A.conf
new file mode 100644
index 0000000000..0d00efdbd4
--- /dev/null
+++ b/qpid/extras/dispatch/tests/config-2/A.conf
@@ -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
+##
+
+
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+container {
+ ##
+ ## worker-threads - The number of threads that will be created to
+ ## process message traffic and other application work (timers, non-amqp
+ ## file descriptors, etc.)
+ ##
+ ## The number of threads should be related to the number of available
+ ## processor cores. To fully utilize a quad-core system, set the
+ ## number of threads to 4.
+ ##
+ worker-threads: 4
+
+ ##
+ ## container-name - The name of the AMQP container. If not specified,
+ ## the container name will be set to a value of the container's
+ ## choosing. The automatically assigned container name is not
+ ## guaranteed to be persistent across restarts of the container.
+ ##
+ container-name: Qpid.Dispatch.Router.A
+}
+
+
+##
+## Listeners and Connectors
+##
+listener {
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20001
+ sasl-mechanisms: ANONYMOUS
+}
+
+router {
+ mode: interior
+ area: A
+ router-id: QDR.A
+}
diff --git a/qpid/extras/dispatch/tests/config-2/B.conf b/qpid/extras/dispatch/tests/config-2/B.conf
new file mode 100644
index 0000000000..8999c9878c
--- /dev/null
+++ b/qpid/extras/dispatch/tests/config-2/B.conf
@@ -0,0 +1,68 @@
+##
+## 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
+##
+
+
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+container {
+ ##
+ ## worker-threads - The number of threads that will be created to
+ ## process message traffic and other application work (timers, non-amqp
+ ## file descriptors, etc.)
+ ##
+ ## The number of threads should be related to the number of available
+ ## processor cores. To fully utilize a quad-core system, set the
+ ## number of threads to 4.
+ ##
+ worker-threads: 4
+
+ ##
+ ## container-name - The name of the AMQP container. If not specified,
+ ## the container name will be set to a value of the container's
+ ## choosing. The automatically assigned container name is not
+ ## guaranteed to be persistent across restarts of the container.
+ ##
+ container-name: Qpid.Dispatch.Router.B
+}
+
+
+##
+## Listeners and Connectors
+##
+listener {
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20002
+ sasl-mechanisms: ANONYMOUS
+}
+
+connector {
+ label: Router Uplink
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20001
+ sasl-mechanisms: ANONYMOUS
+}
+
+router {
+ mode: interior
+ area: A
+ router-id: QDR.B
+}
diff --git a/qpid/extras/dispatch/tests/config-3-linear/A.conf b/qpid/extras/dispatch/tests/config-3-linear/A.conf
new file mode 100644
index 0000000000..3f18ad668c
--- /dev/null
+++ b/qpid/extras/dispatch/tests/config-3-linear/A.conf
@@ -0,0 +1,70 @@
+##
+## 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
+##
+
+
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+container {
+ ##
+ ## worker-threads - The number of threads that will be created to
+ ## process message traffic and other application work (timers, non-amqp
+ ## file descriptors, etc.)
+ ##
+ ## The number of threads should be related to the number of available
+ ## processor cores. To fully utilize a quad-core system, set the
+ ## number of threads to 4.
+ ##
+ worker-threads: 4
+
+ ##
+ ## container-name - The name of the AMQP container. If not specified,
+ ## the container name will be set to a value of the container's
+ ## choosing. The automatically assigned container name is not
+ ## guaranteed to be persistent across restarts of the container.
+ ##
+ container-name: QDR.A
+}
+
+
+##
+## Listeners and Connectors
+##
+listener {
+ addr: 0.0.0.0
+ port: 20001
+ sasl-mechanisms: ANONYMOUS
+}
+
+connector {
+ label: Router Uplink
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20002
+ sasl-mechanisms: ANONYMOUS
+}
+
+router {
+ mode: interior
+ area: A
+ router-id: QDR.A
+}
+
+
+
diff --git a/qpid/extras/dispatch/tests/config-3-linear/B.conf b/qpid/extras/dispatch/tests/config-3-linear/B.conf
new file mode 100644
index 0000000000..10310306d6
--- /dev/null
+++ b/qpid/extras/dispatch/tests/config-3-linear/B.conf
@@ -0,0 +1,69 @@
+##
+## 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
+##
+
+
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+container {
+ ##
+ ## worker-threads - The number of threads that will be created to
+ ## process message traffic and other application work (timers, non-amqp
+ ## file descriptors, etc.)
+ ##
+ ## The number of threads should be related to the number of available
+ ## processor cores. To fully utilize a quad-core system, set the
+ ## number of threads to 4.
+ ##
+ worker-threads: 4
+
+ ##
+ ## container-name - The name of the AMQP container. If not specified,
+ ## the container name will be set to a value of the container's
+ ## choosing. The automatically assigned container name is not
+ ## guaranteed to be persistent across restarts of the container.
+ ##
+ container-name: QDR.B
+}
+
+
+##
+## Listeners and Connectors
+##
+listener {
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20002
+ sasl-mechanisms: ANONYMOUS
+}
+
+connector {
+ label: Router Uplink
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20003
+ sasl-mechanisms: ANONYMOUS
+}
+
+
+router {
+ mode: interior
+ area: A
+ router-id: QDR.B
+}
diff --git a/qpid/extras/dispatch/tests/config-3-linear/C.conf b/qpid/extras/dispatch/tests/config-3-linear/C.conf
new file mode 100644
index 0000000000..83c0ff603d
--- /dev/null
+++ b/qpid/extras/dispatch/tests/config-3-linear/C.conf
@@ -0,0 +1,61 @@
+##
+## 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
+##
+
+
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+container {
+ ##
+ ## worker-threads - The number of threads that will be created to
+ ## process message traffic and other application work (timers, non-amqp
+ ## file descriptors, etc.)
+ ##
+ ## The number of threads should be related to the number of available
+ ## processor cores. To fully utilize a quad-core system, set the
+ ## number of threads to 4.
+ ##
+ worker-threads: 4
+
+ ##
+ ## container-name - The name of the AMQP container. If not specified,
+ ## the container name will be set to a value of the container's
+ ## choosing. The automatically assigned container name is not
+ ## guaranteed to be persistent across restarts of the container.
+ ##
+ container-name: QDR.C
+}
+
+
+##
+## Listeners and Connectors
+##
+listener {
+ role: inter-router
+ addr: 0.0.0.0
+ port: 20003
+ sasl-mechanisms: ANONYMOUS
+}
+
+
+router {
+ mode: interior
+ area: A
+ router-id: QDR.C
+}
diff --git a/qpid/extras/dispatch/tests/config-3-linear/topology.txt b/qpid/extras/dispatch/tests/config-3-linear/topology.txt
new file mode 100644
index 0000000000..07d6d74008
--- /dev/null
+++ b/qpid/extras/dispatch/tests/config-3-linear/topology.txt
@@ -0,0 +1,12 @@
+
+
+ +----------+ +----------+ +----------+
+ |QDR.A | |QDR.B | |QDR.C |
+ |port: | |port: | |port: |
+ | 20001 |--------->| 20002 |--------->| 20003 |
+ | | | | | |
+ | | | | | |
+ +----------+ +----------+ +----------+
+
+ * The direction of the arrow shows the direction of the connection setup
+ Connector --> Listener
diff --git a/qpid/extras/dispatch/tests/field_test.c b/qpid/extras/dispatch/tests/field_test.c
index 59fc3c4cfb..33f9fe5d03 100644
--- a/qpid/extras/dispatch/tests/field_test.c
+++ b/qpid/extras/dispatch/tests/field_test.c
@@ -115,6 +115,10 @@ static char* test_view_address_hash(void *context)
{"amqp:/_topo/my-area/all/local/sub", "Llocal/sub"},
{"amqp:/_topo/all/all/local/sub", "Llocal/sub"},
{"amqp://host:port/_local/my-addr", "Lmy-addr"},
+ {"_topo/area/router/my-addr", "Aarea"},
+ {"_topo/my-area/router/my-addr", "Rrouter"},
+ {"_topo/my-area/my-router/my-addr", "Lmy-addr"},
+ {"_topo/my-area/router", "Rrouter"},
{0, 0}
};
int idx;
diff --git a/qpid/extras/dispatch/tests/message_test.c b/qpid/extras/dispatch/tests/message_test.c
index 2873f48740..59f4e444dc 100644
--- a/qpid/extras/dispatch/tests/message_test.c
+++ b/qpid/extras/dispatch/tests/message_test.c
@@ -47,7 +47,7 @@ static void set_content(dx_message_content_t *content, size_t len)
dx_buffer_t *buf;
while (len > (size_t) (cursor - buffer)) {
- buf = dx_allocate_buffer();
+ buf = dx_buffer();
size_t segment = dx_buffer_capacity(buf);
size_t remaining = len - (size_t) (cursor - buffer);
if (segment > remaining)
@@ -62,7 +62,7 @@ static void set_content(dx_message_content_t *content, size_t len)
static char* test_send_to_messenger(void *context)
{
- dx_message_t *msg = dx_allocate_message();
+ dx_message_t *msg = dx_message();
dx_message_content_t *content = MSG_CONTENT(msg);
dx_message_compose_1(msg, "test_addr_0", 0);
@@ -78,7 +78,7 @@ static char* test_send_to_messenger(void *context)
return "Address mismatch in received message";
pn_message_free(pn_msg);
- dx_free_message(msg);
+ dx_message_free(msg);
return 0;
}
@@ -93,7 +93,7 @@ static char* test_receive_from_messenger(void *context)
int result = pn_message_encode(pn_msg, buffer, &size);
if (result != 0) return "Error in pn_message_encode";
- dx_message_t *msg = dx_allocate_message();
+ dx_message_t *msg = dx_message();
dx_message_content_t *content = MSG_CONTENT(msg);
set_content(content, size);
@@ -118,7 +118,7 @@ static char* test_receive_from_messenger(void *context)
return "Incorrect field content returned from field_copy";
pn_message_free(pn_msg);
- dx_free_message(msg);
+ dx_message_free(msg);
return 0;
}
@@ -133,7 +133,7 @@ static char* test_insufficient_check_depth(void *context)
int result = pn_message_encode(pn_msg, buffer, &size);
if (result != 0) return "Error in pn_message_encode";
- dx_message_t *msg = dx_allocate_message();
+ dx_message_t *msg = dx_message();
dx_message_content_t *content = MSG_CONTENT(msg);
set_content(content, size);
@@ -144,7 +144,7 @@ static char* test_insufficient_check_depth(void *context)
dx_field_iterator_t *iter = dx_message_field_iterator(msg, DX_FIELD_TO);
if (iter) return "Expected no iterator for the 'to' field";
- dx_free_message(msg);
+ dx_message_free(msg);
return 0;
}
@@ -159,7 +159,7 @@ static char* test_check_multiple(void *context)
int result = pn_message_encode(pn_msg, buffer, &size);
if (result != 0) return "Error in pn_message_encode";
- dx_message_t *msg = dx_allocate_message();
+ dx_message_t *msg = dx_message();
dx_message_content_t *content = MSG_CONTENT(msg);
set_content(content, size);
@@ -173,7 +173,7 @@ static char* test_check_multiple(void *context)
valid = dx_message_check(msg, DX_DEPTH_PROPERTIES);
if (!valid) return "dx_message_check returns 'invalid' for PROPERTIES";
- dx_free_message(msg);
+ dx_message_free(msg);
return 0;
}
diff --git a/qpid/extras/dispatch/tests/router_engine_test.py b/qpid/extras/dispatch/tests/router_engine_test.py
index 605e7568d3..e25ce35d22 100644
--- a/qpid/extras/dispatch/tests/router_engine_test.py
+++ b/qpid/extras/dispatch/tests/router_engine_test.py
@@ -18,393 +18,634 @@
#
import unittest
-from qpid.dispatch.router.router_engine import NeighborEngine, PathEngine, Configuration
+from qpid.dispatch.router.router_engine import NeighborEngine, PathEngine, Configuration, NodeTracker
from qpid.dispatch.router.data import LinkState, MessageHELLO
class Adapter(object):
- def __init__(self, domain):
- self._domain = domain
+ def __init__(self, domain):
+ self._domain = domain
- def log(self, level, text):
- print "Adapter.log(%d): domain=%s, text=%s" % (level, self._domain, text)
+ def log(self, level, text):
+ print "Adapter.log(%d): domain=%s, text=%s" % (level, self._domain, text)
- def send(self, dest, opcode, body):
- print "Adapter.send: domain=%s, dest=%s, opcode=%s, body=%s" % (self._domain, dest, opcode, body)
- def remote_bind(self, subject, peer):
- print "Adapter.remote_bind: subject=%s, peer=%s" % (subject, peer)
+ def send(self, dest, opcode, body):
+ print "Adapter.send: domain=%s, dest=%s, opcode=%s, body=%s" % (self._domain, dest, opcode, body)
- def remote_unbind(self, subject, peer):
- print "Adapter.remote_unbind: subject=%s, peer=%s" % (subject, peer)
+ def remote_bind(self, subject, peer):
+ print "Adapter.remote_bind: subject=%s, peer=%s" % (subject, peer)
+ def remote_unbind(self, subject, peer):
+ print "Adapter.remote_unbind: subject=%s, peer=%s" % (subject, peer)
+
+ def node_updated(self, address, reachable, neighbor, link_bit, router_bit):
+ print "Adapter.node_updated: address=%s, reachable=%r, neighbor=%r, link_bit=%d, router_bit=%d" % \
+ (address, reachable, neighbor, link_bit, router_bit)
-class DataTest(unittest.TestCase):
- def test_link_state(self):
- ls = LinkState(None, 'R1', 'area', 1, ['R2', 'R3'])
- self.assertEqual(ls.id, 'R1')
- self.assertEqual(ls.area, 'area')
- self.assertEqual(ls.ls_seq, 1)
- self.assertEqual(ls.peers, ['R2', 'R3'])
- ls.bump_sequence()
- self.assertEqual(ls.id, 'R1')
- self.assertEqual(ls.area, 'area')
- self.assertEqual(ls.ls_seq, 2)
- self.assertEqual(ls.peers, ['R2', 'R3'])
-
- result = ls.add_peer('R4')
- self.assertTrue(result)
- self.assertEqual(ls.peers, ['R2', 'R3', 'R4'])
- result = ls.add_peer('R2')
- self.assertFalse(result)
- self.assertEqual(ls.peers, ['R2', 'R3', 'R4'])
-
- result = ls.del_peer('R3')
- self.assertTrue(result)
- self.assertEqual(ls.peers, ['R2', 'R4'])
- result = ls.del_peer('R5')
- self.assertFalse(result)
- self.assertEqual(ls.peers, ['R2', 'R4'])
-
- encoded = ls.to_dict()
- new_ls = LinkState(encoded)
- self.assertEqual(new_ls.id, 'R1')
- self.assertEqual(new_ls.area, 'area')
- self.assertEqual(new_ls.ls_seq, 2)
- self.assertEqual(new_ls.peers, ['R2', 'R4'])
-
-
- def test_hello_message(self):
- msg1 = MessageHELLO(None, 'R1', 'area', ['R2', 'R3', 'R4'])
- self.assertEqual(msg1.get_opcode(), "HELLO")
- self.assertEqual(msg1.id, 'R1')
- self.assertEqual(msg1.area, 'area')
- self.assertEqual(msg1.seen_peers, ['R2', 'R3', 'R4'])
- encoded = msg1.to_dict()
- msg2 = MessageHELLO(encoded)
- self.assertEqual(msg2.get_opcode(), "HELLO")
- self.assertEqual(msg2.id, 'R1')
- self.assertEqual(msg2.area, 'area')
- self.assertEqual(msg2.seen_peers, ['R2', 'R3', 'R4'])
- self.assertTrue(msg2.is_seen('R3'))
- self.assertFalse(msg2.is_seen('R9'))
+class DataTest(unittest.TestCase):
+ def test_link_state(self):
+ ls = LinkState(None, 'R1', 'area', 1, ['R2', 'R3'])
+ self.assertEqual(ls.id, 'R1')
+ self.assertEqual(ls.area, 'area')
+ self.assertEqual(ls.ls_seq, 1)
+ self.assertEqual(ls.peers, ['R2', 'R3'])
+ ls.bump_sequence()
+ self.assertEqual(ls.id, 'R1')
+ self.assertEqual(ls.area, 'area')
+ self.assertEqual(ls.ls_seq, 2)
+ self.assertEqual(ls.peers, ['R2', 'R3'])
+
+ result = ls.add_peer('R4')
+ self.assertTrue(result)
+ self.assertEqual(ls.peers, ['R2', 'R3', 'R4'])
+ result = ls.add_peer('R2')
+ self.assertFalse(result)
+ self.assertEqual(ls.peers, ['R2', 'R3', 'R4'])
+
+ result = ls.del_peer('R3')
+ self.assertTrue(result)
+ self.assertEqual(ls.peers, ['R2', 'R4'])
+ result = ls.del_peer('R5')
+ self.assertFalse(result)
+ self.assertEqual(ls.peers, ['R2', 'R4'])
+
+ encoded = ls.to_dict()
+ new_ls = LinkState(encoded)
+ self.assertEqual(new_ls.id, 'R1')
+ self.assertEqual(new_ls.area, 'area')
+ self.assertEqual(new_ls.ls_seq, 2)
+ self.assertEqual(new_ls.peers, ['R2', 'R4'])
+
+
+ def test_hello_message(self):
+ msg1 = MessageHELLO(None, 'R1', 'area', ['R2', 'R3', 'R4'])
+ self.assertEqual(msg1.get_opcode(), "HELLO")
+ self.assertEqual(msg1.id, 'R1')
+ self.assertEqual(msg1.area, 'area')
+ self.assertEqual(msg1.seen_peers, ['R2', 'R3', 'R4'])
+ encoded = msg1.to_dict()
+ msg2 = MessageHELLO(encoded)
+ self.assertEqual(msg2.get_opcode(), "HELLO")
+ self.assertEqual(msg2.id, 'R1')
+ self.assertEqual(msg2.area, 'area')
+ self.assertEqual(msg2.seen_peers, ['R2', 'R3', 'R4'])
+ self.assertTrue(msg2.is_seen('R3'))
+ self.assertFalse(msg2.is_seen('R9'))
+
+
+class NodeTrackerTest(unittest.TestCase):
+ def log(self, level, text):
+ pass
+
+ def add_neighbor_router(self, address, router_bit, link_bit):
+ self.address = address
+ self.router_bit = router_bit
+ self.link_bit = link_bit
+ self.calls += 1
+
+ def del_neighbor_router(self, router_bit):
+ self.address = None
+ self.router_bit = router_bit
+ self.link_bit = None
+ self.calls += 1
+
+ def add_remote_router(self, address, router_bit):
+ self.address = address
+ self.router_bit = router_bit
+ self.link_bit = None
+ self.calls += 1
+
+ def del_remote_router(self, router_bit):
+ self.address = None
+ self.router_bit = router_bit
+ self.link_bit = None
+ self.calls += 1
+
+ def reset(self):
+ self.address = None
+ self.router_bit = None
+ self.link_bit = None
+ self.area = "area"
+ self.calls = 0
+
+ def test_node_tracker_limits(self):
+ tracker = NodeTracker(self, 5)
+
+ self.reset()
+ tracker.new_neighbor('A', 1)
+ self.assertEqual(self.address, 'amqp:/_topo/area/A')
+ self.assertEqual(self.link_bit, 1)
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ tracker.new_neighbor('B', 5)
+ self.assertEqual(self.address, 'amqp:/_topo/area/B')
+ self.assertEqual(self.link_bit, 5)
+ self.assertEqual(self.router_bit, 2)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ tracker.new_neighbor('C', 6)
+ self.assertEqual(self.address, 'amqp:/_topo/area/C')
+ self.assertEqual(self.link_bit, 6)
+ self.assertEqual(self.router_bit, 3)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ tracker.new_neighbor('D', 7)
+ self.assertEqual(self.address, 'amqp:/_topo/area/D')
+ self.assertEqual(self.link_bit, 7)
+ self.assertEqual(self.router_bit, 4)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ try:
+ tracker.new_neighbor('E', 9)
+ AssertFalse("We shouldn't be here")
+ except:
+ pass
+
+ self.reset()
+ tracker.lost_neighbor('C')
+ self.assertEqual(self.router_bit, 3)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ tracker.new_neighbor('E', 9)
+ self.assertEqual(self.address, 'amqp:/_topo/area/E')
+ self.assertEqual(self.link_bit, 9)
+ self.assertEqual(self.router_bit, 3)
+ self.assertEqual(self.calls, 1)
+
+
+ def test_node_tracker_remote_neighbor(self):
+ tracker = NodeTracker(self, 5)
+
+ self.reset()
+ tracker.new_node('A')
+ self.assertEqual(self.address, 'amqp:/_topo/area/A')
+ self.assertFalse(self.link_bit)
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ tracker.new_neighbor('A', 3)
+ self.assertEqual(self.address, 'amqp:/_topo/area/A')
+ self.assertEqual(self.link_bit, 3)
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 2)
+
+ self.reset()
+ tracker.lost_node('A')
+ self.assertFalse(self.address)
+ self.assertFalse(self.link_bit)
+ self.assertFalse(self.router_bit)
+ self.assertEqual(self.calls, 0)
+
+ self.reset()
+ tracker.lost_neighbor('A')
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 1)
+
+
+ def test_node_tracker_neighbor_remote(self):
+ tracker = NodeTracker(self, 5)
+
+ self.reset()
+ tracker.new_neighbor('A', 3)
+ self.assertEqual(self.address, 'amqp:/_topo/area/A')
+ self.assertEqual(self.link_bit, 3)
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 1)
+
+ self.reset()
+ tracker.new_node('A')
+ self.assertFalse(self.address)
+ self.assertFalse(self.link_bit)
+ self.assertFalse(self.router_bit)
+ self.assertEqual(self.calls, 0)
+
+ self.reset()
+ tracker.lost_neighbor('A')
+ self.assertEqual(self.address, 'amqp:/_topo/area/A')
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 2)
+
+ self.reset()
+ tracker.lost_node('A')
+ self.assertEqual(self.router_bit, 1)
+ self.assertEqual(self.calls, 1)
class NeighborTest(unittest.TestCase):
- def log(self, level, text):
- pass
-
- def send(self, dest, msg):
- self.sent.append((dest, msg))
-
- def local_link_state_changed(self, link_state):
- self.local_link_state = link_state
-
- def new_neighbor(self, rid):
- self.neighbors[rid] = None
-
- def lost_neighbor(self, rid):
- self.neighbors.pop(rid)
-
- def setUp(self):
- self.sent = []
- self.local_link_state = None
- self.id = "R1"
- self.area = "area"
- self.config = Configuration()
- self.neighbors = {}
-
- def test_hello_sent(self):
- self.sent = []
- self.local_link_state = None
- self.engine = NeighborEngine(self)
- self.engine.tick(0.5)
- self.assertEqual(self.sent, [])
- self.engine.tick(1.5)
- self.assertEqual(len(self.sent), 1)
- dest, msg = self.sent.pop(0)
- self.assertEqual(dest, "_local/qdxrouter")
- self.assertEqual(msg.get_opcode(), "HELLO")
- self.assertEqual(msg.id, self.id)
- self.assertEqual(msg.area, self.area)
- self.assertEqual(msg.seen_peers, [])
- self.assertEqual(self.local_link_state, None)
-
- def test_sees_peer(self):
- self.sent = []
- self.local_link_state = None
- self.engine = NeighborEngine(self)
- self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', []), 2.0)
- self.engine.tick(5.0)
- self.assertEqual(len(self.sent), 1)
- dest, msg = self.sent.pop(0)
- self.assertEqual(msg.seen_peers, ['R2'])
-
- def test_establish_peer(self):
- self.sent = []
- self.local_link_state = None
- self.engine = NeighborEngine(self)
- self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', ['R1']), 0.5)
- self.engine.tick(1.0)
- self.engine.tick(2.0)
- self.engine.tick(3.0)
- self.assertEqual(self.local_link_state.id, 'R1')
- self.assertEqual(self.local_link_state.area, 'area')
- self.assertEqual(self.local_link_state.ls_seq, 1)
- self.assertEqual(self.local_link_state.peers, ['R2'])
-
- def test_establish_multiple_peers(self):
- self.sent = []
- self.local_link_state = None
- self.engine = NeighborEngine(self)
- self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', ['R1']), 0.5)
- self.engine.tick(1.0)
- self.engine.handle_hello(MessageHELLO(None, 'R3', 'area', ['R1', 'R2']), 1.5)
- self.engine.tick(2.0)
- self.engine.handle_hello(MessageHELLO(None, 'R4', 'area', ['R1']), 2.5)
- self.engine.handle_hello(MessageHELLO(None, 'R5', 'area', ['R2']), 2.5)
- self.engine.handle_hello(MessageHELLO(None, 'R6', 'area', ['R1']), 2.5)
- self.engine.tick(3.0)
- self.assertEqual(self.local_link_state.id, 'R1')
- self.assertEqual(self.local_link_state.area, 'area')
- self.assertEqual(self.local_link_state.ls_seq, 3)
- self.local_link_state.peers.sort()
- self.assertEqual(self.local_link_state.peers, ['R2', 'R3', 'R4', 'R6'])
-
- def test_timeout_peer(self):
- self.sent = []
- self.local_link_state = None
- self.engine = NeighborEngine(self)
- self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', ['R3', 'R1']), 2.0)
- self.engine.tick(5.0)
- self.engine.tick(17.1)
- self.assertEqual(self.local_link_state.id, 'R1')
- self.assertEqual(self.local_link_state.area, 'area')
- self.assertEqual(self.local_link_state.ls_seq, 2)
- self.assertEqual(self.local_link_state.peers, [])
+ def log(self, level, text):
+ pass
+
+ def send(self, dest, msg):
+ self.sent.append((dest, msg))
+
+ def local_link_state_changed(self, link_state):
+ self.local_link_state = link_state
+
+ def new_neighbor(self, rid, lbit):
+ self.neighbors[rid] = None
+
+ def lost_neighbor(self, rid):
+ self.neighbors.pop(rid)
+
+ def setUp(self):
+ self.sent = []
+ self.local_link_state = None
+ self.id = "R1"
+ self.area = "area"
+ self.config = Configuration()
+ self.neighbors = {}
+
+ def test_hello_sent(self):
+ self.sent = []
+ self.local_link_state = None
+ self.engine = NeighborEngine(self)
+ self.engine.tick(0.5)
+ self.assertEqual(self.sent, [])
+ self.engine.tick(1.5)
+ self.assertEqual(len(self.sent), 1)
+ dest, msg = self.sent.pop(0)
+ self.assertEqual(dest, "amqp:/_local/qdxhello")
+ self.assertEqual(msg.get_opcode(), "HELLO")
+ self.assertEqual(msg.id, self.id)
+ self.assertEqual(msg.area, self.area)
+ self.assertEqual(msg.seen_peers, [])
+ self.assertEqual(self.local_link_state, None)
+
+ def test_sees_peer(self):
+ self.sent = []
+ self.local_link_state = None
+ self.engine = NeighborEngine(self)
+ self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', []), 2.0, 0)
+ self.engine.tick(5.0)
+ self.assertEqual(len(self.sent), 1)
+ dest, msg = self.sent.pop(0)
+ self.assertEqual(msg.seen_peers, ['R2'])
+
+ def test_establish_peer(self):
+ self.sent = []
+ self.local_link_state = None
+ self.engine = NeighborEngine(self)
+ self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', ['R1']), 0.5, 0)
+ self.engine.tick(1.0)
+ self.engine.tick(2.0)
+ self.engine.tick(3.0)
+ self.assertEqual(self.local_link_state.id, 'R1')
+ self.assertEqual(self.local_link_state.area, 'area')
+ self.assertEqual(self.local_link_state.ls_seq, 1)
+ self.assertEqual(self.local_link_state.peers, ['R2'])
+
+ def test_establish_multiple_peers(self):
+ self.sent = []
+ self.local_link_state = None
+ self.engine = NeighborEngine(self)
+ self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', ['R1']), 0.5, 0)
+ self.engine.tick(1.0)
+ self.engine.handle_hello(MessageHELLO(None, 'R3', 'area', ['R1', 'R2']), 1.5, 0)
+ self.engine.tick(2.0)
+ self.engine.handle_hello(MessageHELLO(None, 'R4', 'area', ['R1']), 2.5, 0)
+ self.engine.handle_hello(MessageHELLO(None, 'R5', 'area', ['R2']), 2.5, 0)
+ self.engine.handle_hello(MessageHELLO(None, 'R6', 'area', ['R1']), 2.5, 0)
+ self.engine.tick(3.0)
+ self.assertEqual(self.local_link_state.id, 'R1')
+ self.assertEqual(self.local_link_state.area, 'area')
+ self.assertEqual(self.local_link_state.ls_seq, 3)
+ self.local_link_state.peers.sort()
+ self.assertEqual(self.local_link_state.peers, ['R2', 'R3', 'R4', 'R6'])
+
+ def test_timeout_peer(self):
+ self.sent = []
+ self.local_link_state = None
+ self.engine = NeighborEngine(self)
+ self.engine.handle_hello(MessageHELLO(None, 'R2', 'area', ['R3', 'R1']), 2.0, 0)
+ self.engine.tick(5.0)
+ self.engine.tick(17.1)
+ self.assertEqual(self.local_link_state.id, 'R1')
+ self.assertEqual(self.local_link_state.area, 'area')
+ self.assertEqual(self.local_link_state.ls_seq, 2)
+ self.assertEqual(self.local_link_state.peers, [])
class PathTest(unittest.TestCase):
- def setUp(self):
- self.id = 'R1'
- self.area = 'area'
- self.next_hops = None
- self.engine = PathEngine(self)
-
- def log(self, level, text):
- pass
-
- def next_hops_changed(self, nh):
- self.next_hops = nh
-
- def test_topology1(self):
- """
-
- +====+ +----+ +----+
- | R1 |------| R2 |------| R3 |
- +====+ +----+ +----+
-
- """
- collection = { 'R1': LinkState(None, 'R1', 'area', 1, ['R2']),
- 'R2': LinkState(None, 'R2', 'area', 1, ['R1', 'R3']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R2']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 2)
- self.assertEqual(self.next_hops['R2'], 'R2')
- self.assertEqual(self.next_hops['R3'], 'R2')
-
- def test_topology2(self):
- """
-
- +====+ +----+ +----+
- | R1 |------| R2 |------| R4 |
- +====+ +----+ +----+
- | |
- +----+ +----+ +----+
- | R3 |------| R5 |------| R6 |
- +----+ +----+ +----+
-
- """
- collection = { 'R1': LinkState(None, 'R1', 'area', 1, ['R2']),
- 'R2': LinkState(None, 'R2', 'area', 1, ['R1', 'R3', 'R4']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R2', 'R5']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R2', 'R5']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R3', 'R4', 'R6']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 5)
- self.assertEqual(self.next_hops['R2'], 'R2')
- self.assertEqual(self.next_hops['R3'], 'R2')
- self.assertEqual(self.next_hops['R4'], 'R2')
- self.assertEqual(self.next_hops['R5'], 'R2')
- self.assertEqual(self.next_hops['R6'], 'R2')
-
- def test_topology3(self):
- """
-
- +----+ +----+ +----+
- | R2 |------| R3 |------| R4 |
- +----+ +----+ +----+
- | |
- +====+ +----+ +----+
- | R1 |------| R5 |------| R6 |
- +====+ +----+ +----+
-
- """
- collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
- 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 5)
- self.assertEqual(self.next_hops['R2'], 'R3')
- self.assertEqual(self.next_hops['R3'], 'R3')
- self.assertEqual(self.next_hops['R4'], 'R3')
- self.assertEqual(self.next_hops['R5'], 'R5')
- self.assertEqual(self.next_hops['R6'], 'R5')
-
- def test_topology4(self):
- """
-
- +----+ +----+ +----+
- | R2 |------| R3 |------| R4 |
- +----+ +----+ +----+
- | |
- +====+ +----+ +----+
- | R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
- +====+ +----+ +----+
-
- """
- collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
- 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 6)
- self.assertEqual(self.next_hops['R2'], 'R3')
- self.assertEqual(self.next_hops['R3'], 'R3')
- self.assertEqual(self.next_hops['R4'], 'R3')
- self.assertEqual(self.next_hops['R5'], 'R5')
- self.assertEqual(self.next_hops['R6'], 'R5')
- self.assertEqual(self.next_hops['R7'], 'R5')
-
- def test_topology5(self):
- """
-
- +----+ +----+ +----+
- | R2 |------| R3 |------| R4 |
- +----+ +----+ +----+
- | | |
- | +====+ +----+ +----+
- +--------| R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
- +====+ +----+ +----+
-
- """
- collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3', 'R1']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
- 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5', 'R2']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 6)
- self.assertEqual(self.next_hops['R2'], 'R2')
- self.assertEqual(self.next_hops['R3'], 'R3')
- self.assertEqual(self.next_hops['R4'], 'R3')
- self.assertEqual(self.next_hops['R5'], 'R5')
- self.assertEqual(self.next_hops['R6'], 'R5')
- self.assertEqual(self.next_hops['R7'], 'R5')
-
- def test_topology5_with_asymmetry1(self):
- """
-
- +----+ +----+ +----+
- | R2 |------| R3 |------| R4 |
- +----+ +----+ +----+
- ^ | |
- ^ +====+ +----+ +----+
- +-<-<-<--| R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
- +====+ +----+ +----+
-
- """
- collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
- 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5', 'R2']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 6)
- self.assertEqual(self.next_hops['R2'], 'R2')
- self.assertEqual(self.next_hops['R3'], 'R3')
- self.assertEqual(self.next_hops['R4'], 'R3')
- self.assertEqual(self.next_hops['R5'], 'R5')
- self.assertEqual(self.next_hops['R6'], 'R5')
- self.assertEqual(self.next_hops['R7'], 'R5')
-
- def test_topology5_with_asymmetry2(self):
- """
-
- +----+ +----+ +----+
- | R2 |------| R3 |------| R4 |
- +----+ +----+ +----+
- v | |
- v +====+ +----+ +----+
- +->->->->| R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
- +====+ +----+ +----+
-
- """
- collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3', 'R1']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
- 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 6)
- self.assertEqual(self.next_hops['R2'], 'R3')
- self.assertEqual(self.next_hops['R3'], 'R3')
- self.assertEqual(self.next_hops['R4'], 'R3')
- self.assertEqual(self.next_hops['R5'], 'R5')
- self.assertEqual(self.next_hops['R6'], 'R5')
- self.assertEqual(self.next_hops['R7'], 'R5')
-
- def test_topology5_with_asymmetry3(self):
- """
-
- +----+ +----+ +----+
- | R2 |------| R3 |------| R4 |
- +----+ +----+ +----+
- v | |
- v +====+ +----+ +----+
- +->->->->| R1 |------| R5 |<-<-<-| R6 |------ R7 (no ls from R7)
- +====+ +----+ +----+
-
- """
- collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3', 'R1']),
- 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
- 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
- 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
- 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4']),
- 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
- self.engine.ls_collection_changed(collection)
- self.engine.tick(1.0)
- self.assertEqual(len(self.next_hops), 4)
- self.assertEqual(self.next_hops['R2'], 'R3')
- self.assertEqual(self.next_hops['R3'], 'R3')
- self.assertEqual(self.next_hops['R4'], 'R3')
- self.assertEqual(self.next_hops['R5'], 'R5')
+ def setUp(self):
+ self.id = 'R1'
+ self.area = 'area'
+ self.next_hops = None
+ self.valid_origins = None
+ self.engine = PathEngine(self)
+
+ def log(self, level, text):
+ pass
+
+ def next_hops_changed(self, nh):
+ self.next_hops = nh
+
+ def valid_origins_changed(self, vo):
+ self.valid_origins = vo
+
+ def test_topology1(self):
+ """
+
+ +====+ +----+ +----+
+ | R1 |------| R2 |------| R3 |
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R1': LinkState(None, 'R1', 'area', 1, ['R2']),
+ 'R2': LinkState(None, 'R2', 'area', 1, ['R1', 'R3']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R2']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 2)
+ self.assertEqual(self.next_hops['R2'], 'R2')
+ self.assertEqual(self.next_hops['R3'], 'R2')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.assertEqual(self.valid_origins['R2'], [])
+ self.assertEqual(self.valid_origins['R3'], [])
+
+ def test_topology2(self):
+ """
+
+ +====+ +----+ +----+
+ | R1 |------| R2 |------| R4 |
+ +====+ +----+ +----+
+ | |
+ +----+ +----+ +----+
+ | R3 |------| R5 |------| R6 |
+ +----+ +----+ +----+
+
+ """
+ collection = { 'R1': LinkState(None, 'R1', 'area', 1, ['R2']),
+ 'R2': LinkState(None, 'R2', 'area', 1, ['R1', 'R3', 'R4']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R2', 'R5']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R2', 'R5']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R3', 'R4', 'R6']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 5)
+ self.assertEqual(self.next_hops['R2'], 'R2')
+ self.assertEqual(self.next_hops['R3'], 'R2')
+ self.assertEqual(self.next_hops['R4'], 'R2')
+ self.assertEqual(self.next_hops['R5'], 'R2')
+ self.assertEqual(self.next_hops['R6'], 'R2')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.valid_origins['R6'].sort()
+ self.assertEqual(self.valid_origins['R2'], [])
+ self.assertEqual(self.valid_origins['R3'], [])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], [])
+ self.assertEqual(self.valid_origins['R6'], [])
+
+ def test_topology3(self):
+ """
+
+ +----+ +----+ +----+
+ | R2 |------| R3 |------| R4 |
+ +----+ +----+ +----+
+ | |
+ +====+ +----+ +----+
+ | R1 |------| R5 |------| R6 |
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
+ 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 5)
+ self.assertEqual(self.next_hops['R2'], 'R3')
+ self.assertEqual(self.next_hops['R3'], 'R3')
+ self.assertEqual(self.next_hops['R4'], 'R3')
+ self.assertEqual(self.next_hops['R5'], 'R5')
+ self.assertEqual(self.next_hops['R6'], 'R5')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.valid_origins['R6'].sort()
+ self.assertEqual(self.valid_origins['R2'], ['R5', 'R6'])
+ self.assertEqual(self.valid_origins['R3'], ['R5', 'R6'])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R6'], ['R2', 'R3'])
+
+ def test_topology4(self):
+ """
+
+ +----+ +----+ +----+
+ | R2 |------| R3 |------| R4 |
+ +----+ +----+ +----+
+ | |
+ +====+ +----+ +----+
+ | R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
+ 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 6)
+ self.assertEqual(self.next_hops['R2'], 'R3')
+ self.assertEqual(self.next_hops['R3'], 'R3')
+ self.assertEqual(self.next_hops['R4'], 'R3')
+ self.assertEqual(self.next_hops['R5'], 'R5')
+ self.assertEqual(self.next_hops['R6'], 'R5')
+ self.assertEqual(self.next_hops['R7'], 'R5')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.valid_origins['R6'].sort()
+ self.valid_origins['R7'].sort()
+ self.assertEqual(self.valid_origins['R2'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R3'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R6'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R7'], ['R2', 'R3'])
+
+ def test_topology5(self):
+ """
+
+ +----+ +----+ +----+
+ | R2 |------| R3 |------| R4 |
+ +----+ +----+ +----+
+ | | |
+ | +====+ +----+ +----+
+ +--------| R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3', 'R1']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
+ 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5', 'R2']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 6)
+ self.assertEqual(self.next_hops['R2'], 'R2')
+ self.assertEqual(self.next_hops['R3'], 'R3')
+ self.assertEqual(self.next_hops['R4'], 'R3')
+ self.assertEqual(self.next_hops['R5'], 'R5')
+ self.assertEqual(self.next_hops['R6'], 'R5')
+ self.assertEqual(self.next_hops['R7'], 'R5')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.valid_origins['R6'].sort()
+ self.valid_origins['R7'].sort()
+ self.assertEqual(self.valid_origins['R2'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R3'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R6'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R7'], ['R2', 'R3'])
+
+ def test_topology5_with_asymmetry1(self):
+ """
+
+ +----+ +----+ +----+
+ | R2 |------| R3 |------| R4 |
+ +----+ +----+ +----+
+ ^ | |
+ ^ +====+ +----+ +----+
+ +-<-<-<--| R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
+ 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5', 'R2']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 6)
+ self.assertEqual(self.next_hops['R2'], 'R2')
+ self.assertEqual(self.next_hops['R3'], 'R3')
+ self.assertEqual(self.next_hops['R4'], 'R3')
+ self.assertEqual(self.next_hops['R5'], 'R5')
+ self.assertEqual(self.next_hops['R6'], 'R5')
+ self.assertEqual(self.next_hops['R7'], 'R5')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.valid_origins['R6'].sort()
+ self.valid_origins['R7'].sort()
+ self.assertEqual(self.valid_origins['R2'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R3'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R6'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R7'], ['R2', 'R3'])
+
+ def test_topology5_with_asymmetry2(self):
+ """
+
+ +----+ +----+ +----+
+ | R2 |------| R3 |------| R4 |
+ +----+ +----+ +----+
+ v | |
+ v +====+ +----+ +----+
+ +->->->->| R1 |------| R5 |------| R6 |------ R7 (no ls from R7)
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3', 'R1']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
+ 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4', 'R6']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 6)
+ self.assertEqual(self.next_hops['R2'], 'R3')
+ self.assertEqual(self.next_hops['R3'], 'R3')
+ self.assertEqual(self.next_hops['R4'], 'R3')
+ self.assertEqual(self.next_hops['R5'], 'R5')
+ self.assertEqual(self.next_hops['R6'], 'R5')
+ self.assertEqual(self.next_hops['R7'], 'R5')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.valid_origins['R6'].sort()
+ self.valid_origins['R7'].sort()
+ self.assertEqual(self.valid_origins['R2'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R3'], ['R5', 'R6', 'R7'])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R6'], ['R2', 'R3'])
+ self.assertEqual(self.valid_origins['R7'], ['R2', 'R3'])
+
+ def test_topology5_with_asymmetry3(self):
+ """
+
+ +----+ +----+ +----+
+ | R2 |------| R3 |------| R4 |
+ +----+ +----+ +----+
+ v | |
+ v +====+ +----+ +----+
+ +->->->->| R1 |------| R5 |<-<-<-| R6 |------ R7 (no ls from R7)
+ +====+ +----+ +----+
+
+ """
+ collection = { 'R2': LinkState(None, 'R2', 'area', 1, ['R3', 'R1']),
+ 'R3': LinkState(None, 'R3', 'area', 1, ['R1', 'R2', 'R4']),
+ 'R4': LinkState(None, 'R4', 'area', 1, ['R3', 'R5']),
+ 'R1': LinkState(None, 'R1', 'area', 1, ['R3', 'R5']),
+ 'R5': LinkState(None, 'R5', 'area', 1, ['R1', 'R4']),
+ 'R6': LinkState(None, 'R6', 'area', 1, ['R5', 'R7']) }
+ self.engine.ls_collection_changed(collection)
+ self.engine.tick(1.0)
+ self.assertEqual(len(self.next_hops), 4)
+ self.assertEqual(self.next_hops['R2'], 'R3')
+ self.assertEqual(self.next_hops['R3'], 'R3')
+ self.assertEqual(self.next_hops['R4'], 'R3')
+ self.assertEqual(self.next_hops['R5'], 'R5')
+
+ self.valid_origins['R2'].sort()
+ self.valid_origins['R3'].sort()
+ self.valid_origins['R4'].sort()
+ self.valid_origins['R5'].sort()
+ self.assertEqual(self.valid_origins['R2'], ['R5'])
+ self.assertEqual(self.valid_origins['R3'], ['R5'])
+ self.assertEqual(self.valid_origins['R4'], [])
+ self.assertEqual(self.valid_origins['R5'], ['R2', 'R3'])
if __name__ == '__main__':
- unittest.main()
+ unittest.main()
diff --git a/qpid/extras/dispatch/tests/system_tests_one_router.py b/qpid/extras/dispatch/tests/system_tests_one_router.py
index b25568d283..1154b3ed37 100644
--- a/qpid/extras/dispatch/tests/system_tests_one_router.py
+++ b/qpid/extras/dispatch/tests/system_tests_one_router.py
@@ -29,7 +29,7 @@ class RouterTest(unittest.TestCase):
if 'CTEST_SOURCE_DIR' not in os.environ:
raise Exception("Environment variable 'CTEST_SOURCE_DIR' not set")
srcdir = os.environ['CTEST_SOURCE_DIR']
- self.router = subprocess.Popen(['../router/dispatch-router', '-c', '%s/onerouter.conf' % srcdir],
+ self.router = subprocess.Popen(['../router/dispatch-router', '-c', '%s/config-1/A.conf' % srcdir],
stderr=subprocess.PIPE, stdout=subprocess.PIPE)
time.sleep(1)
@@ -138,6 +138,65 @@ class RouterTest(unittest.TestCase):
M4.stop()
+ def test_2a_multicast_unsettled(self):
+ addr = "amqp://0.0.0.0:20000/pre_settled/multicast/1"
+ M1 = Messenger()
+ M2 = Messenger()
+ M3 = Messenger()
+ M4 = Messenger()
+
+ M1.timeout = 1.0
+ M2.timeout = 1.0
+ M3.timeout = 1.0
+ M4.timeout = 1.0
+
+ M1.outgoing_window = 5
+ M2.incoming_window = 5
+ M3.incoming_window = 5
+ M4.incoming_window = 5
+
+ M1.start()
+ M2.start()
+ M3.start()
+ M4.start()
+ self.subscribe(M2, addr)
+ self.subscribe(M3, addr)
+ self.subscribe(M4, addr)
+
+ tm = Message()
+ rm = Message()
+
+ tm.address = addr
+ for i in range(2):
+ tm.body = {'number': i}
+ M1.put(tm)
+ M1.send(0)
+
+ for i in range(2):
+ M2.recv(1)
+ trk = M2.get(rm)
+ M2.accept(trk)
+ M2.settle(trk)
+ self.assertEqual(i, rm.body['number'])
+
+ M3.recv(1)
+ trk = M3.get(rm)
+ M3.accept(trk)
+ M3.settle(trk)
+ self.assertEqual(i, rm.body['number'])
+
+ M4.recv(1)
+ trk = M4.get(rm)
+ M4.accept(trk)
+ M4.settle(trk)
+ self.assertEqual(i, rm.body['number'])
+
+ M1.stop()
+ M2.stop()
+ M3.stop()
+ M4.stop()
+
+
def test_3_propagated_disposition(self):
addr = "amqp://0.0.0.0:20000/unsettled/1"
M1 = Messenger()
@@ -310,8 +369,8 @@ class RouterTest(unittest.TestCase):
self.assertEqual(i, rm.body['number'])
da = rm.instructions
self.assertEqual(da.__class__, dict)
- self.assertEqual(da['qdx.ingress'], 'Qpid.Dispatch.Router.A')
- self.assertFalse('qdx.trace' in da)
+ self.assertEqual(da['qdx.ingress'], '_topo/area/Qpid.Dispatch.Router.A/')
+ self.assertEqual(da['qdx.trace'], ['_topo/area/Qpid.Dispatch.Router.A/'])
##
## Pre-existing ingress
@@ -329,7 +388,7 @@ class RouterTest(unittest.TestCase):
da = rm.instructions
self.assertEqual(da.__class__, dict)
self.assertEqual(da['qdx.ingress'], 'ingress-router')
- self.assertFalse('qdx.trace' in da)
+ self.assertEqual(da['qdx.trace'], ['_topo/area/Qpid.Dispatch.Router.A/'])
##
## Invalid trace type
@@ -346,8 +405,8 @@ class RouterTest(unittest.TestCase):
self.assertEqual(i, rm.body['number'])
da = rm.instructions
self.assertEqual(da.__class__, dict)
- self.assertEqual(da['qdx.ingress'], 'Qpid.Dispatch.Router.A')
- self.assertFalse('qdx.trace' in da)
+ self.assertEqual(da['qdx.ingress'], '_topo/area/Qpid.Dispatch.Router.A/')
+ self.assertEqual(da['qdx.trace'], ['_topo/area/Qpid.Dispatch.Router.A/'])
##
## Empty trace
@@ -364,8 +423,8 @@ class RouterTest(unittest.TestCase):
self.assertEqual(i, rm.body['number'])
da = rm.instructions
self.assertEqual(da.__class__, dict)
- self.assertEqual(da['qdx.ingress'], 'Qpid.Dispatch.Router.A')
- self.assertEqual(da['qdx.trace'], ['Qpid.Dispatch.Router.A'])
+ self.assertEqual(da['qdx.ingress'], '_topo/area/Qpid.Dispatch.Router.A/')
+ self.assertEqual(da['qdx.trace'], ['_topo/area/Qpid.Dispatch.Router.A/'])
##
## Non-empty trace
@@ -382,8 +441,8 @@ class RouterTest(unittest.TestCase):
self.assertEqual(i, rm.body['number'])
da = rm.instructions
self.assertEqual(da.__class__, dict)
- self.assertEqual(da['qdx.ingress'], 'Qpid.Dispatch.Router.A')
- self.assertEqual(da['qdx.trace'], ['first.hop', 'Qpid.Dispatch.Router.A'])
+ self.assertEqual(da['qdx.ingress'], '_topo/area/Qpid.Dispatch.Router.A/')
+ self.assertEqual(da['qdx.trace'], ['first.hop', '_topo/area/Qpid.Dispatch.Router.A/'])
M1.stop()
M2.stop()
diff --git a/qpid/extras/dispatch/tests/tool_test.c b/qpid/extras/dispatch/tests/tool_test.c
index ba047d928c..2c90714b3d 100644
--- a/qpid/extras/dispatch/tests/tool_test.c
+++ b/qpid/extras/dispatch/tests/tool_test.c
@@ -21,6 +21,8 @@
#include <stdio.h>
#include <string.h>
#include <qpid/dispatch/ctools.h>
+#include <qpid/dispatch/bitmask.h>
+#include "alloc_private.h"
typedef struct item_t {
DEQ_LINKS(struct item_t);
@@ -188,12 +190,43 @@ static char* test_deq_basic2(void *context)
}
+static char* test_bitmask(void *context)
+{
+ dx_bitmask_t *bm;
+ int num;
+
+ bm = dx_bitmask(0);
+ if (!bm) return "Can't allocate a bit mask";
+ if (dx_bitmask_first_set(bm, &num)) return "Expected no first set bit";
+
+ dx_bitmask_set_bit(bm, 3);
+ dx_bitmask_set_bit(bm, 500);
+
+ if (!dx_bitmask_first_set(bm, &num)) return "Expected first set bit";
+ if (num != 3) return "Expected first set bit to be 3";
+
+ dx_bitmask_clear_bit(bm, num);
+
+ if (!dx_bitmask_first_set(bm, &num)) return "Expected first set bit (2)";
+ if (num != 500) return "Expected first set bit to be 500";
+
+ dx_bitmask_clear_bit(bm, num);
+ if (dx_bitmask_first_set(bm, &num)) return "Expected no first set bit (2)";
+
+ dx_bitmask_free(bm);
+
+ return 0;
+}
+
+
int tool_tests(void)
{
int result = 0;
+ dx_alloc_initialize();
TEST_CASE(test_deq_basic, 0);
TEST_CASE(test_deq_basic2, 0);
+ TEST_CASE(test_bitmask, 0);
return result;
}
diff --git a/qpid/extras/dispatch/tools/src/py/qdstat b/qpid/extras/dispatch/tools/src/py/qdstat
new file mode 100755
index 0000000000..32594b4d4e
--- /dev/null
+++ b/qpid/extras/dispatch/tools/src/py/qdstat
@@ -0,0 +1,328 @@
+#!/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
+from optparse import OptionParser, OptionGroup
+import sys
+import locale
+import socket
+import re
+from proton import Messenger, Message
+
+home = os.environ.get("QD_TOOLS_HOME", os.path.normpath("/usr/share/qd-tools"))
+sys.path.append(os.path.join(home, "python"))
+
+from qdtoollibs import Display, Header, Sorter, YN, Commas, TimeLong
+
+
+class Config:
+ def __init__(self):
+ self._host = "0.0.0.0"
+ self._connTimeout = 10
+ self._types = ""
+ self._limit = 50
+ self._increasing = False
+ self._sortcol = None
+
+config = Config()
+conn_options = {}
+
+def OptionsAndArguments(argv):
+ """ Set global variables for options, return arguments """
+
+ global config
+ global conn_options
+
+ usage = \
+"""%prog -g [options]
+ %prog -c [options]
+ %prog -l [options]
+ %prog -n [options]
+ %prog -a [options]
+ %prog -m [options]"""
+
+ parser = OptionParser(usage=usage)
+
+ group1 = OptionGroup(parser, "General Options")
+ group1.add_option("-b", "--bus", action="store", type="string", default="0.0.0.0", metavar="<url>",
+ help="URL of the messaging bus to connect to")
+ group1.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>",
+ help="Maximum time to wait for connection (in seconds)")
+ group1.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>",
+ help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD5, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.")
+ group1.add_option("--ssl-certificate", action="store", type="string", metavar="<cert>", help="Client SSL certificate (PEM Format)")
+ group1.add_option("--ssl-key", action="store", type="string", metavar="<key>", help="Client SSL private key (PEM Format)")
+ parser.add_option_group(group1)
+
+ group2 = OptionGroup(parser, "Command Options")
+ group2.add_option("-g", "--general", help="Show General Broker Stats", action="store_const", const="g", dest="show")
+ group2.add_option("-c", "--connections", help="Show Connections", action="store_const", const="c", dest="show")
+ group2.add_option("-l", "--links", help="Show Router Links", action="store_const", const="l", dest="show")
+ group2.add_option("-n", "--nodes", help="Show Router Nodes", action="store_const", const="n", dest="show")
+ group2.add_option("-a", "--address", help="Show Router Addresses", action="store_const", const="a", dest="show")
+ group2.add_option("-m", "--memory", help="Show Broker Memory Stats", action="store_const", const="m", dest="show")
+ parser.add_option_group(group2)
+
+ opts, args = parser.parse_args(args=argv)
+
+ if not opts.show:
+ parser.error("You must specify one of these options: -g, -c, -l, -n, -a, or -m. For details, try $ qdstat --help")
+
+ config._types = opts.show
+ config._host = opts.bus
+ config._connTimeout = opts.timeout
+
+ return args
+
+
+class BusManager:
+ def __init__(self):
+ pass
+
+ def SetHost(self, host):
+ self.M = Messenger()
+ self.M.start()
+ self.M.route("amqp:/*", "amqp://%s/$1" % host)
+ self.address = "amqp:/_local/$management"
+ self.reply = "amqp:/temp.reply-address/0001" # FIX THIS!
+ self.M.subscribe(self.reply)
+
+ def Disconnect(self):
+ self.M.stop()
+
+ def displayConn(self):
+ pass
+
+ def _addr_class(self, addr):
+ if not addr:
+ return "-"
+ if addr[0] == 'M' : return "mobile"
+ if addr[0] == 'R' : return "router"
+ if addr[0] == 'A' : return "area"
+ if addr[0] == 'L' : return "local"
+ return "unknown: %s" % addr[0]
+
+ def _addr_text(self, addr):
+ if not addr:
+ return "-"
+ return addr[1:]
+
+ def displayRouterLinks(self):
+ disp = Display(prefix=" ")
+ heads = []
+ heads.append(Header("type"))
+ heads.append(Header("dir"))
+ heads.append(Header("rindex"))
+ heads.append(Header("class"))
+ heads.append(Header("addr"))
+ rows = []
+
+ request = Message()
+ response = Message()
+
+ request.address = self.address
+ request.reply_to = self.reply
+ request.correlation_id = 1
+ request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.router.link'}
+
+ self.M.put(request)
+ self.M.send()
+
+ self.M.recv()
+ self.M.get(response)
+
+ for link in response.body:
+ row = []
+ row.append(link['link-type'])
+ row.append(link['link-dir'])
+ if link['link-type'] == "router":
+ row.append(link['index'])
+ else:
+ row.append('-')
+ row.append(self._addr_class(link['owning-addr']))
+ row.append(self._addr_text(link['owning-addr']))
+ rows.append(row)
+ title = "Router Links"
+ dispRows = rows
+ disp.formattedTable(title, heads, dispRows)
+
+ def displayRouterNodes(self):
+ disp = Display(prefix=" ")
+ heads = []
+ heads.append(Header("class"))
+ heads.append(Header("address"))
+ heads.append(Header("rindex"))
+ heads.append(Header("next-hop"))
+ heads.append(Header("link"))
+ rows = []
+
+ request = Message()
+ response = Message()
+
+ request.address = self.address
+ request.reply_to = self.reply
+ request.correlation_id = 1
+ request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.router.node'}
+
+ self.M.put(request)
+ self.M.send()
+
+ self.M.recv()
+ self.M.get(response)
+
+ for node in response.body:
+ row = []
+ row.append(self._addr_class(node['addr']))
+ row.append(self._addr_text(node['addr']))
+ row.append(node['index'])
+ if node['next-hop'] != None:
+ row.append(node['next-hop'])
+ else:
+ row.append('-')
+ if node['router-link'] != None:
+ row.append(node['router-link'])
+ else:
+ row.append('-')
+ rows.append(row)
+ title = "Router Nodes"
+ dispRows = rows
+ disp.formattedTable(title, heads, dispRows)
+
+ def displayAddresses(self):
+ disp = Display(prefix=" ")
+ heads = []
+ heads.append(Header("class"))
+ heads.append(Header("address"))
+ heads.append(Header("in-proc", Header.Y))
+ heads.append(Header("local", Header.COMMAS))
+ heads.append(Header("remote", Header.COMMAS))
+ heads.append(Header("in", Header.COMMAS))
+ heads.append(Header("out", Header.COMMAS))
+ heads.append(Header("thru", Header.COMMAS))
+ heads.append(Header("to-proc", Header.COMMAS))
+ heads.append(Header("from-proc", Header.COMMAS))
+ rows = []
+
+ request = Message()
+ response = Message()
+
+ request.address = self.address
+ request.reply_to = self.reply
+ request.correlation_id = 1
+ request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.router.address'}
+
+ self.M.put(request)
+ self.M.send()
+
+ self.M.recv()
+ self.M.get(response)
+
+ for addr in response.body:
+ row = []
+ row.append(self._addr_class(addr['addr']))
+ row.append(self._addr_text(addr['addr']))
+ row.append(addr['in-process'])
+ row.append(addr['subscriber-count'])
+ row.append(addr['remote-count'])
+ row.append(addr['deliveries-ingress'])
+ row.append(addr['deliveries-egress'])
+ row.append(addr['deliveries-transit'])
+ row.append(addr['deliveries-to-container'])
+ row.append(addr['deliveries-from-container'])
+ rows.append(row)
+ title = "Router Addresses"
+ sorter = Sorter(heads, rows, 'address', 0, True)
+ dispRows = sorter.getSorted()
+ disp.formattedTable(title, heads, dispRows)
+
+ def displayMemory(self):
+ disp = Display(prefix=" ")
+ heads = []
+ heads.append(Header("type"))
+ heads.append(Header("size", Header.COMMAS))
+ heads.append(Header("batch"))
+ heads.append(Header("thread-max", Header.COMMAS))
+ heads.append(Header("total", Header.COMMAS))
+ heads.append(Header("in-threads", Header.COMMAS))
+ heads.append(Header("rebal-in", Header.COMMAS))
+ heads.append(Header("rebal-out", Header.COMMAS))
+ rows = []
+
+ request = Message()
+ response = Message()
+
+ request.address = self.address
+ request.reply_to = self.reply
+ request.correlation_id = 1
+ request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.allocator'}
+
+ self.M.put(request)
+ self.M.send()
+
+ self.M.recv()
+ self.M.get(response)
+
+ for t in response.body:
+ row = []
+ row.append(t['name'])
+ row.append(t['type_size'])
+ row.append(t['transfer_batch_size'])
+ row.append(t['local_free_list_max'])
+ row.append(t['total_alloc_from_heap'])
+ row.append(t['held_by_threads'])
+ row.append(t['batches_rebalanced_to_threads'])
+ row.append(t['batches_rebalanced_to_global'])
+ rows.append(row)
+ title = "Types"
+ sorter = Sorter(heads, rows, 'type', 0, True)
+ dispRows = sorter.getSorted()
+ disp.formattedTable(title, heads, dispRows)
+
+ def displayMain(self, names, main):
+ if main == 'l': self.displayRouterLinks()
+ elif main == 'n': self.displayRouterNodes()
+ elif main == 'a': self.displayAddresses()
+ elif main == 'm': self.displayMemory()
+
+ def display(self, names):
+ self.displayMain(names, config._types)
+
+
+def main(argv=None):
+
+ args = OptionsAndArguments(argv)
+ bm = BusManager()
+
+ try:
+ bm.SetHost(config._host)
+ bm.display(args)
+ bm.Disconnect()
+ return 0
+ except KeyboardInterrupt:
+ print
+ except Exception,e:
+ print "Failed: %s - %s" % (e.__class__.__name__, e)
+
+ bm.Disconnect() # try to deallocate brokers
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/qpid/extras/dispatch/tools/src/py/qdtoollibs/__init__.py b/qpid/extras/dispatch/tools/src/py/qdtoollibs/__init__.py
new file mode 100644
index 0000000000..378bf24ef1
--- /dev/null
+++ b/qpid/extras/dispatch/tools/src/py/qdtoollibs/__init__.py
@@ -0,0 +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 qdtoollibs.disp import *
+
diff --git a/qpid/extras/dispatch/tools/src/py/qdtoollibs/disp.py b/qpid/extras/dispatch/tools/src/py/qdtoollibs/disp.py
new file mode 100644
index 0000000000..529a727449
--- /dev/null
+++ b/qpid/extras/dispatch/tools/src/py/qdtoollibs/disp.py
@@ -0,0 +1,270 @@
+#!/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
+
+def YN(val):
+ if val:
+ return 'Y'
+ return 'N'
+
+def Commas(value):
+ sval = str(value)
+ result = ""
+ while True:
+ if len(sval) == 0:
+ return result
+ left = sval[:-3]
+ right = sval[-3:]
+ result = right + result
+ if len(left) > 0:
+ result = ',' + result
+ sval = left
+
+def TimeLong(value):
+ return strftime("%c", gmtime(value / 1000000000))
+
+def TimeShort(value):
+ return strftime("%X", gmtime(value / 1000000000))
+
+
+class Header:
+ """ """
+ NONE = 1
+ KMG = 2
+ YN = 3
+ Y = 4
+ TIME_LONG = 5
+ TIME_SHORT = 6
+ DURATION = 7
+ COMMAS = 8
+
+ 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 TimeLong(value)
+ if self.format == Header.TIME_SHORT:
+ return TimeShort(value)
+ 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
+ if self.format == Header.COMMAS:
+ return Commas(value)
+ 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 """
+
+ def __init__(self, spacing=2, prefix=" "):
+ self.tableSpacing = spacing
+ self.tablePrefix = prefix
+ self.timestampFormat = "%X"
+
+ 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:
+ diff = len(heads) - len(row)
+ for idx in range(diff):
+ row.append("")
+
+ print title
+ if len (rows) == 0:
+ return
+ colWidth = []
+ col = 0
+ line = self.tablePrefix
+ for head in heads:
+ width = len (head)
+ for row in rows:
+ text = row[col]
+ if text.__class__ == str:
+ text = text.decode('utf-8')
+ cellWidth = len(unicode(text))
+ if cellWidth > width:
+ width = cellWidth
+ colWidth.append (width + self.tableSpacing)
+ line = line + head
+ if col < len (heads) - 1:
+ 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:
+ text = row[col]
+ if text.__class__ == str:
+ text = text.decode('utf-8')
+ line = line + unicode(text)
+ if col < len (heads) - 1:
+ for i in range (width - len(unicode(text))):
+ 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))
+
+ 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
+
+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()
+ if not inc:
+ list.reverse()
+ 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