qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From acon...@apache.org
Subject svn commit: r1643319 - in /qpid/dispatch/trunk: doc/man/ python/qpid_dispatch/management/ python/qpid_dispatch_internal/management/ tests/ tests/management/ tools/
Date Fri, 05 Dec 2014 15:12:53 GMT
Author: aconway
Date: Fri Dec  5 15:12:53 2014
New Revision: 1643319

URL: http://svn.apache.org/r1643319
Log:
DISPATCH-80: Management schema: implement annotations and entity inheritance as per AMQP management WD 09

Renamed "includes" as "annotations" - The AMQP management 09 draft introduces annotations
which serve exactly the same function as our includes, so renamed according to the standard.

Introduced entity inheritance and created the following hierarchy among entity types:

"entity": the base of all entity types.
- "configurationEntity": base for all types that hold configuration information.
- "operationalEntity": base for all types that hold operational information.

"Configuration" information is supplied in advance and expresse intent. For
example "I want the router to listen on port N", or, "I want the router to
connect to this address" All the entities that can be used in the configuration
file are "configurationEntity".

"Operational" information reflects the actual state of the router at a given
moment in time.  For example, "how many addresses are presently active on the
router?" All the entities queried by qdstat are "operationalEntity".

The two types are often related, for example "I want an on-demand connection to
this address" and "is there presently a live outgoing connection to this
address?"  As a matter of good practice we separate the two kinds of
entities. For example "listener" and "connector" are configuration entities
expressing the intent to make or receive connectoins. The "connection" entity
holds live operational information about an actual connection.

- Fixed man pages and configuration to use new hierarchy.
- Fixed use of "connection" as both annotation and entity type name.
- Implemented GET-ANNOTATIONS and GET-TYPES to return annotations and bases.

Added:
    qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.txt
Modified:
    qpid/dispatch/trunk/doc/man/qdrouterd_conf_man.py
    qpid/dispatch/trunk/python/qpid_dispatch/management/client.py
    qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json
    qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py
    qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py
    qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.py
    qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py
    qpid/dispatch/trunk/tests/management/schema.py
    qpid/dispatch/trunk/tests/system_tests_management.py
    qpid/dispatch/trunk/tools/qdmanage

Modified: qpid/dispatch/trunk/doc/man/qdrouterd_conf_man.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/doc/man/qdrouterd_conf_man.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/doc/man/qdrouterd_conf_man.py (original)
+++ qpid/dispatch/trunk/doc/man/qdrouterd_conf_man.py Fri Dec  5 15:12:53 2014
@@ -71,10 +71,10 @@ management name and identity of <section
 "listener-2". You can provide explicit name and identity attributes in any
 section.
 
-Include sections define a group of attribute values that can be included in
+Annotation sections define a group of attribute values that can be included in
 one or more entity sections.
 
-For example you can define an "ssl-profile" include section with SSL credentials
+For example you can define an "ssl-profile" annotation section with SSL credentials
 that can be included in multiple "listener" entities. Here's an example, note
 how the 'ssl-profile' attribute of 'listener' sections references the 'name'
 attribute of 'ssl-profile' sections.
