qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From acon...@apache.org
Subject svn commit: r1655398 - in /qpid/dispatch/trunk: python/qpid_dispatch/management/ python/qpid_dispatch_internal/compat/ python/qpid_dispatch_internal/management/ python/qpid_dispatch_internal/tools/ tests/ tools/
Date Wed, 28 Jan 2015 17:30:40 GMT
Author: aconway
Date: Wed Jan 28 17:30:40 2015
New Revision: 1655398

URL: http://svn.apache.org/r1655398
Log:
DISPATCH-103: Add management entity providng get-schema operations.

- Add org.amqp.management to qdrouter schema.
- Add org.apache.qpid.dispatch extending org.amq.management with get-schema operations
- Make org.amqp.management a regulary entity class, move standard mgmt ops Agent to ManagementEntity.
- Add tests for get-schema operations.

Modified:
    qpid/dispatch/trunk/python/qpid_dispatch/management/client.py
    qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json
    qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json.readme.txt
    qpid/dispatch/trunk/python/qpid_dispatch_internal/compat/__init__.py
    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/schema.py
    qpid/dispatch/trunk/python/qpid_dispatch_internal/tools/command.py
    qpid/dispatch/trunk/tests/system_tests_management.py
    qpid/dispatch/trunk/tests/system_tests_qdmanage.py
    qpid/dispatch/trunk/tools/qdmanage

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=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/client.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/client.py Wed Jan 28 17:30:40 2015
@@ -22,7 +22,7 @@ AMQP management client for Qpid dispatch
 """
 
 import qpid_dispatch_site
-import proton, threading
+import proton
 from proton import Url
 from .error import *
 from .entity import EntityBase, clean_dict
@@ -92,7 +92,7 @@ class Node(object):
                 self.url.path = '_topo/0/%s/$management' % router
             else:
                 self.url.path = '$management'
-        connection=connection or BlockingConnection(url, timeout)
+        connection = connection or BlockingConnection(url, timeout)
         self.client = SyncRequestResponse(connection, self.url.path)
         self.reply_to = self.client.reply_to
 
@@ -281,6 +281,6 @@ class Node(object):
     def get_operations(self, type=None):
         return self.call(self.node_request(operation="GET-OPERATIONS", entityType=type)).body
 
-    def get_mgmt_nodes(self):
-        return self.call(self.node_request(operation="GET-MGMT-NODES")).body
+    def get_mgmt_nodes(self, type=None):
+        return self.call(self.node_request(operation="GET-MGMT-NODES", 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=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json Wed Jan 28 17:30:40
2015
@@ -100,6 +100,20 @@
             }
         },
 
+        "org.amqp.management": {
+            "description": "The standard AMQP management node interface.",
+            "fullName": true,
+            "extends": "entity",
+            "operations": ["QUERY", "GET-TYPES", "GET-ANNOTATIONS", "GET-OPERATIONS", "GET-ATTRIBUTES",
"GET-MGMT-NODES"]
+        },
+
+        "management": {
+            "description": "Extends the standard org.amqp.management node interface.",
+            "extends": "org.amqp.management",
+            "singleton": true,
+            "operations": ["CREATE", "GET-SCHEMA", "GET-JSON-SCHEMA"]
+        },
+
         "configurationEntity": {
             "description": "Base type for entities containing configuration information.",
             "extends": "entity",

Modified: qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json.readme.txt
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json.readme.txt?rev=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json.readme.txt (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json.readme.txt Wed Jan 28
17:30:40 2015
@@ -35,6 +35,7 @@ Annotation and entity type definition ma
 - "description": documentation string.
 - "operations": list of allowed operation names.
 - "attributes": map of attribute names to attribute definitions (see below)
+- "fullName": if true use the type name as-is, do not apply the schema prefix.
 
 Entity type definitions also have these fields:
 

Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/compat/__init__.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/compat/__init__.py?rev=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/compat/__init__.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/compat/__init__.py Wed Jan 28 17:30:40
2015
@@ -30,3 +30,10 @@ if sys.version_info >= (2, 7):
     JSON_LOAD_KWARGS = {'object_pairs_hook':OrderedDict}
 else:
     JSON_LOAD_KWARGS = {}
+
+def dictify(od):
+    """Recursively replace OrderedDict with dict"""
+    if isinstance(od, OrderedDict):
+        return dict((k, dictify(v)) for k, v in od.iteritems())
+    else:
+        return od

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=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py Wed Jan 28 17:30:40
2015
@@ -48,7 +48,7 @@ Adding/removing/updating entities from C
   4. unlocks the router.
 """
 
