Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 3d73a89c1 -> cc2d0cb9b


DISPATCH-190 - Exposed the protocol family for the configuration of listeners 
and connectors.


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/de3bba38
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/de3bba38
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/de3bba38

Branch: refs/heads/master
Commit: de3bba38d7e372bc5bb4d86241a8f9795ab67c29
Parents: 3d73a89
Author: ganeshmurthy <[email protected]>
Authored: Wed Dec 2 13:29:49 2015 -0500
Committer: ganeshmurthy <[email protected]>
Committed: Fri Dec 4 09:55:48 2015 -0500

----------------------------------------------------------------------
 include/qpid/dispatch/driver.h                |  16 ++-
 include/qpid/dispatch/server.h                |   6 +
 python/qpid_dispatch/management/qdrouter.json |   8 +-
 src/connection_manager.c                      |  19 ++--
 src/posix/driver.c                            |  43 ++++++-
 src/server.c                                  |   4 +-
 tests/CMakeLists.txt                          |  23 ++--
 tests/system_test.py                          |  87 ++++++++++++---
 tests/system_tests_protocol_family.py         | 124 +++++++++++++++++++++
 9 files changed, 286 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/include/qpid/dispatch/driver.h
----------------------------------------------------------------------
diff --git a/include/qpid/dispatch/driver.h b/include/qpid/dispatch/driver.h
index c286c44..05e8b18 100644
--- a/include/qpid/dispatch/driver.h
+++ b/include/qpid/dispatch/driver.h
@@ -142,12 +142,16 @@ void qdpn_driver_free(qdpn_driver_t *driver);
  * @param[in] driver driver that will 'own' this listener
  * @param[in] host local host address to listen on
  * @param[in] port local port to listen on
+ * @param[in] protocol family to use (IPv4 or IPv6 or 0). If 0 (zero) is 
passed in the protocol family will be automatically determined from the address
  * @param[in] context application-supplied, can be accessed via
  *                    qdpn_listener_context()
  * @return a new listener on the given host:port, NULL if error
  */
-qdpn_listener_t *qdpn_listener(qdpn_driver_t *driver, const char *host,
-                               const char *port, void* context);
+qdpn_listener_t *qdpn_listener(qdpn_driver_t *driver,
+                               const char *host,
+                               const char *port,
+                               const char *protocol_family,
+                               void* context);
 
 /** Access the head listener for a driver.
  *
@@ -213,12 +217,16 @@ void qdpn_listener_free(qdpn_listener_t *listener);
  * @param[in] driver owner of this connection.
  * @param[in] host remote host to connect to.
  * @param[in] port remote port to connect to.
+ * @param[in] protocol family to use (IPv4 or IPv6 or 0). If 0 (zero) is 
passed in the protocol family will be automatically determined from the address
  * @param[in] context application supplied, can be accessed via
  *                    qdpn_connector_context() @return a new connector
  *                    to the given remote, or NULL on error.
  */
-qdpn_connector_t *qdpn_connector(qdpn_driver_t *driver, const char *host,
-                                 const char *port, void* context);
+qdpn_connector_t *qdpn_connector(qdpn_driver_t *driver,
+                                 const char *host,
+                                 const char *port,
+                                 const char *protocol_family,
+                                 void* context);
 
 /** Access the head connector for a driver.
  *

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/include/qpid/dispatch/server.h
----------------------------------------------------------------------
diff --git a/include/qpid/dispatch/server.h b/include/qpid/dispatch/server.h
index 2fb237d..4f39fc5 100644
--- a/include/qpid/dispatch/server.h
+++ b/include/qpid/dispatch/server.h
@@ -248,6 +248,12 @@ typedef struct qd_server_config_t {
     char *port;
 
     /**
+     * Protocol family that the socket will use when binding listener or 
connector.
+     * Possible values are IPv4 or IPv6. If not specified, the protocol family 
will be automatically determined from the address
+     */
+    char *protocol_family;
+
+    /**
      * Space-separated list of SASL mechanisms to be accepted for the 
connection.
      */
     char *sasl_mechanisms;

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/python/qpid_dispatch/management/qdrouter.json
----------------------------------------------------------------------
diff --git a/python/qpid_dispatch/management/qdrouter.json 
b/python/qpid_dispatch/management/qdrouter.json
index a3ee6d0..50b6640 100644
--- a/python/qpid_dispatch/management/qdrouter.json
+++ b/python/qpid_dispatch/management/qdrouter.json
@@ -20,6 +20,12 @@
                     "default": "amqp",
                     "create": true
 
