Author: aconway
Date: Mon Jun 2 19:26:53 2014
New Revision: 1599320
URL: http://svn.apache.org/r1599320
Log:
DISPATCH-56: Generate qdrouterd.conf man file from management schema.
Move documentation from qdrouterd.conf.5.in to the qdrouter.json schema,
generate
the man file from the schema.
Added:
qpid/dispatch/trunk/doc/man/CMakeLists.txt (with props)
qpid/dispatch/trunk/doc/man/qdrouterd_man.py (with props)
Modified:
qpid/dispatch/trunk/doc/CMakeLists.txt
qpid/dispatch/trunk/doc/api/CMakeLists.txt
qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.json
qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py
qpid/dispatch/trunk/tests/CMakeLists.txt
qpid/dispatch/trunk/tests/management/schema.py
Modified: qpid/dispatch/trunk/doc/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/doc/CMakeLists.txt?rev=1599320&r1=1599319&r2=1599320&view=diff
==============================================================================
--- qpid/dispatch/trunk/doc/CMakeLists.txt (original)
+++ qpid/dispatch/trunk/doc/CMakeLists.txt Mon Jun 2 19:26:53 2014
@@ -6,9 +6,9 @@
## to you under the Apache License, Version 2.0 (the
## "License"); you may not use this file except in compliance
## with the License. You may obtain a copy of the License at
-##
+##
## http://www.apache.org/licenses/LICENSE-2.0
-##
+##
## Unless required by applicable law or agreed to in writing,
## software distributed under the License is distributed on an
## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -17,12 +17,5 @@
## under the License.
##
-configure_file(man/qdrouterd.8.in ${CMAKE_CURRENT_BINARY_DIR}/man/qdrouterd.8)
-configure_file(man/qdrouterd.conf.5.in
${CMAKE_CURRENT_BINARY_DIR}/man/qdrouterd.conf.5)
-configure_file(man/qdstat.8.in ${CMAKE_CURRENT_BINARY_DIR}/man/qdstat.8)
-
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/qdrouterd.8 DESTINATION
${MAN_INSTALL_DIR}/man8)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/qdrouterd.conf.5 DESTINATION
${MAN_INSTALL_DIR}/man5)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/man/qdstat.8 DESTINATION
${MAN_INSTALL_DIR}/man8)
-
add_subdirectory(api)
+add_subdirectory(man)
Modified: qpid/dispatch/trunk/doc/api/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/doc/api/CMakeLists.txt?rev=1599320&r1=1599319&r2=1599320&view=diff
==============================================================================
--- qpid/dispatch/trunk/doc/api/CMakeLists.txt (original)
+++ qpid/dispatch/trunk/doc/api/CMakeLists.txt Mon Jun 2 19:26:53 2014
@@ -47,5 +47,4 @@ if (BUILD_DOCS)
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.in ${API_SOURCES})
add_custom_target(apidocs ALL DEPENDS user dev)
-
endif (BUILD_DOCS)
Added: qpid/dispatch/trunk/doc/man/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/doc/man/CMakeLists.txt?rev=1599320&view=auto
==============================================================================
--- qpid/dispatch/trunk/doc/man/CMakeLists.txt (added)
+++ qpid/dispatch/trunk/doc/man/CMakeLists.txt Mon Jun 2 19:26:53 2014
@@ -0,0 +1,42 @@
+##
+## 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.
+##
+
+configure_file(qdrouterd.8.in ${CMAKE_CURRENT_BINARY_DIR}/qdrouterd.8)
+configure_file(qdrouterd.conf.5.in
${CMAKE_CURRENT_BINARY_DIR}/qdrouterd.conf.5)
+configure_file(qdstat.8.in ${CMAKE_CURRENT_BINARY_DIR}/qdstat.8)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qdrouterd.8 DESTINATION
${MAN_INSTALL_DIR}/man8)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qdrouterd.conf.5 DESTINATION
${MAN_INSTALL_DIR}/man5)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qdstat.8 DESTINATION
${MAN_INSTALL_DIR}/man8)
+
+# Generate a man page from the new qdrouter.json schema. This will eventually
replace
+# the hand-written man page generated from qdrouterd.conf.5.in
+
+file (GLOB_RECURSE MAN_PAGE_GENERATOR
+ ${CMAKE_CURRENT_SOURCE_DIR}/qdrouterd_man.py
+ ${CMAKE_SOURCE_DIR}/python/qpid_router_internal/management/*.py
+ ${CMAKE_SOURCE_DIR}/python/qpid_router_internal/management/qdrouterd.json)
+
+set (QDROUTERD_MAN qdrouterdconf.conf.5.new)
+
+add_custom_command (OUTPUT ${QDROUTERD_MAN}
+ COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qdrouterd_man.py
${QDROUTERD_MAN}
+ DEPENDS ${MAN_PAGE_GENERATOR})
+
+add_custom_target(man ALL DEPENDS ${QDROUTERD_MAN})
Propchange: qpid/dispatch/trunk/doc/man/CMakeLists.txt
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: qpid/dispatch/trunk/doc/man/CMakeLists.txt
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: qpid/dispatch/trunk/doc/man/qdrouterd_man.py
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/doc/man/qdrouterd_man.py?rev=1599320&view=auto
==============================================================================
--- qpid/dispatch/trunk/doc/man/qdrouterd_man.py (added)
+++ qpid/dispatch/trunk/doc/man/qdrouterd_man.py Mon Jun 2 19:26:53 2014
@@ -0,0 +1,113 @@
+##
+## 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
+##
+
+"""
+Generate the qdrouterd.conf man page from the qdrouterd management schema."""
+
+import sys
+from qpid_dispatch_internal.management.qdrouter import SCHEMA
+
+def make_man_page(filename):
+ """Generate a man page for the configuration file from L{SCHEMA}
descriptions"""
+ with open(filename, 'w') as f:
+
+ f.write(
+ r""".\" -*- nroff -*-
+.\"
+.\" 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
+.\"
+.TH QDROUTERD.CONF 5
+.SH NAME
+qdrouterd.conf \- Configuration file for the Qpid Dispatch router
+.SH DESCRIPTION
+
+The dispatch router is configured in terms of configuration "entities". Each
+type of entity has a set of associated attributes. For example there is a
single
+"router" entity that has attributes to set configuration associated with the
+router as a whole. There may be multiple "listener" and "connector" entities
+that specify how to make and receive external connections.
+
+Some entities have attributes in common, for example "listener" and "connector"
+entities both have attributes to specify an IP address. All entities have
"name"
+and "identity" attributes. Commonly used attribute groups are specified as an
+"include group" and referenced from entity types that want to include them.
+
+
+.SH SYNTAX
+
+This file is divided into sections. "Include" sections define groups of
+attributes that are included by multiple entity types. "Entity" sections define
+the attributes associated with each type of configuration entity.
+
+.nf
+<section-name> {
+ <attribute-name>: <attribute-value>
+ <attribute-name>: <attribute-value>
+ ...
+}
+
+<section-name> { ...
+.fi
+
+.SH SECTIONS
+""")
+ def write_attribute(attr, attrs):
+ if attr.include and attr.include != attrs:
+ return # Don't repeat included attributes
+ f.write('.IP %s\n'%(attr.name))
+ f.write('(%s)\n\n'%(', '.join(
+ filter(None, [str(attr.atype),
+ attr.required and "required",
+ attr.unique and "unique",
+ attr.default and "default=%s"%attr.default]))))
+ if attr.description: f.write("%s\n"%attr.description)
+
+ def write_attributes(attrs):
+ if attrs.description:
+ f.write('\n%s\n'%attrs.description)
+ for attr in attrs.attributes.itervalues():
+ write_attribute(attr, attrs)
+
+ for include in SCHEMA.includes.itervalues():
+ f.write('.SS "\'%s\' include group"\n'% include.name)
+ write_attributes(include)
+
+ for name, entity_type in SCHEMA.entity_types.iteritems():
+ f.write('.SS "\'%s\' entity"\n'% name)
+ f.write('Includes: %s\n\n'%(', '.join(entity_type.include)))
+ write_attributes(entity_type)
+
+
+if __name__ == '__main__':
+ make_man_page(sys.argv[1])
Propchange: qpid/dispatch/trunk/doc/man/qdrouterd_man.py
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.json
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.json?rev=1599320&r1=1599319&r2=1599320&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.json
(original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/qdrouter.json
Mon Jun 2 19:26:53 2014
@@ -1,80 +1,289 @@
{
"prefix": "org.apache.qpid.dispatch",
-
"includes": {
"entity-id": {
- "name": {"type":"String", "required":true, "unique":true},
- "identity": {"type":"String", "required":true, "unique":true}
+ "description":"Name and identity attributes common to all entity types",
+ "attributes": {
+ "name": {
+ "type": "String",
+ "required": true,
+ "unique": true,
+ "description": "Unique name, can be changed."
+ },
+ "identity": {
+ "type": "String",
+ "required": true,
+ "unique": true,
+ "description": "Unique identity, will not change."
+ }
+ }
},
-
"ssl-profile": {
- "cert-db" : {"type":"String"},
- "cert-file" : {"type":"String"},
- "key-file" : {"type":"String"},
- "password-file" : {"type":"String"},
- "password" : {"type":"String"}
+ "description": "SSL profile to be referenced in listeners (for incoming
connections) or connectors (for outgoing connectors).",
+ "attributes": {
+ "cert-db": {
+ "type": "String",
+ "description": "The path to the database that contains the public
certificates of trusted certificate authorities (CAs). "
+ },
+ "cert-file": {
+ "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. "
+ },
+ "key-file": {
+ "type": "String",
+ "description": "The path to the file containing the PEM-formatted
private key for the above certificate. "
+ },
+ "password-file": {
+ "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.
"
+ },
+ "password": {
+ "type": "String",
+ "description": "An alternative to storing the password in a file
referenced by password-file 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 password-file in the same
profile. "
+ }
+ }
},
-
"ip-addr": {
- "addr" : {"type":"String", "default":"0.0.0.0"},
- "port" : {"type":"String", "default":"amqp"}
+ "description": "IP address to be referenced in listeners (for incoming
connections) or connectors (for outgoing connectors).",
+ "attributes": {
+ "addr": {
+ "description":"IP address: ipv4 or ipv6 literal or a host name",
+ "type": "String",
+ "default": "0.0.0.0"
+ },
+ "port": {
+ "description":"Port number or symbolic service name",
+ "type": "String",
+ "default": "amqp"
+ }
+ }
+ },
+ "connection": {
+ "description": "Common connection attributes for listeners and
connectors.",
+ "attributes": {
+ "sasl-mechanisms": {
+ "type": "String",
+ "required": true,
+ "description": "Comma separated list of accepted SASL mechanisms."
+ },
+ "role": {
+ "type": [
+ "normal",
+ "inter-router",
+ "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 inter-router connections.
"
+ }
+ }
}
},
-
"entity_types": {
"container": {
"singleton": true,
- "include" : ["entity-id"],
+ "include": [
+ "entity-id"
+ ],
"attributes": {
- "worker-threads" : {"type":"Integer", "default":"1"}
+ "worker-threads": {
+ "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.) "
+ }
}
},
-
+
"router": {
"singleton": true,
- "include" : ["entity-id"],
+ "include": [
+ "entity-id"
+ ],
"attributes": {
- "mode" : {"type": ["standalone", "interior"], "default":"standalone"},
- "area" : {"type": "String"},
- "hello-interval" : {"type": "Integer", "default": 1},
- "hello-max-age" : {"type": "Integer", "default": 3},
- "ra-Integererval" : {"type": "Integer", "default": 30},
- "remote-ls-max-age" : {"type": "Integer", "default": 60},
- "mobile-addr-max-age" : {"type": "Integer", "default": 60}
+ "mode": {
+ "type": [
+ "standalone",
+ "interior",
+ "edge"
+ ],
+ "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. "
+ },
+ "area": {
+ "type": "String",
+ "description": ""
+ },
+ "hello-interval": {
+ "type": "Integer",
+ "default": 1,
+ "description": ""
+ },
+ "hello-max-age": {
+ "type": "Integer",
+ "default": 3,
+ "description": ""
+ },
+ "ra-interval": {
+ "type": "Integer",
+ "default": 30,
+ "description": ""
+ },
+ "remote-ls-max-age": {
+ "type": "Integer",
+ "default": 60,
+ "description": ""
+ },
+ "mobile-addr-max-age": {
+ "type": "Integer",
+ "default": 60,
+ "description": ""
+ }
}
},
-
+
"listener": {
- "include" : ["entity-id", "ssl-profile", "ip-addr"],
+ "include": [
+ "entity-id",
+ "ssl-profile",
+ "ip-addr",
+ "connection"
+ ],
"attributes": {
- "role" : {"type":["normal", "inter-router"], "default":"normal"},
- "sasl-mechanisms" : {"type":"String", "required":true},
- "require-peer-auth" : {"type": "Boolean", "default":true},
- "trusted-certs" : {"type": "String"},
- "allow-unsecured" : {"type": "Boolean", "default":false},
- "max-frame-size" : {"type": "Integer", "default":65536}
+ "require-peer-auth": {
+ "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. "
+ },
+ "trusted-certs": {
+ "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. "
+ },
+ "allow-unsecured": {
+ "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. "
+ },
+ "max-frame-size": {
+ "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. "
+ }
}
},
-
"connector": {
- "include" : ["entity-id", "ssl-profile", "ip-addr"],
+ "include": [
+ "entity-id",
+ "ssl-profile",
+ "ip-addr",
+ "connection"
+ ],
"attributes": {
- "role" : {"type": ["normal", "inter-router", "on-demand"],
"default":"normal"},
- "sasl-mechanisms" : {"type":"String", "required":true},
- "allow-redirect" : {"type": "Boolean", "default":true},
- "max-frame-size" : {"type": "Integer", "default":65536}
+ "allow-redirect": {
+ "type": "Boolean",
+ "default": true,
+ "description": ""
+ },
+ "max-frame-size": {
+ "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. "
+ }
}
},
"logging": {
- "include": ["entity-id"],
+ "include": [
+ "entity-id"
+ ],
+ "attributes": {
+ "module": {
+ "type":[
+ "router",
+ "message",
+ "server",
+ "agent",
+ "container",
+ "config",
+ "default"
+ ],
+ "description": "Module to configure logging level. The special module
'default' specifies logging for modules that don't have explicit log sections."
+ },
+ "level": {
+ "type": [
+ "none",
+ "trace",
+ "debug",
+ "info",
+ "notice",
+ "warning",
+ "error",
+ "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. "
+ },
+ "timestamp": {
+ "type": "Boolean",
+ "default": true,
+ "description": "Set a timestamp on log messages"
+ },
+ "output": {
+ "type": "String",
+ "description": "Where to send log messages. Can be 'stderr',
'syslog' or a file name. "
+ }
+ }
+ },
+
+ "fixed-address": {
+ "include": [
+ "entity-id"
+ ],
"attributes": {
- "level" : {"type": ["none", "trace", "debug", "info", "notice",
"warning", "error", "critical"], "default":"info"},
- "timestamp" : {"type": "Boolean"},
- "output" : {"type": "String"}
+ "prefix": {
+ "type": "String",
+ "required": true,
+ "description": "The address prefix (always starting with \"/\"). "
+ },
+ "phase": {
+ "type": "Integer",
+ "description": ""
+ },
+ "fanout": {
+ "type": [
+ "multiple",
+ "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. "
+ },
+ "bias": {
+ "type": [
+ "closest",
+ "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. "
+ }
+ }
+ },
+
+ "waypoint": {
+ "include": [
+ "entity-id"
+ ],
+ "attributes": {
+ "in-phase": {
+ "type": "Integer",
+ "default": -1
+ },
+ "out-phase": {
+ "type": "Integer",
+ "default": -1
+ },
+ "connector": {
+ "type": "String",
+ "required": true
+ }
}
}
}
}
-
-
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=1599320&r1=1599319&r2=1599320&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py
(original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py Mon
Jun 2 19:26:53 2014
@@ -1,4 +1,4 @@
-##*
+##
## 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
@@ -27,6 +27,7 @@ A Schema can be loaded/dumped to a json
"""
import os
+from collections import OrderedDict
class SchemaError(Exception):
"""Class for schema errors"""
@@ -64,7 +65,8 @@ class Type(object):
"""
return self.name
- def __repr__(self):
+ def __str__(self):
+ """String name of type."""
return str(self.dump())
class BooleanType(Type):
@@ -131,6 +133,10 @@ class EnumType(Type):
"""
return self.tags
+ def __str__(self):
+ """String description of enum type."""
+ return "One of [%s]"%(', '.join(self.tags))
+
BUILTIN_TYPES = dict((t.name, t) for t in [Type("String", str),
Type("Integer", int), BooleanType()])
def get_type(rep):
@@ -149,7 +155,7 @@ def _dump_dict(items):
Remove all items with None value from a mapping.
@return: Map of non-None items.
"""
- return dict((k, v) for k, v in items if v)
+ return OrderedDict((k, v) for k, v in items if v)
def _is_unique(found, category, value):
"""
@@ -173,7 +179,7 @@ def _is_unique(found, category, value):
return True
-class AttributeDef(object):
+class AttributeType(object):
"""
Definition of an attribute.
@@ -182,19 +188,25 @@ class AttributeDef(object):
@ivar required: True if the attribute is reqiured.
@ivar default: Default value for the attribute or None if no default.
@ivar unique: True if the attribute value is unique.
+ @ivar description: Description of the attribute type.
+ @ivar include: Include section or None
"""
- def __init__(self, name, type=None, default=None, required=False,
unique=False): # pylint: disable=redefined-builtin
+ def __init__(self, name, type=None, default=None, required=False,
unique=False, include=None,
+ description=""
+ ): # pylint: disable=redefined-builtin
"""
- See L{AttributeDef} instance variables.
+ See L{AttributeType} instance variables.
"""
self.name = name
self.atype = get_type(type)
self.required = required
self.default = default
self.unique = unique
+ self.description = description
if default is not None:
self.default = self.atype.validate(default)
+ self.include = include
def validate(self, value, check_required=True, add_default=True,
check_unique=None, **kwargs):
"""
@@ -223,52 +235,81 @@ class AttributeDef(object):
@return: Json-friendly representation of an attribute type
"""
return _dump_dict([
- ('type', self.atype.dump()), ('default', self.default),
('required', self.required)])
+ ('type', self.atype.dump()),
+ ('default', self.default),
+ ('required', self.required),
+ ('unique', self.unique),
+ ('description', self.description)
+ ])
def __str__(self):
- return "AttributeDef%s"%(self.__dict__)
+ return "AttributeType%s"%(self.__dict__)
+
+class AttributeTypeHolder(object):
+ """Base class for IncludeType and EntityType - a named holder of attribute
types"""
-class EntityType(object):
+ def __init__(self, name, schema, attributes=None, description=""):
+ self.name, self.schema, self.description = name, schema, description
+ self.attributes = OrderedDict()
+ if attributes:
+ self.add_attributes(attributes)
+
+ def add_attributes(self, attributes):
+ """
+ Add attributes.
+ @param attributes: Map of attributes {name: {type:, default:,
required:, unique:}}
+ """
+ for k, v in attributes.iteritems():
+ if k in self.attributes:
+ raise SchemaError("Attribute '%s' duplicated in '%s'"%(k,
self.name))
+ self.attributes[k] = AttributeType(k, **v)
+
+ def dump(self):
+ """Json friendly representation"""
+ return _dump_dict([
+ ('attributes', OrderedDict((k, v.dump()) for k, v in
self.attributes.iteritems())),
+ ('description', self.description or None)
+ ])
+
+class IncludeType(AttributeTypeHolder):
+
+ def __init__(self, name, schema, attributes=None, description=""):
+ super(IncludeType, self).__init__(name, schema, attributes,
description)
+ for a in self.attributes.itervalues():
+ a.include = self
+
+class EntityType(AttributeTypeHolder):
"""
An entity type defines a set of attributes for an entity.
@ivar name: Entity type name.
- @ivar attributes: Map of L{AttributeDef} for entity.
+ @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.
"""
- def __init__(self, name, schema, singleton=False, include=None,
attributes=None):
+ def __init__(self, name, schema, singleton=False, include=None,
attributes=None,
+ description=""):
"""
@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 attributes: Map of attributes {name: {type:, default:,
required:, unique:}}
+ @param description: Human readable description.
"""
- self.name = name
- self.schema = schema
+ super(EntityType, self).__init__(name, schema, attributes, description)
self.singleton = singleton
- self.attributes = {}
- if attributes:
- self.add_attributes(attributes)
+ self.include = include
if include and self.schema.includes:
for i in include:
- self.add_attributes(schema.includes[i])
-
- def add_attributes(self, attributes):
- """
- Add attributes.
- @param attributes: Map of attributes {name: {type:, default:,
required:, unique:}}
- """
- for k, v in attributes.iteritems():
- if k in self.attributes:
- raise SchemaError("Attribute '%s' duplicated in '%s'"%(k,
self.name))
- self.attributes[k] = AttributeDef(k, **v)
+ for attr in schema.includes[i].attributes.itervalues():
+ self.attributes[attr.name] = attr
def dump(self):
"""Json friendly representation"""
- return _dump_dict([
- ('singleton', self.singleton),
- ('attributes', dict((k, v.dump()) for k, v in
self.attributes.iteritems()))])
+ d = super(EntityType, self).dump()
+ if self.singleton: d['singleton'] = True
+ return d
def validate(self, attributes, check_singleton=None, **kwargs):
"""
@@ -303,30 +344,26 @@ class Schema(object):
@ivar prefix: Prefix to prepend to short entity names.
@ivar entity_types: Map of L{EntityType} by name.
"""
- def __init__(self, prefix="", includes=None, entity_types=None):
+ def __init__(self, prefix="", includes=None, entity_types=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 description: Human readable description.
"""
self.prefix = self.prefixdot = prefix
if not prefix.endswith('.'):
self.prefixdot += '.'
- self.includes = includes or {}
- self.entity_types = {}
+ self.description = description
+ self.includes = OrderedDict()
+ if includes:
+ for k, v in includes.iteritems():
+ self.includes[k] = IncludeType(k, self, **v)
+ self.entity_types = OrderedDict()
if entity_types:
for k, v in entity_types.iteritems():
- self.add_entity_type(k, **v)
+ self.entity_types[k] = EntityType(k, self, **v)
- def add_entity_type(self, name, singleton=False, include=None,
attributes=None):
- """
- Add an entity type to the schema.
- @param name: Entity type name.
- @param singleton: True if this is a singleton.
- @param include: List of names of include sections for this entity.
- @param attributes: Map of attributes {name: {type:, default:,
required:, unique:}}
- """
- self.entity_types[name] = EntityType(name, self, singleton, include,
attributes)
def short_name(self, name):
"""Remove prefix from name if present"""
@@ -342,9 +379,9 @@ class Schema(object):
def dump(self):
"""Return json-friendly representation"""
- return {'prefix':self.prefix,
- 'includes':self.includes,
- 'entity_types':dict((k, v.dump()) for k, v in
self.entity_types.iteritems())}
+ return {'prefix': self.prefix,
+ 'includes': OrderedDict((k, v.dump()) for k, v in
self.includes.iteritems()),
+ 'entity_types': OrderedDict((k, v.dump()) for k, v in
self.entity_types.iteritems())}
def validate(self, entities, enum_as_int=False, check_required=True,
add_default=True, check_unique=True, check_singleton=True):
"""
Modified: qpid/dispatch/trunk/tests/CMakeLists.txt
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/CMakeLists.txt?rev=1599320&r1=1599319&r2=1599320&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/CMakeLists.txt (original)
+++ qpid/dispatch/trunk/tests/CMakeLists.txt Mon Jun 2 19:26:53 2014
@@ -6,9 +6,9 @@
## to you under the Apache License, Version 2.0 (the
## "License"); you may not use this file except in compliance
## with the License. You may obtain a copy of the License at
-##
+##
## http://www.apache.org/licenses/LICENSE-2.0
-##
+##
## Unless required by applicable law or agreed to in writing,
## software distributed under the License is distributed on an
## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -52,6 +52,7 @@ add_test(unit_tests_size_2 unit_test
add_test(unit_tests_size_1 unit_tests_size 1)
add_test(unit_tests unit_tests
${CMAKE_CURRENT_SOURCE_DIR}/threads4.conf)
add_test(router_tests python
${CMAKE_CURRENT_SOURCE_DIR}/router_engine_test.py -v)
+add_test(management_tests python -m unittest -v management)
set(SYSTEM_TEST_FILES system_test.py system_tests_one_router.py
system_tests_two_routers.py system_tests_broker.py)
@@ -64,7 +65,7 @@ install(FILES ${SYSTEM_TEST_FILES}
DESTINATION ${QPID_DISPATCH_HOME_INSTALLED}/tests
)
-install(FILES qdstat_test.sh
+install(FILES qdstat_test.sh
DESTINATION ${QPID_DISPATCH_HOME_INSTALLED}/tests
)
Modified: qpid/dispatch/trunk/tests/management/schema.py
URL:
http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/management/schema.py?rev=1599320&r1=1599319&r2=1599320&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/management/schema.py (original)
+++ qpid/dispatch/trunk/tests/management/schema.py Mon Jun 2 19:26:53 2014
@@ -21,15 +21,25 @@
#pylint: disable=wildcard-import,missing-docstring,too-many-public-methods
import unittest, json
-from qpid_dispatch_internal.management.schema import Schema, EntityType,
BooleanType, EnumType, AttributeDef, SchemaError, schema_file
+from qpid_dispatch_internal.management.schema import Schema, EntityType,
BooleanType, EnumType, AttributeType, SchemaError, schema_file
from qpid_dispatch_internal.management.entity import Entity
+import collections
+
+def replace_od(thing):
+ if isinstance(thing, collections.Mapping):
+ return dict((k, replace_od(v)) for k,v in thing.iteritems())
+ if isinstance(thing, list):
+ return [replace_od(t) for t in thing]
+ return thing
SCHEMA_1 = {
"prefix":"org.example",
"includes": {
"entity-id": {
- "name": {"type":"String", "required": True, "unique":True}
- },
+ "attributes": {
+ "name": {"type":"String", "required": True, "unique":True}
+ }
+ }
},
"entity_types": {
"container": {
@@ -54,7 +64,6 @@ SCHEMA_1 = {
}
}
-
class SchemaTest(unittest.TestCase):
def test_bool(self):
@@ -75,18 +84,18 @@ class SchemaTest(unittest.TestCase):
self.assertRaises(ValueError, e.validate, 3)
def test_attribute_def(self):
- a = AttributeDef('foo', 'String', 'FOO', False)
+ a = AttributeType('foo', 'String', 'FOO', False)
self.assertEqual(a.validate('x'), 'x')
self.assertEqual(a.validate(None), 'FOO')
- a = AttributeDef('foo', 'String', 'FOO', True)
+ a = AttributeType('foo', 'String', 'FOO', True)
self.assertEqual('FOO', a.validate(None))
- a = AttributeDef('foo', 'Integer', None, True)
+ a = AttributeType('foo', 'Integer', None, True)
self.assertRaises(SchemaError, a.validate, None) # Missing default
def test_entity_type(self):
s = Schema(includes={
- 'i1':{'foo1': {'type':'String', 'default':'FOO1'}},
- 'i2':{'foo2': {'type':'String', 'default':'FOO2'}}})
+ 'i1':{'attributes': { 'foo1': {'type':'String',
'default':'FOO1'}}},
+ 'i2':{'attributes': { 'foo2': {'type':'String',
'default':'FOO2'}}}})
e = EntityType('MyEntity', s, attributes={
'foo': {'type':'String', 'default':'FOO'},
@@ -110,32 +119,46 @@ class SchemaTest(unittest.TestCase):
def test_schema_dump(self):
s = Schema(**SCHEMA_1)
self.maxDiff = None # pylint: disable=invalid-name
+ self.longMessage = True # pylint: disable=invalid-name
expect = {
"prefix":"org.example",
- "includes": {"entity-id": {"name": {"required": True,
"unique":True, "type": "String"}}},
+
+ "includes": {
+ "entity-id": {
+ "attributes": {
+ "name": {"required": True,
+ "unique":True,
+ "type": "String"}
+ }
+ }
+ },
+
"entity_types": {
"container": {
"singleton": True,
"attributes": {
- "name": {"type":"String", "required": True},
+ "name": {"type":"String", "unique":True, "required":
True},
"worker-threads": {"type":"Integer", "default": 1}
}
},
"listener": {
"attributes": {
- "name": {"type":"String", "required": True},
+ "name": {"type":"String", "unique":True,
"required": True},
"addr" : {"type":"String"}
}
},
"connector": {
"attributes": {
- "name": {"type":"String", "required": True},
+ "name": {"type":"String", "unique":True, "required":
True},
"addr" : {"type":"String"}
}
}
}
}
- self.assertEquals(s.dump(), expect)
+ def jsontof(j,fname):
+ with open(fname,'w') as f:
+ json.dump(j, f, indent=4)
+ self.assertDictEqual(replace_od(s.dump()), expect)
s2 = Schema(**s.dump())
self.assertEqual(s.dump(), s2.dump())
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]