-import traceback, sys
+import traceback, json
 from itertools import ifilter, chain
 from traceback import format_exc
 from threading import Lock
@@ -57,7 +57,6 @@ from dispatch import IoAdapter, LogAdapt
 from qpid_dispatch.management.error import ManagementError, OK, CREATED, NO_CONTENT, STATUS_TEXT,
\
     BadRequestStatus, InternalServerErrorStatus, NotImplementedStatus, NotFoundStatus
 from qpid_dispatch.management.entity import camelcase
-from .. import dispatch_c
 from .schema import ValidationError, SchemaEntity, EntityType
 from .qdrouter import QdSchema
 from ..router.message import Message
@@ -110,9 +109,10 @@ class AgentEntity(SchemaEntity):
     def validate(self):
         # Set default identity and name if not already set.
         prefix = self.entity_type.short_name + "/"
-        if self.attributes.get('identity') is None:
+        identity = self.attributes.get('identity')
+        if identity is None:
             self.attributes['identity'] = prefix + str(self._identifier())
-        elif not self.attributes['identity'].startswith(prefix):
+        elif not identity.startswith(prefix) and identity != 'self':
             self.attributes['identity'] = prefix + self.attributes['identity']
         self.attributes.setdefault('name', self.attributes['identity'])
         super(AgentEntity, self).validate()
@@ -198,7 +198,7 @@ class RouterEntity(AgentEntity):
 
 class LogEntity(AgentEntity):
     def __init__(self, agent, entity_type, attributes=None, validate=True):
-        # Special defaults for DEFAULT module. 
+        # Special defaults for DEFAULT module.
         if attributes.get("module") == "DEFAULT":
             defaults = dict(enable="info+", timestamp=True, source=False, output="stderr")
             attributes = dict(defaults, **attributes)
@@ -277,6 +277,7 @@ class ConnectionEntity(CEntity): pass
 
 class AllocatorEntity(CEntity): pass
 
+
 class EntityCache(object):
     """
     Searchable cache of entities, can be refreshd from C attributes.
@@ -369,18 +370,102 @@ class EntityCache(object):
             self.qd.qd_entity_refresh_end()
             self.qd.qd_dispatch_router_unlock(self.agent.dispatch)
 
+class ManagementEntity(AgentEntity):
+    """An entity representing the agent itself. It is a singleton created by the agent."""
+
+    def __init__(self, agent, entity_type, attributes, validate=True):
+        attributes = {"identity": "self", "name": "self"}
+        super(ManagementEntity, self).__init__(agent, entity_type, attributes, validate=validate)
+        self.__dict__["_schema"] = entity_type.schema
+
+    def requested_type(self, request):
+        type = request.properties.get('entityType')
+        if type: return self._schema.entity_type(type)
+        else: return None
+
+    def query(self, request):
+        """Management node query operation"""
+        entity_type = self.requested_type(request)
+        if entity_type:
+            all_attrs = set(entity_type.attributes.keys())
+        else:
+            all_attrs = self._schema.all_attributes
+
+        names = set(request.body.get('attributeNames'))
+        if names:
+            unknown = names - all_attrs
+            if unknown:
+                if entity_type:
+                    for_type = " for type %s" % entity_type.name
+                else:
+                    for_type = ""
+                raise NotFoundStatus("Unknown attributes %s%s." % (list(unknown), for_type))
+        else:
+            names = all_attrs
+
+        results = []
+        def add_result(entity):
+            result = []
+            non_empty = False
+            for name in names:
+                result.append(entity.attributes.get(name))
+                if result[-1] is not None: non_empty = True
+            if non_empty: results.append(result)
+
+        self._agent.entities.map_type(add_result, entity_type)
+        return (OK, {'attributeNames': list(names), 'results': results})
+
+    def get_types(self, request):
+        type = self.requested_type(request)
+        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)
+        return (OK, dict((t, et.operations)
+                         for t, et in self._schema.entity_types.iteritems()
+                         if not type or type.name == t))
+
+    def get_attributes(self, request):
+        type = self.requested_type(request)
+        return (OK, dict((t, [a for a in et.attributes])
+                         for t, et in self._schema.entity_types.iteritems()
+                         if not type or type.name == t))
+
+    def get_mgmt_nodes(self, request):
+        router = self._agent.entities.map_type(None, 'router')[0]
+        area = router.attributes['area']
+        def node_address(node):
+            return "amqp:/_topo/%s/%s/$management" % (area, node.attributes['addr'][1:])
+        return (OK, self._agent.entities.map_type(node_address, 'router.node'))
+
+
+    def get_schema(self, request):
+        return (OK, self._schema.dump())
+
+    def get_json_schema(self, request):
+        indent = request.properties.get("indent")
+        if indent is not None: indent = int(indent)
+        return (OK, json.dumps(self._schema.dump(), indent=indent))
+
+
 class Agent(object):
-    """AMQP managment agent"""
+    """AMQP managment agent. Manages entities, directs requests to the correct entity."""
 
     def __init__(self, dispatch, qd):
         self.qd = qd
         self.dispatch = dispatch
         self.schema = QdSchema()
         self.entities = EntityCache(self)
-        self.name = self.identity = 'self'
-        self.type = 'org.amqp.management' # AMQP management node type
         self.request_lock = Lock()
         self.log_adapter = LogAdapter("AGENT")
+        self.management = self.create_entity({"type": "management"})
+        self.add_entity(self.management)
 
     def log(self, level, text):
         info = traceback.extract_stack(limit=2)[0] # Caller frame info
@@ -458,56 +543,18 @@ class Agent(object):
         @return: (response-code, body)
         """
         operation = required_property('operation', request)