+                },
+                "protocolFamily": {
+                    "type": ["IPv4", "IPv6"],
+                    "required": false,
+                    "description": "['IPv4', 'IPv6'] IPv4: Internet Protocol 
version 4; IPv6: Internet Protocol version 6.  If not specified, the protocol 
family will be automatically determined from the address.",
+                    "create": true
                 }
             }
         },
@@ -633,7 +639,7 @@
                     "default": "both",
                     "description": "['in', 'out', 'both', 'no'] in: Strip the 
dispatch router specific annotations only on ingress; out: Strip the dispatch 
router specific annotations only on egress; both: Strip the dispatch router 
specific annotations on both ingress and egress; no - do not strip dispatch 
router specific annotations",
                     "create": true
-                }
+                } 
             }
         },
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/src/connection_manager.c
----------------------------------------------------------------------
diff --git a/src/connection_manager.c b/src/connection_manager.c
index c90db75..3b597da 100644
--- a/src/connection_manager.c
+++ b/src/connection_manager.c
@@ -62,7 +62,6 @@ struct qd_connection_manager_t {
     qd_config_connector_list_t   on_demand_connectors;
 };
 
-
 // True if entity has any of attributes.
 static bool has_attrs(qd_entity_t *entity, const char **attributes, int n) {
     for (int i = 0; i < n; ++i)
@@ -73,6 +72,7 @@ static bool has_attrs(qd_entity_t *entity, const char 
**attributes, int n) {
 static const char *ssl_attributes[] = {
   "certDb", "certFile", "keyFile", "passwordFile", "password"
 };
+
 static const int ssl_attributes_count = 
sizeof(ssl_attributes)/sizeof(ssl_attributes[0]);
 
 static void qd_server_config_free(qd_server_config_t *cf)
@@ -139,15 +139,16 @@ static qd_error_t load_server_config(qd_dispatch_t *qd, 
qd_server_config_t *conf
     bool depAllowUnsecured  = qd_entity_opt_bool(entity, "allowUnsecured", 
!requireSsl); CHECK();
 
     memset(config, 0, sizeof(*config));
-    config->host            = qd_entity_get_string(entity, "addr"); CHECK();
-    config->port            = qd_entity_get_string(entity, "port"); CHECK();
-    config->role            = qd_entity_get_string(entity, "role"); CHECK();
-    config->max_frame_size  = qd_entity_get_long(entity, "maxFrameSize"); 
CHECK();
+    config->host                 = qd_entity_get_string(entity, "addr"); 
CHECK();
+    config->port                 = qd_entity_get_string(entity, "port"); 
CHECK();
+    config->role                 = qd_entity_get_string(entity, "role"); 
CHECK();
+    config->protocol_family      = qd_entity_opt_string(entity, 
"protocolFamily", 0); CHECK();
+    config->max_frame_size       = qd_entity_get_long(entity, "maxFrameSize"); 
CHECK();
     config->idle_timeout_seconds = qd_entity_get_long(entity, 
"idleTimeoutSeconds"); CHECK();
-    config->sasl_username = qd_entity_opt_string(entity, "saslUsername", 0); 
CHECK();
-    config->sasl_password = qd_entity_opt_string(entity, "saslPassword", 0); 
CHECK();
-    config->sasl_mechanisms = qd_entity_opt_string(entity, "saslMechanisms", 
0); CHECK();
-    config->ssl_enabled = has_attrs(entity, ssl_attributes, 
ssl_attributes_count);
+    config->sasl_username        = qd_entity_opt_string(entity, 
"saslUsername", 0); CHECK();
+    config->sasl_password        = qd_entity_opt_string(entity, 
"saslPassword", 0); CHECK();
+    config->sasl_mechanisms      = qd_entity_opt_string(entity, 
"saslMechanisms", 0); CHECK();
+    config->ssl_enabled          = has_attrs(entity, ssl_attributes, 
ssl_attributes_count);
 
     //
     // For now we are hardwiring this attribute to true.  If there's an outcry 
from the

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/src/posix/driver.c
----------------------------------------------------------------------
diff --git a/src/posix/driver.c b/src/posix/driver.c
index 5eb7c55..2fa4e45 100644
--- a/src/posix/driver.c
+++ b/src/posix/driver.c
@@ -58,6 +58,12 @@
 DEQ_DECLARE(qdpn_listener_t, qdpn_listener_list_t);
 DEQ_DECLARE(qdpn_connector_t, qdpn_connector_list_t);
 
+const char *protocol_family_ipv4 = "IPv4";
+const char *protocol_family_ipv6 = "IPv6";
+
+const char *AF_INET6_STR = "AF_INET6";
+const char *AF_INET_STR = "AF_INET";
+
 struct qdpn_driver_t {
     qd_log_source_t *log;
     pn_trace_t       trace;
@@ -232,8 +238,26 @@ static void qdpn_configure_sock(qdpn_driver_t *driver, int 
sock)
 }
 
 
-qdpn_listener_t *qdpn_listener(qdpn_driver_t *driver, const char *host,
-                               const char *port, void* context)
+/**
+ * Sets the ai_family field on the addrinfo struct based on the passed in 
NON-NULL protocol_family.
+ * If the passed in protocol family does not match IPv6, IPv4, the function 
does not set the ai_family field
+ */
+static void qd_set_addr_ai_family(qdpn_driver_t *driver, struct addrinfo 
*addr, const char* protocol_family)
+{
+    if (protocol_family) {
+        if(strcmp(protocol_family, protocol_family_ipv6) == 0)
+            addr->ai_family = AF_INET6;
+        else if(strcmp(protocol_family, protocol_family_ipv4) == 0)
+            addr->ai_family = AF_INET;
+    }
+}
+
+
+qdpn_listener_t *qdpn_listener(qdpn_driver_t *driver,
+                               const char *host,
+                               const char *port,
+                               const char *protocol_family,
+                               void* context)
 {
     if (!driver) return NULL;
 
@@ -244,6 +268,10 @@ qdpn_listener_t *qdpn_listener(qdpn_driver_t *driver, 
const char *host,
         return 0;
     }
 
+    // Set the protocol family before creating the socket.
+    qd_set_addr_ai_family(driver, addr, protocol_family);
+    qd_log(driver->log, QD_LOG_TRACE, "Set protocol family of port %s to: %s", 
port, protocol_family);
+
     int sock = qdpn_create_socket(addr->ai_family);
     if (sock < 0) {
         qdpn_log_errno(driver, "pn_create_socket");
@@ -428,8 +456,11 @@ static void qdpn_driver_remove_connector(qdpn_driver_t *d, 
qdpn_connector_t *c)
     sys_mutex_unlock(d->lock);
 }
 
-qdpn_connector_t *qdpn_connector(qdpn_driver_t *driver, const char *host,
-                                 const char *port, void *context)
+qdpn_connector_t *qdpn_connector(qdpn_driver_t *driver,
+                                 const char *host,
+                                 const char *port,
+                                 const char *protocol_family,
+                                 void *context)
 {
     if (!driver) return NULL;
 
@@ -440,6 +471,10 @@ qdpn_connector_t *qdpn_connector(qdpn_driver_t *driver, 
const char *host,
         return 0;
     }
 
+    // Set the protocol family before creating the socket.
+    qd_set_addr_ai_family(driver, addr, protocol_family);
+    qd_log(driver->log, QD_LOG_TRACE, "Set protocol family of port %s to: %s", 
port, protocol_family);
+
     int sock = qdpn_create_socket(addr->ai_family);
     if (sock == PN_INVALID_SOCKET) {
         freeaddrinfo(addr);

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/src/server.c
----------------------------------------------------------------------
diff --git a/src/server.c b/src/server.c
index c15e871..8ff30b1 100644
--- a/src/server.c
+++ b/src/server.c
@@ -826,7 +826,7 @@ static void cxtr_try_open(void *context)
     sys_mutex_lock(ct->server->lock);
     // Increment the connection id so the next connection can use it
     ctx->connection_id = ct->server->next_connection_id++;
-    ctx->pn_cxtr = qdpn_connector(ct->server->driver, ct->config->host, 
ct->config->port, (void*) ctx);
+    ctx->pn_cxtr = qdpn_connector(ct->server->driver, ct->config->host, 
ct->config->port, ct->config->protocol_family, (void*) ctx);
     if (ctx->pn_cxtr) {
         DEQ_INSERT_TAIL(ct->server->connections, ctx);
         qd_entity_cache_add(QD_CONNECTION_TYPE, ctx);
@@ -1282,7 +1282,7 @@ qd_listener_t *qd_server_listen(qd_dispatch_t *qd, const 
qd_server_config_t *con
     li->server      = qd_server;
     li->config      = config;
     li->context     = context;
-    li->pn_listener = qdpn_listener(qd_server->driver, config->host, 
config->port, (void*) li);
+    li->pn_listener = qdpn_listener(qd_server->driver, config->host, 
config->port, config->protocol_family, (void*) li);
 
     if (!li->pn_listener) {
         free_qd_listener_t(li);

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index c02c4fa..825f84e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -60,6 +60,7 @@ file(COPY 
${CMAKE_CURRENT_SOURCE_DIR}/system_tests_one_router.py DESTINATION ${C
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/system_tests_qdmanage.py DESTINATION 
${CMAKE_CURRENT_BINARY_DIR})
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/system_tests_qdstat.py DESTINATION 
${CMAKE_CURRENT_BINARY_DIR})
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/system_tests_two_routers.py DESTINATION 
${CMAKE_CURRENT_BINARY_DIR})
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/system_tests_protocol_family.py 
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/system_tests_link_routes.py DESTINATION 
${CMAKE_CURRENT_BINARY_DIR})
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/run_system_tests.py DESTINATION 
${CMAKE_CURRENT_BINARY_DIR})
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/system_test.py DESTINATION 
${CMAKE_CURRENT_BINARY_DIR})
@@ -78,16 +79,17 @@ add_test(unit_tests_size_1     ${TEST_WRAP} --vg 
unit_tests_size 1)
 add_test(unit_tests            ${TEST_WRAP} --vg unit_tests 
${CMAKE_CURRENT_SOURCE_DIR}/threads4.conf)
 
 # Add all sytem_tests* using add_test
-add_test(router_tests             ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/router_engine_test.py -v)
-add_test(system_tests_broker      ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_broker.py -v)
-add_test(system_tests_management  ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_management.py -v)
-add_test(system_tests_one_router  ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_one_router.py -v)
-add_test(system_tests_qdmanage    ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_qdmanage.py -v)
-add_test(system_tests_qdstat      ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_qdstat.py -v)
-add_test(system_tests_two_routers ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_two_routers.py -v)
-add_test(system_tests_link_routes ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_link_routes.py -v)
-add_test(system_tests_sasl_plain  ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_sasl_plain.py -v)
-add_test(management_tests         ${TEST_WRAP} -m unittest -v management)
+add_test(router_tests                 ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/router_engine_test.py -v)
+add_test(system_tests_broker          ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_broker.py -v)
+add_test(system_tests_management      ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_management.py -v)
+add_test(system_tests_one_router      ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_one_router.py -v)
+add_test(system_tests_qdmanage        ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_qdmanage.py -v)
+add_test(system_tests_qdstat          ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_qdstat.py -v)
+add_test(system_tests_two_routers     ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_two_routers.py -v)
+add_test(system_tests_protocol_family ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_protocol_family.py -v)
+add_test(system_tests_link_routes     ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_link_routes.py -v)
+add_test(system_tests_sasl_plain      ${TEST_WRAP} -s 
${CMAKE_CURRENT_BINARY_DIR}/system_tests_sasl_plain.py -v)
+add_test(management_tests             ${TEST_WRAP} -m unittest -v management)
 
 #macro(add_system_test test)
 #  add_test(${test} ${TEST_WRAP} -m ${test} -v)
@@ -102,6 +104,7 @@ set(SYSTEM_TEST_FILES
   ${CMAKE_CURRENT_BINARY_DIR}/system_test.py
   ${CMAKE_CURRENT_BINARY_DIR}/system_tests_one_router.py
   ${CMAKE_CURRENT_BINARY_DIR}/system_tests_two_routers.py
+  ${CMAKE_CURRENT_BINARY_DIR}/system_tests_protocol_family.py
   ${CMAKE_CURRENT_BINARY_DIR}/system_tests_broker.py
   ${CMAKE_CURRENT_BINARY_DIR}/system_tests_management.py
   ${CMAKE_CURRENT_BINARY_DIR}/system_tests_qdstat.py

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/tests/system_test.py
----------------------------------------------------------------------
diff --git a/tests/system_test.py b/tests/system_test.py
index 60f54c6..964bf88 100644
--- a/tests/system_test.py
+++ b/tests/system_test.py
@@ -57,6 +57,7 @@ from copy import copy
 import proton
 from proton import Message
 from qpid_dispatch.management.client import Node
+
 try:
     # NOTE: the tests can be run outside a build to test an installed dispatch.
     # In this case we won't have access to the run.py module so no valgrind.
@@ -159,9 +160,20 @@ def retry_exception(function, timeout=TIMEOUT, delay=.001, 
max_delay=1, exceptio
             if delay is None:
                 raise
 
-def port_available(port, host='127.0.0.1'):
+def get_local_host_socket(protocol_family='IPv4'):
+    if protocol_family == 'IPv4':
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        host = '127.0.0.1'
+    elif protocol_family == 'IPv6':
+        s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+        host = '::1'
+
+    return s, host
+
+def port_available(port, protocol_family='IPv4'):
     """Return true if connecting to host:port gives 'connection refused'."""
-    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s, host = get_local_host_socket(protocol_family)
+
     try:
         s.connect((host, port))
         s.close()
@@ -171,14 +183,14 @@ def port_available(port, host='127.0.0.1'):
         pass
     return False
 
-def wait_port(port, host='127.0.0.1', **retry_kwargs):
+def wait_port(port, protocol_family='IPv4', **retry_kwargs):
     """Wait up to timeout for port (on host) to be connectable.
     Takes same keyword arguments as retry to control the timeout"""
     def check(e):
         """Only retry on connection refused"""
         if not isinstance(e, socket.error) or not e.errno == 111:
             raise
-    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s, host = get_local_host_socket(protocol_family)
     try:
         retry_exception(lambda: s.connect((host, port)), exception_test=check,
                         **retry_kwargs)
@@ -187,11 +199,11 @@ def wait_port(port, host='127.0.0.1', **retry_kwargs):
 
     finally: s.close()
 
-def wait_ports(ports, host="127.0.0.1", **retry_kwargs):
+def wait_ports(ports, **retry_kwargs):
     """Wait up to timeout for all ports (on host) to be connectable.
     Takes same keyword arguments as retry to control the timeout"""
-    for p in ports:
-        wait_port(p, host=host, **retry_kwargs)
+    for port, protocol_family in ports.iteritems():
+        wait_port(port=port, protocol_family=protocol_family, **retry_kwargs)
 
 def message(**properties):
     """Convenience to create a proton.Message with properties set"""
@@ -372,6 +384,22 @@ class Qdrouterd(Process):
         super(Qdrouterd, self).teardown()
 
     @property
+    def ports_family(self):
+        """
+        Return a dict of listener ports and the respective port family
+        Example -
+        { 23456: 'IPv4', 243455: 'IPv6' }
+        """
+        ports_fam = {}
+        for l in self.config.sections('listener'):
+            if l.get('protocolFamily'):
+                ports_fam[l['port']] = l['protocolFamily']
+            else:
+                ports_fam[l['port']] = 'IPv4'
+
+        return ports_fam
+
+    @property
     def ports(self):
         """Return list of configured ports for all listeners"""
         return [l['port'] for l in self.config.sections('listener')]
@@ -379,12 +407,34 @@ class Qdrouterd(Process):
     @property
     def addresses(self):
         """Return amqp://host:port addresses for all listeners"""
-        return ["amqp://%s:%s"%(l['addr'], l['port']) for l in 
self.config.sections('listener')]
+        address_list = []
+        for l in self.config.sections('listener'):
+            protocol_family = l.get('protocolFamily')
+            if protocol_family == 'IPv6':
+                address_list.append("amqp://[%s]:%s"%(l['addr'], l['port']))
+            elif protocol_family == 'IPv4':
+                address_list.append("amqp://%s:%s"%(l['addr'], l['port']))
+            else:
+                # Default to IPv4
+                address_list.append("amqp://%s:%s"%(l['addr'], l['port']))
+
+        return address_list
 
     @property
     def hostports(self):
         """Return host:port for all listeners"""
-        return ["%s:%s"%(l['addr'], l['port']) for l in 
self.config.sections('listener')]
+        address_list = []
+        for l in self.config.sections('listener'):
+            protocol_family = l.get('protocolFamily')
+            if protocol_family == 'IPv6':
+                address_list.append("[%s]:%s"%(l['addr'], l['port']))
+            elif protocol_family == 'IPv4':
+                address_list.append("%s:%s"%(l['addr'], l['port']))
+            else:
+                # Default to IPv4
+                address_list.append("%s:%s"%(l['addr'], l['port']))
+
+        return address_list
 
     def is_connected(self, port, host='127.0.0.1'):
         """If router has a connection to host:port return the management info.
@@ -412,19 +462,27 @@ class Qdrouterd(Process):
             return addrs and addrs[0]['subscriberCount'] >= subscribers and 
addrs[0]['remoteCount'] >= remotes
         assert retry(check, **retry_kwargs)
 
+    def get_host(self, protocol_family):
+        if protocol_family == 'IPv4':
+            return '127.0.0.1'
+        elif protocol_family == 'IPv6':
+            return '::1'
+        else:
+            return '127.0.0.1'
+
     def wait_connectors(self, **retry_kwargs):
         """
         Wait for all connectors to be connected
         @param retry_kwargs: keyword args for L{retry}
         """
         for c in self.config.sections('connector'):
-            assert retry(lambda: self.is_connected(c['port']), 
**retry_kwargs), "Port not connected %s" % c['port']
+            assert retry(lambda: self.is_connected(port=c['port'], 
host=self.get_host(c.get('protocolFamily'))), **retry_kwargs), "Port not 
connected %s" % c['port']
 
     def wait_ready(self, **retry_kwargs):
         """Wait for ports and connectors to be ready"""
         if not self._wait_ready:
             self._wait_ready = True
-            wait_ports(self.ports, **retry_kwargs)
+            wait_ports(self.ports_family, **retry_kwargs)
             self.wait_connectors(**retry_kwargs)
         return self
 
@@ -466,6 +524,7 @@ class Qpidd(Process):
             name=name, expect=Process.RUNNING)
         self.port = self.config['port'] or 5672
         self.address = "127.0.0.1:%s"%self.port
+
         self._management = None
         if wait:
             self.wait_ready()
@@ -587,7 +646,7 @@ class Tester(object):
     next_port = random.randint(port_range[0], port_range[1])
 
     @classmethod
-    def get_port(cls):
+    def get_port(cls, protocol_family='IPv4'):
         """Get an unused port"""
         def advance():
             """Advance with wrap-around"""
@@ -595,10 +654,10 @@ class Tester(object):
             if cls.next_port >= cls.port_range[1]:
                 cls.next_port = cls.port_range[0]
         start = cls.next_port
-        while not port_available(cls.next_port):
+        while not port_available(cls.next_port, protocol_family):
             advance()
             if cls.next_port == start:
-                raise Exception("No avaliable ports in range %s", 
cls.port_range)
+                raise Exception("No available ports in range %s", 
cls.port_range)
         p = cls.next_port
         advance()
         return p

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/de3bba38/tests/system_tests_protocol_family.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_protocol_family.py 
b/tests/system_tests_protocol_family.py
new file mode 100644
index 0000000..0a3182b
--- /dev/null
+++ b/tests/system_tests_protocol_family.py
@@ -0,0 +1,124 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import unittest
+from time import sleep
+from proton import Message
+from system_test import TestCase, Qdrouterd, main_module
+
+try:
+    from proton import MODIFIED
+except ImportError:
+    from proton import PN_STATUS_MODIFIED as MODIFIED
+
+
+class ProtocolFamilyTest(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        """
+        Starts three routers with various listeners and connectors.
+        There is a call to wait_router_connected to make sure that the routers 
are able to communicate with each
+        other on ports using the assigned protocol family.
+        """
+        super(ProtocolFamilyTest, cls).setUpClass()
+
+        def router(name, connection):
+
+            config = [
+                ('container', {'workerThreads': 4, 'containerName': 
'Qpid.Dispatch.Router.%s'%name}),
+                ('router', {'mode': 'interior', 'routerId': 'QDR.%s'%name}),
+
+                # No protocolFamily is specified for this listener.
+                # This will test if the router defaults addr to 127.0.0.1 and 
if the router auto-detects protocol family
+
+                ('listener', {'port': cls.tester.get_port()}),
+
+                # Specify addr as 127.0.0.1 and protocol family as IPv4
+                ('listener', {'addr': '127.0.0.1', 'protocolFamily': 
'IPv4','port': cls.tester.get_port()}),
+
+                # Specify protocol family as IPv4 but don't specify any addr
+                # This will test if the router defaults the addr field to 
127.0.0.1
+                ('listener', {'protocolFamily': 'IPv4', 'port': 
cls.tester.get_port()}),
+
+                # Specify the addr as 127.0.0.1
+                # This will test router's auto-detection of protocol family
+                ('listener', {'addr': '127.0.0.1', 'port': 
cls.tester.get_port()}),
+
+
+                # Specify addr as ::1 and protocol family as IPv6
+                ('listener', {'addr': '::1', 'protocolFamily': 'IPv6', 'port': 
cls.tester.get_port(protocol_family='IPv6')}),
+
+                ('fixedAddress', {'prefix': '/closest/', 'fanout': 'single', 
'bias': 'closest'}),
+                ('fixedAddress', {'prefix': '/spread/', 'fanout': 'single', 
'bias': 'spread'}),
+                ('fixedAddress', {'prefix': '/multicast/', 'fanout': 
'multiple'}),
+                ('fixedAddress', {'prefix': '/', 'fanout': 'multiple'}),
+
+            ] + connection
+
+            config = Qdrouterd.Config(config)
+
+            # The wait=True attempts to connect to each listening port with 
the appropriate protocol family
+            # and tests each connector
+            cls.routers.append(cls.tester.qdrouterd(name, config, wait=True))
+
+        cls.routers = []
+
+        inter_router_port = cls.tester.get_port(protocol_family='IPv6')
+        inter_router_ipv4_port = cls.tester.get_port(protocol_family='IPv4')
+
+        router('A',
+               [
+                   ('listener', {'addr': '::1', 'role': 'inter-router', 
'protocolFamily': 'IPv6', 'port': inter_router_port})
+               ]
+        )
+
+        router('B',
+               [
+                   # Tests an IPv6 connector
+                   ('connector', {'addr': '::1', 'role': 'inter-router', 
'protocolFamily': 'IPv6', 'port': inter_router_port}),
+                   ('listener', {'addr': '127.0.0.1', 'role': 'inter-router', 
'port': inter_router_ipv4_port})
+                ]
+
+        )
+
+        router('C',
+               [
+                   # Tests an IPv4 connector
+                   ('connector', {'addr': '127.0.0.1', 'role': 'inter-router', 
'port': inter_router_ipv4_port})
+               ]
+        )
+
+        cls.routers[0].wait_router_connected('QDR.B')
+        cls.routers[1].wait_router_connected('QDR.A')
+        cls.routers[2].wait_router_connected('QDR.B')
+
+    # Without at least one test the setUpClass does not execute
+    def test_00_discard(self):
+        addr = self.routers[0].addresses[4]+"/test/1"
+        print 'addr', addr
+        M1 = self.messenger()
+        tm = Message()
+        tm.address = addr
+        for i in range(100):
+            tm.body = {'number': i}
+            M1.put(tm)
+        M1.send()
+
+if __name__ == '__main__':
+    unittest.main(main_module())
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to