@@ -98,8 +98,8 @@ listener {
         schema = QdSchema()
 
         def write_attribute(attr, attrs):
-            if attr.include and attr.include != attrs:
-                return          # Don't repeat included attributes
+            if attr.annotation and attr.annotation != attrs:
+                return          # Don't repeat annotationd attributes
             if attr.value is not None:
                 return          # Don't show fixed-value attributes, they can't be set in conf file.
 
@@ -127,19 +127,22 @@ listener {
                 write_attribute(attr, attrs)
             f.write('\n\n')
 
-        f.write(".SH INCLUDE SECTIONS\n\n")
-        for include in schema.includes.itervalues():
-            used_by = [e.name for e in schema.entity_types.itervalues() if include.name in e.include]
-            f.write('.SS "%s"\n'%include.name)
-            write_attributes(include)
-            f.write('.IP "Included by %s."\n'%(', '.join(used_by)))
+        f.write(".SH ANNOTATION SECTIONS\n\n")
+        for annotation in schema.annotations.itervalues():
+            used_by = [e.name for e in schema.entity_types.itervalues()
+                       if annotation in e.annotations]
+            f.write('.SS "%s"\n'%annotation.short_name)
+            write_attributes(annotation)
+            f.write('.IP "Annotationd by %s."\n'%(', '.join(used_by)))
 
         f.write(".SH ENTITY SECTIONS\n\n")
+        config = schema.entity_type("configurationEntity")
         for entity_type in schema.entity_types.itervalues():
-            if "CREATE" in entity_type.operations and not entity_type.short_name == 'dummy':
+            if config in entity_type.all_bases:
                 f.write('.SS "%s"\n'% entity_type.short_name)
                 write_attributes(entity_type)
-                f.write('.IP "Includes %s."\n'%(', '.join(entity_type.include)))
+                f.write('.IP "Annotations %s."\n'%(', '.join(
+                    [a.short_name for a in entity_type.annotations])))
 
 
 if __name__ == '__main__':

Modified: qpid/dispatch/trunk/python/qpid_dispatch/management/client.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch/management/client.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/client.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/client.py Fri Dec  5 15:12:53 2014
@@ -331,6 +331,9 @@ class Node(object):
     def get_types(self, type=None):
         return self.call(self.node_request(operation="GET-TYPES", entityType=type)).body
 
+    def get_annotations(self, type=None):
+        return self.call(self.node_request(operation="GET-ANNOTATIONS", entityType=type)).body
+
     def get_attributes(self, type=None):
         return self.call(self.node_request(operation="GET-ATTRIBUTES", entityType=type)).body
 

Modified: qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json Fri Dec  5 15:12:53 2014
@@ -1,56 +1,40 @@
 {
+    "description":  "Schema for the Qpid Dispatch Router management model. See qdrouter.txt.",
+
     "prefix": "org.apache.qpid.dispatch",
-    "includes": {
-        "common": {
-            "description":"Attributes common to all entities.",
-            "attributes": {
-                "name": {
-                    "type": "String",
-                    "required": true,
-                    "unique": true,
-                    "default": "$identity",
-                    "description": "Unique name, can be changed."
-                },
-                "identity": {
-                    "type": "String",
-                    "required": true,
-                    "unique": true,
-                    "default": "$name",
-                    "description": "Unique identity, will not change."
-                },
-                "type": {
-                    "type": "String",
-                    "required": true,
-                    "value": "$$entityType",
-                    "description": "Management entity type."
-                }
-            }
-        },
 
-        "hostAddress": {
-            "description":"Specifies where in the Internet to find a peer AMQP container.",
+    "annotations": {
+
+        "addrPort": {
+            "description": "Attributes for internet address and port.",
             "attributes": {
                 "addr": {
-                    "description":"Host address: ipv4 or ipv6 literal or a host name",
+                    "description":"Host address: ipv4 or ipv6 literal or a host name.",
                     "type": "String",
                     "default": "0.0.0.0"
                 },
                 "port": {
-                    "description":"Port number or symbolic service name",
+                    "description":"Port number or symbolic service name.",
                     "type": "String",
                     "default": "amqp"
                 }
             }
         },
 
-        "connection": {
-            "description":"Attributes common to AMQP connections",
+        "saslMechanisms": {
+            "description": "Attribute for a list of SASL mechanisms.",
             "attributes": {
                 "saslMechanisms": {
                     "type": "String",
                     "required": true,
                     "description": "Comma separated list of accepted SASL authentication mechanisms."
-                },
+                }
+            }
+        },
+
+        "connectionRole": {
+            "description": "Attribute for the role of a connection.",
+            "attributes": {
                 "role": {
                     "type": [
                         "normal",
@@ -58,7 +42,7 @@
                         "on-demand"
                     ],
                     "default": "normal",
-                    "description": "The role of an established connection. In the normal role, the connection is assumed to be used for AMQP clients that are doing normal message delivery over the connection.  In the inter-router role, the connection is assumed to be to another router in the network.  Inter-router discovery and routing protocols can only be used over interRouter connections. "
+                    "description": "The role of an established connection. In the normal role, the connection is assumed to be used for AMQP clients that are doing normal message delivery over the connection.  In the inter-router role, the connection is assumed to be to another router in the network.  Inter-router discovery and routing protocols can only be used over interRouter connections."
                 }
             }
         },
@@ -68,36 +52,74 @@
             "attributes": {
                 "certDb": {
                     "type": "String",
-                    "description": "The path to the database that contains the public certificates of trusted certificate authorities (CAs). "
+                    "description": "The path to the database that contains the public certificates of trusted certificate authorities (CAs)."
                 },
                 "certFile": {
                     "type": "String",
-                    "description": "The path to the file containing the PEM-formatted public certificate to be used on the local end of any connections using this profile. "
+                    "description": "The path to the file containing the PEM-formatted public certificate to be used on the local end of any connections using this profile."
                 },
                 "keyFile": {
                     "type": "String",
-                    "description": "The path to the file containing the PEM-formatted private key for the above certificate. "
+                    "description": "The path to the file containing the PEM-formatted private key for the above certificate."
                 },
                 "passwordFile": {
                     "type": "String",
-                    "description": "If the above private key is password protected, this is the path to a file containing the password that unlocks the certificate key. "
+                    "description": "If the above private key is password protected, this is the path to a file containing the password that unlocks the certificate key."
                 },
                 "password": {
                     "type": "String",
-                    "description": "An alternative to storing the password in a file referenced by passwordFile is to supply the password right here in the configuration file.  This option can be used by supplying the password in the 'password' option.  Don't use both password and passwordFile in the same profile. "
+                    "description": "An alternative to storing the password in a file referenced by passwordFile is to supply the password right here in the configuration file.  This option can be used by supplying the password in the 'password' option.  Don't use both password and passwordFile in the same profile."
                 }
             }
         }
     },
 
     "entityTypes": {
+
+        "entity": {
+            "description": "Base entity type for all entities.",
+            "attributes": {
+                "name": {
+                    "type": "String",
+                    "required": true,
+                    "unique": true,
+                    "default": "$identity",
+                    "description": "Unique name, can be changed."
+                },
+                "identity": {
+                    "type": "String",
+                    "required": true,
+                    "unique": true,
+                    "default": "$name",
+                    "description": "Unique identity, will not change."
+                },
+                "type": {
+                    "type": "String",
+                    "required": true,
+                    "value": "$$entityType",
+                    "description": "Management entity type."
+                }
+            }
+        },
+
+        "configurationEntity": {
+            "description": "Base type for entities containing configuration information.",
+            "extends": "entity",
+            "attributes": {}
+        },
+
+        "operationalEntity": {
+            "description": "Base type for entities containing current operational information.",
+            "extends": "entity",
+            "operations": ["READ"],
+            "attributes": {}
+        },
+
         "container": {
-            "description":"Attributes related to the AMQP container",
+            "description":"Attributes related to the AMQP container.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ"],
             "singleton": true,
-            "include": [
-                "common"
-            ],
             "attributes": {
                 "containerName": {
                     "type": "String",
@@ -105,22 +127,20 @@
                 },
                 "workerThreads": {
                     "type": "Integer",
-                    "default": "1",
-                    "description": "The number of threads that will be created to process message traffic and other application work (timers, non-amqp file descriptors, etc.) "
+                    "default": 1,
+                    "description": "The number of threads that will be created to process message traffic and other application work (timers, non-amqp file descriptors, etc.) ."
                 }
             }
         },
 
         "router": {
-            "description":"Tracks peer routers and computes routes to destinations",
+            "description":"Tracks peer routers and computes routes to destinations.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ"],
             "singleton": true,
-            "include": [
-                "common"
-            ],
             "attributes": {
                 "routerId": {
-                    "description":"Router's unique identity",
+                    "description":"Router's unique identity.",
                     "type": "String"
                 },
                 "mode": {
@@ -131,108 +151,108 @@
                         "endpoint"
                     ],
                     "default": "standalone",
-                    "description": "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. In interior mode, the router operates in cooreration with other interior routers in an interconnected network.  In edge mode, 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. "
+                    "description": "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. In interior mode, the router operates in cooreration with other interior routers in an interconnected network.  In edge mode, 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."
                 },
                 "area": {
                     "type": "String",
-                    "description": "Unused placeholder"
+                    "description": "Unused placeholder."
                 },
                 "helloInterval": {
                     "type": "Integer",
                     "default": 1,
-                    "description": "Interval in seconds between HELLO messages sent to neighbor routers"
+                    "description": "Interval in seconds between HELLO messages sent to neighbor routers."
                 },
                 "helloMaxAge": {
                     "type": "Integer",
                     "default": 3,
-                    "description": "Time in seconds after which a neighbor is declared lost if no HELLO is received"
+                    "description": "Time in seconds after which a neighbor is declared lost if no HELLO is received."
                 },
                 "raInterval": {
                     "type": "Integer",
                     "default": 30,
-                    "description": "Interval in seconds between Router-Advertisements sent to all routers"
+                    "description": "Interval in seconds between Router-Advertisements sent to all routers."
                 },
                 "remoteLsMaxAge": {
                     "type": "Integer",
                     "default": 60,
-                    "description": "Time in seconds after which link state is declared stale if no RA is received"
+                    "description": "Time in seconds after which link state is declared stale if no RA is received."
                 },
                 "mobileAddrMaxAge": {
                     "type": "Integer",
                     "default": 60,
-                    "description": "Time in seconds after which mobile addresses are declared stale if no RA is received"
+                    "description": "Time in seconds after which mobile addresses are declared stale if no RA is received."
                 },
-                "addrCount": {"type": "Integer", "description":"Number of addresses known to the router"},
-                "linkCount": {"type": "Integer", "description":"Number of links attached to the router node"},
-                "nodeCount": {"type": "Integer", "description":"Number of known peer router nodes"}
+	        "addrCount": {"type": "Integer", "description":"Number of addresses known to the router."},
+	        "linkCount": {"type": "Integer", "description":"Number of links attached to the router node."},
+	        "nodeCount": {"type": "Integer", "description":"Number of known peer router nodes."}
             }
         },
 
         "listener": {
-            "description": "Listens for incoming connections to the router",
+            "description": "Listens for incoming connections to the router.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ"],
-            "include": [
-                "common",
+            "annotations": [
                 "sslProfile",
-                "hostAddress",
-                "connection"
+                "addrPort",
+                "saslMechanisms",
+                "connectionRole"
             ],
             "attributes": {
                 "requirePeerAuth": {
                     "type": "Boolean",
                     "default": true,
-                    "description": "Only for listeners using SSL.  If set to \"yes\", attached clients will be required to supply a certificate.  If the certificate is not traceable to a CA in the ssl profile's cert-db, authentication fails for the connection. "
+                    "description": "Only for listeners using SSL.  If set to 'yes', attached clients will be required to supply a certificate.  If the certificate is not traceable to a CA in the ssl profile's cert-db, authentication fails for the connection."
                 },
                 "trustedCerts": {
                     "type": "String",
-                    "description": "This optional setting can be used to reduce the set of available CAs for client authentication.  If used, this setting must provide a path to a PEM file that contains the trusted certificates. "
+                    "description": "This optional setting can be used to reduce the set of available CAs for client authentication.  If used, this setting must provide a path to a PEM file that contains the trusted certificates."
                 },
                 "allowUnsecured": {
                     "type": "Boolean",
                     "default": false,
-                    "description": "For listeners using SSL only.  If set to \"yes\", this option causes the listener to watch the initial network traffic to determine if the client is using SSL or is running in-the-clear.  The listener will enable SSL only if the client uis using SSL. "
+                    "description": "For listeners using SSL only.  If set to 'yes', this option causes the listener to watch the initial network traffic to determine if the client is using SSL or is running in-the-clear.  The listener will enable SSL only if the client uis using SSL."
                 },
                 "allowNoSasl": {
                     "type": "Boolean",
                     "default": false,
-                    "description": "If set to \"yes\", this option causes the listener to allow clients to connect even if they skip the SASL authentication protocol. "
+                    "description": "If set to 'yes', this option causes the listener to allow clients to connect even if they skip the SASL authentication protocol."
                 },
                 "maxFrameSize": {
                     "type": "Integer",
                     "default": 65536,
-                    "description": "Defaults to 65536.  If specified, it is the maximum frame size in octets that will be used in the connection-open negotiation with a connected peer.  The frame size is the largest contiguous set of uniterruptible data that can be sent for a message delivery over the connection. Interleaving of messages on different links is done at frame granularity. "
+                    "description": "Defaults to 65536.  If specified, it is the maximum frame size in octets that will be used in the connection-open negotiation with a connected peer.  The frame size is the largest contiguous set of uniterruptible data that can be sent for a message delivery over the connection. Interleaving of messages on different links is done at frame granularity."
                 }
             }
         },
 
         "connector": {
-            "description": "Establishes an outgoing connections from the router",
+            "description": "Establishes an outgoing connections from the router.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ"],
-            "include": [
-                "common",
+            "annotations": [
                 "sslProfile",
-                "hostAddress",
-                "connection"
+                "addrPort",
+                "saslMechanisms",
+                "connectionRole"
             ],
             "attributes": {
                 "allowRedirect": {
                     "type": "Boolean",
                     "default": true,
-                    "description": "Allow the peer to redirect this connection to another address"
+                    "description": "Allow the peer to redirect this connection to another address."
                 },
                 "maxFrameSize": {
                     "type": "Integer",
                     "default": 65536,
-                    "description": "Maximum frame size in octets that will be used in the connection-open negotiation with a connected peer.  The frame size is the largest contiguous set of uniterruptible data that can be sent for a message delivery over the connection. Interleaving of messages on different links is done at frame granularity. "
+                    "description": "Maximum frame size in octets that will be used in the connection-open negotiation with a connected peer.  The frame size is the largest contiguous set of uniterruptible data that can be sent for a message delivery over the connection. Interleaving of messages on different links is done at frame granularity."
                 }
             }
         },
 
         "log": {
-            "description": "Set the level of logging output from a particular module",
-            "include": [
-                "common"
-            ],
+            "description": "Set the level of logging output from a particular module.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ", "UPDATE", "DELETE"],
             "attributes": {
                 "module": {
@@ -263,40 +283,38 @@
                         "critical"
                     ],
                     "default": "info",
-                    "description": "Indicates the minimum logging level for the module. E.g. 'warning' means log warning, error and critical messages. 'trace' logs all messages. 'none' disables logging for the module. "
+                    "description": "Indicates the minimum logging level for the module. E.g. 'warning' means log warning, error and critical messages. 'trace' logs all messages. 'none' disables logging for the module."
                 },
                 "timestamp": {
                     "type": "Boolean",
                     "default": true,
-                    "description": "Include timestamp in log messages"
+                    "description": "Include timestamp in log messages."
                 },
                 "source": {
                     "type": "Boolean",
                     "default": false,
-                    "description": "Include source file and line number in log messages"
+                    "description": "Include source file and line number in log messages."
                 },
                 "output": {
                     "type": "String",
-                    "description": "Where to send log messages. Can be 'stderr', 'syslog' or a file name. "
+                    "description": "Where to send log messages. Can be 'stderr', 'syslog' or a file name."
                 }
             }
         },
 
         "fixedAddress": {
-            "description":"Establishes semantics for addresses starting with a prefix",
+            "description":"Establishes semantics for addresses starting with a prefix.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ"],
-            "include": [
-                "common"
-            ],
             "attributes": {
                 "prefix": {
                     "type": "String",
                     "required": true,
-                    "description": "The address prefix (always starting with \"/\"). "
+                    "description": "The address prefix (always starting with '/')."
                 },
                 "phase": {
                     "type": "Integer",
-                    "description": "The phase of a multi-hop address passing through one or more waypoints"
+                    "description": "The phase of a multi-hop address passing through one or more waypoints."
                 },
                 "fanout": {
                     "type": [
@@ -304,7 +322,7 @@
                         "single"
                     ],
                     "default": "multiple",
-                    "description": "One of \"multiple\" or \"single\".  Multiple fanout is a non-competing pattern.  If there are multiple consumers using the same address, each consumer will receive its own copy of every message sent to the address.  Single fanout is a competing pattern where each message is sent to only one consumer. "
+                    "description": "One of 'multiple' or 'single'.  Multiple fanout is a non-competing pattern.  If there are multiple consumers using the same address, each consumer will receive its own copy of every message sent to the address.  Single fanout is a competing pattern where each message is sent to only one consumer."
                 },
                 "bias": {
                     "type": [
@@ -312,35 +330,33 @@
                         "spread"
                     ],
                     "default": "closest",
-                    "description": "Only if fanout is single.  One of \"closest\" or \"spread\".  Closest bias means that messages to an address will always be delivered to the closest (lowest cost) subscribed consumer. Spread bias will distribute the messages across subscribers in an approximately even manner. "
+                    "description": "Only if fanout is single.  One of 'closest' or 'spread'.  Closest bias means that messages to an address will always be delivered to the closest (lowest cost) subscribed consumer. Spread bias will distribute the messages across subscribers in an approximately even manner."
                 }
             }
         },
 
         "waypoint": {
-            "description":"A remote node that messages for an address pass through",
+            "description":"A remote node that messages for an address pass through.",
+            "extends": "configurationEntity",
             "operations": ["CREATE", "READ"],
-            "include": [
-                "common"
-            ],
             "attributes": {
                 "address": {
-                    "description":"The AMQP address of the waypoint",
+                    "description":"The AMQP address of the waypoint.",
                     "type": "String",
                     "required": true
                 },
                 "connector": {
-                    "description":"The name of the on-demand connector used to reach the waypoint's container",
+                    "description":"The name of the on-demand connector used to reach the waypoint's container.",
                     "type": "String",
                     "required": true
                 },
                 "inPhase": {
-                    "description":"The phase of the address as it is routed _to_ the waypoint",
+                    "description":"The phase of the address as it is routed _to_ the waypoint.",
                     "type": "Integer",
                     "default": -1
                 },
                 "outPhase": {
-                    "description":"The phase of the address as it is routed _from_ the waypoint",
+                    "description":"The phase of the address as it is routed _from_ the waypoint.",
                     "type": "Integer",
                     "default": -1
                 }
@@ -349,8 +365,8 @@
 
         "dummy": {
             "description": "Dummy entity for test purposes.",
+            "extends": "entity",
             "operations": ["CREATE", "READ", "UPDATE", "DELETE", "CALLME"],
-            "include": ["common"],
             "attributes": {
                 "arg1": {"type": "String"},
                 "arg2": {"type": "String"},
@@ -359,10 +375,10 @@
             }
         },
 
+
         "router.link": {
             "description": "Link to another AMQP endpoint: router node, client or other AMQP process.",
-            "operations": ["READ"],
-            "include": ["common"],
+            "extends": "operationalEntity",
             "attributes": {
                 "linkType": {"type": ["endpoint", "waypoint", "inter-router", "inter-area"]},
                 "linkDir": {"type": ["in", "out"]},
@@ -373,9 +389,8 @@
         },
 
         "router.address": {
-            "description": "AMQP address managed by the router",
-            "operations": ["READ"],
-            "include": ["common"],
+            "description": "AMQP address managed by the router.",
+            "extends": "operationalEntity",
             "attributes": {
                 "inProcess": {"type": "Boolean"},
                 "subscriberCount": {"type": "Integer"},
@@ -389,9 +404,8 @@
         },
 
         "router.node": {
-            "description": "AMQP node managed by the router",
-            "operations": ["READ"],
-            "include": ["common"],
+            "description": "AMQP node managed by the router.",
+            "extends": "operationalEntity",
             "attributes": {
                 "addr": {"type": "String"},
                 "nextHop": {"type": "Integer"},
@@ -401,9 +415,8 @@
         },
 
         "connection": {
-            "description": "Connections to the router's container",
-            "operations": ["READ"],
-            "include": ["common"],
+            "description": "Connections to the router's container.",
+            "extends": "operationalEntity",
             "attributes": {
                 "container": {"type": "String"} ,
                 "state": {"type": [
@@ -422,8 +435,7 @@
 
         "allocator": {
             "description": "Memory allocation pool.",
-            "operations": ["READ"],
-            "include": ["common"],
+            "extends": "operationalEntity",
             "attributes": {
                 "typeSize": {"type": "Integer"},
                 "transferBatchSize": {"type": "Integer"},

Added: qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.txt
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.txt?rev=1643319&view=auto
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.txt (added)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.txt Fri Dec  5 15:12:53 2014
@@ -0,0 +1,57 @@
+Informal desctription of the qdrouter.json schema.
+==================================================
+
+qdrouterd.json defines annotations and entity types of the Qpid Dispatch Router management model.
+The model is based on the AMQP management specification.
+
+The schema is a JSON map with the following keys:
+
+- "description": documentation string
+- "prefix": attribute and annotation names in the schema are short form with no prefix. Names must be prepended with this prefix when they are exposed to outside AMQP management clients or agents.
+
+- "annotations": map of annotation names to definitions.
+- "entityTypes": map of entity type names to definitions.
+
+Annotation and entity type definition maps have the following keys:
+
+- "description": documentation string.
+- "operations": list of allowed operation names.
+- "attributes": map of attribute names to attribute definitions.
+
+Entity type definitions also have these fields:
+
+- "extends": Name of base type, the new type includes all operations and attributes from the base type.
+- "annotations": List of annotation names. The new type includes all operations and attributes from all the annotations.
+
+Attribute definition maps have the following fields:
+
+- "type": one of the following:
+  - "String": a unicode string value.
+  - "Integer": an integer value.
+  - "Boolean": a true/false value.
+  - [...]: a list of strings means an enumeration. Values must be one of the strings or an integer integer index into the list, starting from 0.
+
+- "default": a default value can be a literal value or a reference to another attribute in the form $attributeName.
+
+
+There is the following hierarchy among entity types:
+
+"entity": the base of all entity types.
+- "configurationEntity": base for all types that hold configuration information.
+- "operationalEntity": base for all types that hold operational information.
+
+"Configuration" information is supplied in advance and expresse intent. For
+example "I want the router to listen on port N", or, "I want the router to
+connect to this address" All the entities that can be used in the configuration
+file are now "configurationEntity".
+
+"Operational" information reflects the actual state of the router at a given
+moment in time.  For example, "how many addresses are presently active on the
+router?" All the entities queried by qdstat are now "operationalEntity".
+
+The two types are often related, for example "I want an on-demand connection to
+this address" and "is there presently a live outgoing connection to this
+address?"  As a matter of good practice we separate the two kinds of
+entities. For example "listener" and "connector" are configuration entities
+expressing the intent to make or receive connectoins. The "connection" entity
+holds live operational information about an actual connection.

Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py Fri Dec  5 15:12:53 2014
@@ -496,8 +496,13 @@ class Agent(object):
 
     def get_types(self, request):
         type = self.requested_type(request)
-        return (OK, dict((t, []) for t in self.schema.entity_types
-                         if not type or type.name == t))
+        return (OK, dict((t.name, [b.name for b in t.all_bases])
+                         for t in self.schema.by_type(type)))
+
+    def get_annotations(self, request):
+        type = self.requested_type(request)
+        return (OK, dict((t.name, [a.name for a in t.annotations])
+                         for t in self.schema.by_type(type)))
 
     def get_operations(self, request):
         type = self.requested_type(request)

Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py Fri Dec  5 15:12:53 2014
@@ -33,6 +33,8 @@ class Config(object):
 
     def __init__(self, filename=None, schema=QdSchema()):
         self.schema = schema
+        self.config_types = [e for e in schema.entity_types.itervalues()
+                             if schema.is_configuration(e)]
         if filename:
             self.load(filename)
         else:
@@ -66,24 +68,25 @@ class Config(object):
 
     def _expand(self, content):
         """
-        Find include sections (defined by schema) in the content,
-        expand references and remove the include sections.
+        Find annotation sections (defined by schema) in the content,
+        expand references and remove the annotation sections.
         @param content: ((section-name:{name:value...}))
         """
-        def _expand_section(section, includes):
+        def _expand_section(section, annotations):
             """Expand one section"""
             attrs = section[1]
             for k in attrs.keys(): # Iterate over keys() because we will modify attr
-                inc = [i[1] for i in includes if i[0] == k and i[1]['name'] == attrs[k]]
+                inc = [i[1] for i in annotations if i[0] == k and i[1]['name'] == attrs[k]]
                 if inc:
                     assert len(inc) == 1
                     inc = copy(inc[0])
-                    del inc['name'] # Not a real attribute, just an include id.
+                    del inc['name'] # Not a real attribute, just an annotation id.
                     attrs.update(inc)
-                    del attrs[k] # Delete the include attribute.
+                    del attrs[k] # Delete the annotation attribute.
             return section
-        includes = [s for s in content if s[0] in self.schema.includes]
-        return [_expand_section(s, includes) for s in content if s[0] not in self.schema.includes]
+        annotations = [s for s in content if self.schema.annotation(s[0], error=False)]
+        return [_expand_section(s, annotations) for s in content
+                if self.schema.is_configuration(self.schema.entity_type(s[0], False))]
 
     def _default_ids(self, content):
         """
@@ -92,7 +95,7 @@ class Config(object):
         - If entity has one of name/identity set the other to be the same.
         - If entity has both, do nothing
         """
-        counts = dict((et.short_name, 0) for et in self.schema.entity_types.itervalues())
+        counts = dict((et.short_name, 0) for et in self.config_types)
         for section in content:
             section_name, attrs = section
             count = counts[section_name]
@@ -123,7 +126,7 @@ class Config(object):
         else:
             sections = self._parse(source)
             # Add missing singleton sections
-            for et in self.schema.entity_types.itervalues():
+            for et in self.config_types:
                 if et.singleton and not [s for s in sections if s[0] == et.short_name]:
                     sections.append((et.short_name, {}))
             sections = self._expand(sections)

Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.py Fri Dec  5 15:12:53 2014
@@ -30,10 +30,16 @@ class QdSchema(schema.Schema):
     """
     Qpid Dispatch Router management schema.
     """
+
+    CONFIGURATION_ENTITY = "configurationEntity"
+    OPERATIONAL_ENTITY = "operationalEntity"
+
     def __init__(self):
         """Load schema."""
         qd_schema = get_data('qpid_dispatch.management', 'qdrouter.json')
         super(QdSchema, self).__init__(**json.loads(qd_schema, **JSON_LOAD_KWARGS))
+        self.configuration_entity = self.entity_type(self.CONFIGURATION_ENTITY)
+        self.operational_entity = self.entity_type(self.OPERATIONAL_ENTITY)
 
     def validate(self, entities, full=True, **kwargs):
         """
@@ -53,3 +59,9 @@ class QdSchema(schema.Schema):
                 for connect in entities.get(entity_type='listeners') + entities.get(entity_type='connector'):
                     if connect['role'] != 'normal':
                         raise schema.ValidationError("Role '%s' for connection '%s' only permitted with 'interior' mode" % (connect['role'], connect.name))
+
+    def is_configuration(self, entity_type):
+        return entity_type and self.configuration_entity in entity_type.all_bases
+
+    def is_operational(self, entity_type):
+        return entity_type and self.operational_entity in entity_type.all_bases

Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py Fri Dec  5 15:12:53 2014
@@ -188,11 +188,11 @@ class AttributeType(object):
     @ivar value: Fixed value for the attribute. Can be a reference.
     @ivar unique: True if the attribute value is unique.
     @ivar description: Description of the attribute type.
-    @ivar include: Include section or None
+    @ivar annotation: Annotation section or None
     """
 
     def __init__(self, name, type=None, default=None, required=False, unique=False,
-                 value=None, include=None, description=""):
+                 value=None, annotation=None, description=""):
         """
         See L{AttributeType} instance variables.
         """
@@ -203,7 +203,7 @@ class AttributeType(object):
         self.value = value
         self.unique = unique
         self.description = description
-        self.include = include
+        self.annotation = annotation
         if self.value is not None and self.default is not None:
             raise ValidationError("Attribute '%s' has default value and fixed value"%self.name)
 
@@ -258,14 +258,18 @@ class AttributeType(object):
         return "%s(%s)"%(self.__class__.__name__, self.name)
 
 
-class AttributeTypeHolder(object):
-    """Base class for IncludeType and EntityType - a named holder of attribute types"""
+class RedefinedError(ValueError): pass
 
-    def __init__(self, name, schema, attributes=None, description=""):
-        self.name, self.schema, self.description = name, schema, description
+
+class AttrsAndOps(object):
+    """Base class for Annotation and EntityType - a named holder of attribute types and operations"""
+
+    def __init__(self, name, schema, attributes=None, operations=None, description=""):
+        self.name, self.schema, self.description = schema.long_name(name), schema, description
+        self.short_name = schema.short_name(name)
         self.attributes = OrderedDict()
-        if attributes:
-            self.add_attributes(attributes)
+        if attributes: self.add_attributes(attributes)
+        self.operations = operations or []
 
     def add_attributes(self, attributes):
         """
@@ -283,6 +287,7 @@ class AttributeTypeHolder(object):
             ('attributes', OrderedDict(
                 (k, v.dump()) for k, v in self.attributes.iteritems()
                 if k != 'type')), # Don't dump 'type' attribute, dumped separately.
+            ('operations', self.operations),
             ('description', self.description or None)
         ])
 
@@ -292,16 +297,17 @@ class AttributeTypeHolder(object):
     def __str__(self):
         return self.name
 
-class IncludeType(AttributeTypeHolder):
-    """An include type defines a set of attributes that can be re-used by multiple EntityTypes"""
-    def __init__(self, name, schema, attributes=None, description=""):
-        super(IncludeType, self).__init__(name, schema, attributes, description)
+
+class Annotation(AttrsAndOps):
+    """An annotation type defines a set of attributes that can be re-used by multiple EntityTypes"""
+    def __init__(self, name, schema, attributes=None, operations=None, description=""):
+        super(Annotation, self).__init__(name, schema, attributes, operations, description)
         attributes = attributes or {}
         for a in self.attributes.itervalues():
-            a.include = self
+            a.annotation = self
 
 
-class EntityType(AttributeTypeHolder):
+class EntityType(AttrsAndOps):
     """
     An entity type defines a set of attributes for an entity.
 
@@ -309,31 +315,50 @@ class EntityType(AttributeTypeHolder):
     @ivar short_name: Un-prefixed short name.
     @ivar attributes: Map of L{AttributeType} for entity.
     @ivar singleton: If true only one entity of this type is allowed.
-    #ivar include: List of names of sections included by this entity.
+    #ivar annotation: List of names of sections annotationd by this entity.
     """
-    def __init__(self, name, schema, singleton=False, include=None, attributes=None,
-                 description="", operations=None):
+    def __init__(self, name, schema, singleton=False, annotations=None, attributes=None,
+                 extends=None, description="", operations=None):
         """
         @param name: name of the entity type.
         @param schema: schema for this type.
         @param singleton: True if entity type is a singleton.
-        @param include: List of names of include types for this entity.
+        @param annotation: List of names of annotation types for this entity.
         @param attributes: Map of attributes {name: {type:, default:, required:, unique:}}
         @param description: Human readable description.
         @param operations: Allowed operations, list of operation names.
         """
-        super(EntityType, self).__init__(name, schema, attributes, description)
-        self.short_name = schema.short_name(name)
-        self.refs = {'entityType': name}
+        super(EntityType, self).__init__(name, schema, attributes, operations, description)
+        self.base = None
+        self.all_bases = []
+        if extends:
+            self.base = schema.entity_type(extends)
+            self.all_bases = [self.base] + self.base.all_bases
+            self._extend(self.base, 'extend')
+
+        self.annotations = [ schema.annotation(a) for a in annotations or []]
+        for a in self.annotations:
+            self._extend(a, 'be annotated')
+
+        # This map defines values that can be referred to using $$ in the schema.
+        self.refs = {'entityType': self.name}
         self.singleton = singleton
-        self.include = include
-        self.operations = operations or []
-        if include and self.schema.includes:
-            for i in include:
-                if not i in schema.includes:
-                    raise ValidationError("Include '%s' not found in %s'"%(i, self))
-                for attr in schema.includes[i].attributes.itervalues():
-                    self.attributes[attr.name] = attr
+
+    def _extend(self, other, how):
+        """Add attributes and operations from other"""
+        def check(a, b, what):
+            overlap = set(a) & set(b)
+            if overlap:
+                raise RedefinedError("'%s' cannot %s '%s', re-defines %s: %s"
+                                     % (name, how, other.short_name, what, list(overlap).join(', ')))
+        check(self.operations, other.operations, "operations")
+        self.operations = self.operations + other.operations
+        check(self.attributes.iterkeys(), other.attributes.itervalues(), "attributes")
+        self.attributes.update(other.attributes)
+
+    def extends(self, base): return base in self.all_bases
+
+    def is_a(self, type): return type == self or self.extends(type)
 
     def dump(self):
         """Json friendly representation"""
@@ -418,30 +443,31 @@ class Schema(object):
 
     @ivar prefix: Prefix to prepend to short entity type names.
     @ivar entityTypes: Map of L{EntityType} by name.
-    @ivar includes: Map of L{IncludeType} by name.
+    @ivar annotations: Map of L{Annotation} by name.
     @ivar description: Text description of schema.
     """
-    def __init__(self, prefix="", includes=None, entityTypes=None, description=""):
+    def __init__(self, prefix="", annotations=None, entityTypes=None, description=""):
         """
         @param prefix: Prefix for entity names.
-        @param includes: Map of  { include-name: {attribute-name:value, ... }}
-        @param entity_types: Map of  { entity-type-name: { singleton:, include:[...], attributes:{...}}}
+        @param annotations: Map of  { annotation-name: {attribute-name:value, ... }}
+        @param entity_types: Map of  { entity-type-name: { singleton:, annotation:[...], attributes:{...}}}
         @param description: Human readable description.
         """
-        if prefix.endswith('.'): prefix = prefix[:-1]
-        self.prefix = self.prefixdot = prefix
-        if prefix: self.prefixdot += '.'
+        if prefix:
+            self.prefix = prefix.strip('.')
+            self.prefixdot = self.prefix + '.'
+        else:
+            self.prefix = self.prefixdot = ""
 
         self.description = description
-        self.includes = OrderedDict()
-        if includes:
-            for k, v in includes.iteritems():
-                self.includes[k] = IncludeType(k, self, **v)
+        self.annotations = OrderedDict()
         self.entity_types = OrderedDict()
-        if entityTypes:
-            for k, v in entityTypes.iteritems():
-                name = self.long_name(k)
-                self.entity_types[name] = EntityType(name, self, **v)
+        def add_defs(thing, mymap, defs):
+            for k, v in defs.iteritems():
+                t = thing(k, self, **v)
+                mymap[t.name] = t
+        add_defs(Annotation, self.annotations, annotations or {})
+        add_defs(EntityType, self.entity_types, entityTypes or {})
 
     def short_name(self, name):
         """Remove prefix from name if present"""
@@ -460,22 +486,24 @@ class Schema(object):
         """Return json-friendly representation"""
         return OrderedDict([
             ('prefix', self.prefix),
-            ('includes',
-             OrderedDict((k, v.dump()) for k, v in self.includes.iteritems())),
+            ('annotations',
+             OrderedDict((a.short_name, a.dump()) for a in self.annotations.itervalues())),
             ('entityTypes',
-             OrderedDict((e.short_name, e.dump())
-                         for e in self.entity_types.itervalues()))
+             OrderedDict((e.short_name, e.dump()) for e in self.entity_types.itervalues()))
         ])
 
-    def entity_type(self, name, error=True):
-        """Look up an EntityType by name.
-        If error raise exception if not found else return None
-        """
+    def _lookup(self, map, name, message, error):
         try:
-            return self.entity_types[self.long_name(name)]
+            return map[self.long_name(name)]
         except KeyError:
-            if error: raise ValidationError("No such entity type '%s'" % name)
-        return None
+            if error: raise ValidationError(message % name)
+            else: return None
+
+    def entity_type(self, name, error=True):
+        return self._lookup(self.entity_types, name, "No such entity type '%s'", error)
+
+    def annotation(self, name, error=True):
+        return self._lookup(self.annotations, name, "No such annotation '%s'", error)
 
     def validate_entity(self, attributes, check_required=True, add_default=True,
                         check_unique=None, check_singleton=None):
@@ -531,6 +559,11 @@ class Schema(object):
         """Convert a list of attribute maps into a list of L{Entity}"""
         return [self.entity(m) for m in attribute_maps]
 
+    def by_type(self, type):
+        """Return an iterator over entity types that extend or are type.
+        If type is None return all entities."""
+        return (t for t in self.entity_types.itervalues() if not type or t.is_a(type))
+
 
 class Entity(entity.Entity):
     """A map of attributes associated with an L{EntityType}"""

Modified: qpid/dispatch/trunk/tests/management/schema.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/management/schema.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/management/schema.py (original)
+++ qpid/dispatch/trunk/tests/management/schema.py Fri Dec  5 15:12:53 2014
@@ -33,7 +33,7 @@ def replace_od(thing):
 
 SCHEMA_1 = {
     "prefix":"org.example",
-    "includes": {
+    "annotations": {
         "entityId": {
             "attributes": {
                 "name": {"type":"String", "required": True, "unique":True},
@@ -44,19 +44,19 @@ SCHEMA_1 = {
     "entityTypes": {
         "container": {
             "singleton": True,
-            "include" : ["entityId"],
+            "annotations" : ["entityId"],
             "attributes": {
                 "workerThreads" : {"type":"Integer", "default": 1}
             }
         },
         "listener": {
-            "include" : ["entityId"],
+            "annotations" : ["entityId"],
             "attributes": {
                 "addr" : {"type":"String"}
             }
         },
         "connector": {
-            "include" : ["entityId"],
+            "annotations" : ["entityId"],
             "attributes": {
                 "addr" : {"type":"String"}
             }
@@ -112,7 +112,7 @@ class SchemaTest(unittest.TestCase):
 
 
     def test_entity_type(self):
-        s = Schema(includes={
+        s = Schema(annotations={
             'i1':{'attributes': { 'foo1': {'type':'String', 'default':'FOO1'}}},
             'i2':{'attributes': { 'foo2': {'type':'String', 'default':'FOO2'}}}})
 
@@ -122,8 +122,8 @@ class SchemaTest(unittest.TestCase):
             'e': {'type':['x', 'y']}})
         self.assertRaises(ValidationError, e.validate, {}) # Missing required 'req'
         self.assertEqual(e.validate({'req':42}), {'foo': 'FOO', 'req': 42})
-        # Try with an include
-        e = EntityType('e2', s, attributes={'x':{'type':'Integer'}}, include=['i1', 'i2'])
+        # Try with an annotation
+        e = EntityType('e2', s, attributes={'x':{'type':'Integer'}}, annotations=['i1', 'i2'])
         self.assertEqual(e.validate({'x':1}), {'x':1, 'foo1': 'FOO1', 'foo2': 'FOO2'})
 
     def test_entity_refs(self):
@@ -140,13 +140,13 @@ class SchemaTest(unittest.TestCase):
                          e.validate({'identity': 'x', 'name':'y'}))
         self.assertRaises(ValidationError, e.validate, {}) # Circular reference.
 
-    def test_entity_include_refs(self):
-        s = Schema(includes={
+    def test_entity_annotation_refs(self):
+        s = Schema(annotations={
             'i1': {'attributes': {
                 'name': {'type':'String', 'default':'$identity'},
                 'identity': {'type':'String', 'default':'$name', "required": True}}}})
 
-        e = EntityType('MyEntity', s, attributes={}, include=['i1'])
+        e = EntityType('MyEntity', s, attributes={}, annotations=['i1'])
         self.assertEqual({'identity': 'x', 'name': 'x'}, e.validate({'identity':'x'}))
         self.assertEqual({'identity': 'x', 'name': 'x'}, e.validate({'name':'x'}))
         self.assertEqual({'identity': 'x', 'name': 'y'}, e.validate({'identity': 'x', 'name':'y'}))
@@ -159,7 +159,7 @@ class SchemaTest(unittest.TestCase):
         expect = {
             "prefix":"org.example",
 
-            "includes": {
+            "annotations": {
                 "entityId": {
                     "attributes": {
                         "name": {"required": True,

Modified: qpid/dispatch/trunk/tests/system_tests_management.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/system_tests_management.py?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/system_tests_management.py (original)
+++ qpid/dispatch/trunk/tests/system_tests_management.py Fri Dec  5 15:12:53 2014
@@ -24,6 +24,8 @@ from qpid_dispatch.management import Nod
 from system_test import Qdrouterd, message, retry
 
 DISPATCH = 'org.apache.qpid.dispatch'
+CONFIGURATION = DISPATCH + '.configurationEntity'
+OPERATIONAL = DISPATCH + '.operationalEntity'
 LISTENER = DISPATCH + '.listener'
 CONNECTOR = DISPATCH + '.connector'
 FIXED_ADDRESS = DISPATCH + '.fixedAddress'
@@ -335,9 +337,14 @@ class ManagementTest(system_test.TestCas
 
     def test_get_types(self):
         types = self.node.get_types()
-        self.assertIn('org.apache.qpid.dispatch.listener', types)
-        self.assertIn('org.apache.qpid.dispatch.waypoint', types)
-        self.assertIn('org.apache.qpid.dispatch.router.link', types)
+        self.assertIn(CONFIGURATION, types[LISTENER])
+        self.assertIn(WAYPOINT, types)
+        self.assertIn(OPERATIONAL, types[LINK])
+
+    def test_get_attributes(self):
+        types = self.node.get_attributes()
+        self.assertIn(SSL_PROFILE, types[CONNECTOR])
+        self.assertEqual([], types[WAYPOINT])
 
     def test_get_operations(self):
         result = self.node.get_operations(type=DUMMY)

Modified: qpid/dispatch/trunk/tools/qdmanage
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tools/qdmanage?rev=1643319&r1=1643318&r2=1643319&view=diff
==============================================================================
--- qpid/dispatch/trunk/tools/qdmanage (original)
+++ qpid/dispatch/trunk/tools/qdmanage Fri Dec  5 15:12:53 2014
@@ -186,11 +186,18 @@ For help with a command: %s <command> --
     @commands
     def get_types(self):
         self.setup(
-            "Get the list of entity types. Prints a JSON list of type names.",
+            "Get the list of entity types with they types they extend. Prints a JSON map of type names with base types",
             [('type', 'string', 'Restrict types implementing <type>.')])
         self.print_json(self.call_node('get_types'))
 
     @commands
+    def get_annotations(self):
+        self.setup(
+            "Get the list of entity types with the annotations they include. Prints a JSON map of type names and annotations.",
+            [('type', 'string', 'Restrict types implementing <type>.')])
+        self.print_json(self.call_node('get_annotations'))
+
+    @commands
     def get_attributes(self):
         self.setup(
             "Get the list of entity attribute names. Prints a JSON list of attribute names.",



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


Mime
View raw message