-        type = request.properties.get('type') # Allow absent type for requests with a name.
-        if type == self.type or operation.lower() == 'create':
+        if operation.lower() == 'create':
             # Create requests are entity requests but must be handled by the agent since
             # the entity does not yet exist.
-            target = self
+            return self.create(request)
         else:
             target = self.find_entity(request)
             target.entity_type.allowed(operation)
-        try:
-            method = getattr(target, operation.lower().replace("-", "_"))
-        except AttributeError:
-            not_implemented(operation, target.type)
-        return method(request)
-
-    def requested_type(self, request):
-        type = request.properties.get('entityType')
-        if type: return self.entity_type(type)
-        else: return None
-
-    def query(self, request):
-        """Management node query operation"""
-        entity_type = self.requested_type(request)
-        if entity_type:
-            all_attrs = set(entity_type.attributes.keys())
-        else:
-            all_attrs = self.schema.all_attributes
-
-        names = set(request.body.get('attributeNames'))
-        if names:
-            unknown = names - all_attrs
-            if unknown:
-                if entity_type:
-                    for_type =  " for type %s" % entity_type.name
-                else:
-                    for_type = ""
-                raise NotFoundStatus("Unknown attributes %s%s." % (list(unknown), for_type))
-        else:
-            names = all_attrs
-
-        results = []
-        def add_result(entity):
-            result = []
-            non_empty = False
-            for name in names:
-                result.append(entity.attributes.get(name))
-                if result[-1] is not None: non_empty = True
-            if non_empty: results.append(result)
-
-        self.entities.map_type(add_result, entity_type)
-        return (OK, {'attributeNames': list(names), 'results': results})
+            try:
+                method = getattr(target, operation.lower().replace("-", "_"))
+            except AttributeError:
+                not_implemented(operation, target.type)
+            return method(request)
 
     def create(self, request=None, attributes=None):
         """
@@ -536,39 +583,16 @@ class Agent(object):
 
     def remove(self, entity): self.entities.remove(entity)
 
-    def get_types(self, request):
-        type = self.requested_type(request)
-        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)
-        return (OK, dict((t, et.operations)
-                         for t, et in self.schema.entity_types.iteritems()
-                         if not type or type.name == t))
-
-    def get_attributes(self, request):
-        type = self.requested_type(request)
-        return (OK, dict((t, [a for a in et.attributes])
-                         for t, et in self.schema.entity_types.iteritems()
-                         if not type or type.name == t))
-
-    def get_mgmt_nodes(self, request):
-        router = self.entities.map_type(None, 'router')[0]
-        area = router.attributes['area']
-        def node_address(node):
-            return "amqp:/_topo/%s/%s/$management" % (area, node.attributes['addr'][1:])
-        return (OK, self.entities.map_type(node_address, 'router.node'))
-
-
     def find_entity(self, request):
         """Find the entity addressed by request"""
 
+        requested_type = request.properties.get('type')
+        if requested_type:
+            requested_type = self.schema.entity_type(requested_type)
+            # Special case for management object, allow just type with no name/id
+            if self.management.entity_type.is_a(requested_type):
+                return self.management
+
         # ids is a map of identifying attribute values
         ids = dict((k, request.properties.get(k))
                    for k in ['name', 'identity'] if k in request.properties)
@@ -591,10 +615,10 @@ class Agent(object):
         for k, v in ids.iteritems():
             if entity[k] != v: raise BadRequestStatus("Conflicting %s" % attrvals())
 
-        request_type = request.properties.get('type')
-        if request_type and not entity.entity_type.name_is(request_type):
-            raise NotFoundStatus("Entity type '%s' does match requested type '%s'" %
-                           (entity.entity_type.name, request_type))
+        if requested_type:
+            if not entity.entity_type.is_a(requested_type):
+                raise BadRequestStatus("Entity type '%s' does not extend requested type '%s'"
%
+                                       (entity.entity_type.name, requested_type))
 
         return entity
 

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=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py Wed Jan 28 17:30:40
2015
@@ -21,7 +21,7 @@
 Configuration file parsing
 """
 
-import json, re, sys, time
+import json, re, sys
 from copy import copy
 from qpid_dispatch.management.entity import camelcase
 from .schema import ValidationError
@@ -33,8 +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)]
+        self.config_types = [et for et in schema.entity_types.itervalues()
+                             if schema.is_configuration(et)]
         if filename:
             try:
                 self.load(filename)

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=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/schema.py Wed Jan 28 17:30:40
2015
@@ -26,9 +26,9 @@ check for uniqueness of enties/attribute
 A Schema can be loaded/dumped to a json file.
 """
 
-import sys
+import sys, json
 from qpid_dispatch.management.entity import EntityBase
-from qpid_dispatch.management.error import ForbiddenStatus
+from qpid_dispatch.management.error import NotImplementedStatus
 from ..compat import OrderedDict
 
 class ValidationError(Exception):
@@ -274,9 +274,13 @@ class RedefinedError(ValueError): pass
 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)
+    def __init__(self, name, schema, attributes=None, operations=None, description="", fullName=True):
+        self.schema, self.description = schema, description
+        if fullName:
+            self.name = schema.long_name(name)
+            self.short_name = schema.short_name(name)
+        else:
+            self.name = self.short_name = name
         self.attributes = OrderedDict()
         if attributes: self.add_attributes(attributes)
         self.operations = operations or []
@@ -315,9 +319,8 @@ class AttrsAndOps(object):
 
 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 OrderedDict()
+    def __init__(self, name, schema, **kwargs):
+        super(Annotation, self).__init__(name, schema, **kwargs)
         for a in self.attributes.itervalues():
             a.annotation = self
 
@@ -332,8 +335,7 @@ class EntityType(AttrsAndOps):
     @ivar singleton: If true only one entity of this type is allowed.
     #ivar annotation: List of names of sections annotationd by this entity.
     """
-    def __init__(self, name, schema, singleton=False, annotations=None, attributes=None,
-                 extends=None, description="", operations=None):
+    def __init__(self, name, schema, singleton=False, annotations=None, extends=None, **kwargs):
         """
         @param name: name of the entity type.
         @param schema: schema for this type.
@@ -343,7 +345,7 @@ class EntityType(AttrsAndOps):
         @param description: Human readable description.
         @param operations: Allowed operations, list of operation names.
         """
-        super(EntityType, self).__init__(name, schema, attributes, operations, description)
+        super(EntityType, self).__init__(name, schema, **kwargs)
         # Bases and annotations are resolved in self.resolve
         self.base = extends
         self.all_bases = []
@@ -373,7 +375,7 @@ class EntityType(AttrsAndOps):
             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(',
')))
+                                     % (self.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")
@@ -455,7 +457,7 @@ class EntityType(AttrsAndOps):
         """Raise excepiton if op is not a valid operation on entity."""
         op = op.upper()
         if op not in self.operations:
-            raise ForbiddenStatus("Operation '%s' not allowed for '%s'" % (op, self.name))
+            raise NotImplementedStatus("Operation '%s' not implemented for '%s'" % (op, self.name))
 
     def __repr__(self): return "%s(%s)" % (type(self).__name__, self.name)
 
@@ -493,8 +495,14 @@ class Schema(object):
 
         def add_defs(thing, mymap, defs):
             for k, v in defs.iteritems():
-                t = thing(k, self, **v)
+                try:
+                    t = thing(k, self, **v)
+                except:
+                    ex_type, ex_value, ex_trace = sys.exc_info()
+                    raise ValidationError, "Adding %s%s: %s %s" % (
+                        thing.__name__, json.dumps(v), ex_type.__name__, ex_value), ex_trace
                 mymap[t.name] = t
+
         add_defs(Annotation, self.annotations, annotations or OrderedDict())
         add_defs(EntityType, self.entity_types, entityTypes or OrderedDict())
 
@@ -527,11 +535,10 @@ class Schema(object):
         ])
 
     def _lookup(self, map, name, message, error):
-        try:
-            return map[self.long_name(name)]
-        except KeyError:
-            if error: raise ValidationError(message % name)
-            else: return None
+        found = map.get(name) or map.get(self.long_name(name))
+        if not found and error:
+            raise ValidationError(message % name)
+        return found
 
     def entity_type(self, name, error=True):
         return self._lookup(self.entity_types, name, "No such entity type '%s'", error)
@@ -595,6 +602,7 @@ class Schema(object):
 
     def filter(self, predicate):
         """Return an iterator over entity types that satisfy predicate."""
+        if predicate is None: return self.entity_types.itervalues()
         return (t for t in self.entity_types.itervalues() if predicate(t))
 
     def by_type(self, type):

Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/tools/command.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/tools/command.py?rev=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/tools/command.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/tools/command.py Wed Jan 28 17:30:40
2015
@@ -21,10 +21,9 @@
 Utilities for command-line programs.
 """
 
-import os, sys, json, optparse
+import sys, json, optparse
 from collections import Sequence, Mapping
 from qpid_dispatch_site import VERSION
-from textwrap import fill
 
 class UsageError(Exception):
     """

Modified: qpid/dispatch/trunk/tests/system_tests_management.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/system_tests_management.py?rev=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/system_tests_management.py (original)
+++ qpid/dispatch/trunk/tests/system_tests_management.py Wed Jan 28 17:30:40 2015
@@ -21,6 +21,8 @@
 
 import unittest, system_test, re, os, json, sys
 from qpid_dispatch.management import Node, ManagementError, Url, BadRequestStatus, NotImplementedStatus,
NotFoundStatus, ForbiddenStatus
+from qpid_dispatch_internal.management.qdrouter import QdSchema
+from qpid_dispatch_internal.compat import OrderedDict, dictify
 from system_test import Qdrouterd, message, retry, wait_ports, Process
 from proton import ConnectionException
 from itertools import chain
@@ -44,7 +46,6 @@ def short_name(name):
         return name[len(PREFIX):]
     return name
 
-
 class ManagementTest(system_test.TestCase): # pylint: disable=too-many-public-methods
 
     @classmethod
@@ -264,17 +265,17 @@ class ManagementTest(system_test.TestCas
         self.assertEqual(str(self.router.ports[1]), entity.port)
 
         # Bad type
-        self.assertRaises(NotFoundStatus, self.node.read, type=CONNECTOR, name='l0')
+        self.assertRaises(BadRequestStatus, self.node.read, type=CONNECTOR, name='l0')
 
         # Unknown entity
         self.assertRaises(NotFoundStatus, self.node.read, type=LISTENER, name='nosuch')
 
         # Update and delete are not allowed by the schema
-        self.assertRaises(ForbiddenStatus, entity.update)
-        self.assertRaises(ForbiddenStatus, entity.delete)
+        self.assertRaises(NotImplementedStatus, entity.update)
+        self.assertRaises(NotImplementedStatus, entity.delete)
 
         # Non-standard request is not allowed by schema.
-        self.assertRaises(ForbiddenStatus, entity.call, 'nosuchop', foo="bar")
+        self.assertRaises(NotImplementedStatus, entity.call, 'nosuchop', foo="bar")
 
         # Dummy entity supports all CRUD operations
         dummy = self.node.create({'arg1': 'START'}, type=DUMMY, name='MyDummy', )
@@ -378,7 +379,10 @@ class ManagementTest(system_test.TestCas
         entities = list(chain(
             *[n.query(attribute_names=['type', 'identity', 'name']).iter_entities() for n
in nodes]))
         for e in entities:
-            self.assertRegexpMatches(e.identity, "^%s/" % short_name(e.type))
+            if e.type == MANAGEMENT:
+                self.assertEqual(e.identity, "self")
+            else:
+                self.assertRegexpMatches(e.identity, "^%s/" % short_name(e.type), e)
 
     def test_get_types(self):
         types = self.node.get_types()
@@ -446,5 +450,13 @@ class ManagementTest(system_test.TestCas
         router = self.qdrouterd("multi_log_conf", conf, wait=True)
 
 
+    def test_get_schema(self):
+        schema = dictify(QdSchema().dump())
+        # FIXME aconway 2015-01-26: improve node API.
+        got = self.node.call(self.node.request(operation="GET-JSON-SCHEMA", identity="self")).body
+        self.assertEquals(schema, dictify(json.loads(got)))
+        got = self.node.call(self.node.request(operation="GET-SCHEMA", identity="self")).body
+        self.assertEquals(schema, got)
+
 if __name__ == '__main__':
     unittest.main(system_test.main_module())

Modified: qpid/dispatch/trunk/tests/system_tests_qdmanage.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/system_tests_qdmanage.py?rev=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/system_tests_qdmanage.py (original)
+++ qpid/dispatch/trunk/tests/system_tests_qdmanage.py Wed Jan 28 17:30:40 2015
@@ -48,12 +48,6 @@ class QdmanageTest(TestCase):
             raise Exception("%s\n%s" % (e, out))
         return out
 
-    def test_help(self):
-        help_out = self.run_qdmanage('--help', r'Usage: qdmanage', expect=Process.EXIT_OK)
-        assert re.search('Usage: qdmanage', help_out)
-        for cmd in ['create', 'read', 'update', 'delete', 'query']:
-            assert re.search(cmd, help_out)
-
     def assert_entity_equal(self, expect, actual, copy=None):
         """Copy keys in copy from actual to idenity, then assert maps equal."""
         if copy:

Modified: qpid/dispatch/trunk/tools/qdmanage
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tools/qdmanage?rev=1655398&r1=1655397&r2=1655398&view=diff
==============================================================================
--- qpid/dispatch/trunk/tools/qdmanage (original)
+++ qpid/dispatch/trunk/tools/qdmanage Wed Jan 28 17:30:40 2015
@@ -36,12 +36,13 @@ class QdManage():
 
     def __init__(self):
 
-        self.operations = ['query', 'create', 'read', 'update', 'delete',
-                           'get-types', 'get-operations', 'get-attributes', 'get-annotations',
-                           'get-mgmt-nodes']
+        self.operations = ['QUERY', 'CREATE', 'READ', 'UPDATE', 'DELETE',
+                           'GET-TYPES', 'GET-OPERATIONS', 'GET-ATTRIBUTES', 'GET-ANNOTATIONS',
+                           'GET-MGMT-NODES']
 
         usage = "%prog <operation> [options...] [arguments...]"
-        description = "Operations: "+", ".join(self.operations)
+        description = "Standard operations: %s. Use GET-OPERATIONS to find additional operations."
\
+                      % (", ".join(self.operations))
 
         op = OptionParser(usage=usage, option_class=Option, description=description)
         op.add_option('--type', help='Type of entity to operate on.')
@@ -51,20 +52,19 @@ class QdManage():
                      help="Pretty-printing indent. -1 means don't pretty-print (default %default)")
         op.add_option('--stdin', action='store_true',
                       help='Read attributes as JSON map or list of maps from stdin.')
-
-
         op.add_option_group(connection_options(op))
-
         self.op = op
 
     def run(self, argv):
         self.opts, self.args = self.op.parse_args(argv[1:])
         if len(self.args) == 0: raise UsageError("No operation specified")
         operation = self.args.pop(0)
-        if operation not in self.operations: raise UsageError("Unknown operation: %s" % operation)
         self.node = Node(Url(self.opts.bus), self.opts.router)
-        method = getattr(self, operation.replace('-','_'))
-        method()
+        if operation.upper() in self.operations:
+            method = getattr(self, operation.lower().replace('-','_'))
+            method()
+        else:                   # Custom operation
+            self.operation(operation)
 
     def main(self, argv):
         return main(self.run, argv, self.op)
@@ -151,5 +151,11 @@ class QdManage():
         check_args(self.args, 0)
         self.print_json(self.call_node('get_mgmt_nodes'))
 
+    def operation(self, operation):
+        """operation [ATTR=VALUE...]   Call custom operation with ATTR=VALUE as request properties."""
+        properties = dict(attr_split(arg) for arg in self.args or [])
+        request = self.call_node('request', 'type', 'name', 'identity', operation=operation,
**properties)
+        print self.node.call(request).body
+
 if __name__ == "__main__":
     sys.exit(QdManage().main(sys.argv))



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


Mime
View raw message