You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2014/11/18 16:55:42 UTC
svn commit: r1640379 - in /qpid/dispatch/trunk:
python/qpid_dispatch/management/ python/qpid_dispatch_internal/
python/qpid_dispatch_internal/management/ src/ tests/
Author: aconway
Date: Tue Nov 18 15:55:41 2014
New Revision: 1640379
URL: http://svn.apache.org/r1640379
Log:
DISPATCH-74: Allow changes to logging configuration of a running router.
Allow UPDATE management requests to log entities to change log settings on a running router.
Use log:MODULE as management ID for log entity representing MODULE.
The generic qdmanage client can be used to modify log settings.
This commit does not include a more convenient client for this task.
Modified:
qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json
qpid/dispatch/trunk/python/qpid_dispatch_internal/dispatch_c.py
qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py
qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py
qpid/dispatch/trunk/src/alloc.c
qpid/dispatch/trunk/src/c_entity.c
qpid/dispatch/trunk/src/log.c
qpid/dispatch/trunk/src/router_agent.c
qpid/dispatch/trunk/src/server.c
qpid/dispatch/trunk/tests/system_tests_management.py
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=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch/management/qdrouter.json Tue Nov 18 15:55:41 2014
@@ -233,7 +233,7 @@
"include": [
"common"
],
- "operations": ["CREATE", "READ"],
+ "operations": ["CREATE", "READ", "UPDATE", "DELETE"],
"attributes": {
"module": {
"type":[
Modified: qpid/dispatch/trunk/python/qpid_dispatch_internal/dispatch_c.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/python/qpid_dispatch_internal/dispatch_c.py?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/dispatch_c.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/dispatch_c.py Tue Nov 18 15:55:41 2014
@@ -70,8 +70,8 @@ class QdDll(ctypes.PyDLL):
self._prototype(self.qd_connection_manager_start, None, [self.qd_dispatch_p])
self._prototype(self.qd_waypoint_activate_all, None, [self.qd_dispatch_p])
- self._prototype(self.qd_c_entity_update_begin, c_long, [py_object])
- self._prototype(self.qd_c_entity_update_end, None, [])
+ self._prototype(self.qd_c_entity_refresh_begin, c_long, [py_object])
+ self._prototype(self.qd_c_entity_refresh_end, None, [])
def _errcheck(self, result, func, args):
if self.qd_error_code():
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=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/agent.py Tue Nov 18 15:55:41 2014
@@ -48,7 +48,7 @@ Adding/removing/updating entities from C
4. unlocks the router.
"""
-import traceback
+import traceback, sys
from itertools import ifilter, chain
from traceback import format_exc
from threading import Lock
@@ -93,7 +93,9 @@ class Entity(SchemaEntity):
Base class for agent entities with operations as well as attributes.
"""
- def _update(self): return False # Replaced by _set_pointer
+ def _refresh(self):
+ """Refresh self.attributes from C implementation. No-op if no C impl pointer"""
+ return False # Replaced by _set_pointer
def __init__(self, agent, entity_type, attributes=None, validate=True):
"""
@@ -101,7 +103,7 @@ class Entity(SchemaEntity):
@param dispatch: Pointer to qd_dispatch C object.
@param entity_type: L{EntityType}
@param attributes: Attribute name:value map
- @param pointer: Pointer to C object that can be used to update attributes.
+ @param pointer: Pointer to C object that can be used to refresh attributes.
"""
super(Entity, self).__init__(entity_type, attributes, validate=validate)
# Direct __dict__ access to avoid validation as schema attributes
@@ -110,13 +112,13 @@ class Entity(SchemaEntity):
self.__dict__['_dispatch'] = agent.dispatch
def _set_pointer(self, pointer):
- fname = "qd_c_entity_update_" + self.entity_type.short_name.replace('.', '_')
- updatefn = self._qd.function(
+ fname = "qd_c_entity_refresh_" + self.entity_type.short_name.replace('.', '_')
+ refreshfn = self._qd.function(
fname, c_long, [py_object, c_void_p])
- def _do_update():
- updatefn(self.attributes, pointer)
+ def _do_refresh():
+ refreshfn(self.attributes, pointer)
return True
- self.__dict__['_update'] = _do_update
+ self.__dict__['_refresh'] = _do_refresh
def create(self, request):
"""Subclasses can add extra create actions here"""
@@ -135,13 +137,22 @@ class Entity(SchemaEntity):
newattrs = dict(self.attributes, **request.body)
self.entity_type.validate(newattrs)
self.attributes = newattrs
+ self._update()
return (OK, self.attributes)
+ def _update(self):
+ """Subclasses implement update logic here"""
+ pass
+
def delete(self, request):
"""Handle delete request from client"""
self._agent.remove(self)
+ self._delete()
return (NO_CONTENT, {})
+ def _delete(self):
+ """Subclasses implement delete logic here"""
+ pass
class ContainerEntity(Entity): pass
@@ -153,9 +164,21 @@ class RouterEntity(Entity):
self._set_pointer(self._dispatch)
class LogEntity(Entity):
+ def __init__(self, agent, entity_type, attributes=None, validate=True):
+ module = attributes.get('module')
+ if module:
+ attributes['identity'] = attributes['name'] = "%s:%s" % (entity_type.short_name, module)
+ super(LogEntity, self).__init__(agent, entity_type, attributes, validate)
+
def create(self, request):
self._qd.qd_log_entity(self)
+ def _update(self):
+ self._qd.qd_log_entity(self)
+
+ def _delete(self):
+ """Can't actually delete a log source but return it to the default state"""
+ self._qd.qd_log_source_reset(self.attributes['module'])
class ListenerEntity(Entity):
def create(self, request):
@@ -204,7 +227,7 @@ class CEntity(Entity):
super(CEntity, self).__init__(agent, entity_type, validate=False)
self._set_pointer(pointer)
- self._update()
+ self._refresh()
identity = self.attributes.get('identity')
if identity is None: identity = str(self.id_count.next())
self.attributes['identity'] = prefix(entity_type.short_name, identity)
@@ -233,7 +256,7 @@ class AllocatorEntity(CEntity):
class EntityCache(object):
"""
- Searchable cache of entities, can be updated from C attributes.
+ Searchable cache of entities, can be refreshd from C attributes.
"""
def __init__(self, agent):
self.entities = []
@@ -284,8 +307,8 @@ class EntityCache(object):
del self.pointers[pointer]
self._remove(entity)
- def update_from_c(self):
- """Update entities from the C dispatch runtime"""
+ def refresh_from_c(self):
+ """Refresh entities from the C dispatch runtime"""
events = []
REMOVE, ADD, REMOVE_ADD = 0, 1, 2
@@ -312,7 +335,7 @@ class EntityCache(object):
# FIXME aconway 2014-10-23: locking is ugly, push it down into C code.
self.qd.qd_dispatch_router_lock(self.agent.dispatch)
try:
- self.qd.qd_c_entity_update_begin(events)
+ self.qd.qd_c_entity_refresh_begin(events)
# Collapse sequences of add/remove into a single remove/add/remove_add per pointer.
actions = {}
for action, type, pointer in events:
@@ -327,9 +350,9 @@ class EntityCache(object):
entity = klass(self.agent, entity_type, pointer)
self.add(entity, pointer)
- for e in self.entities: e._update()
+ for e in self.entities: e._refresh()
finally:
- self.qd.qd_c_entity_update_end()
+ self.qd.qd_c_entity_refresh_end()
self.qd.qd_dispatch_router_unlock(self.agent.dispatch)
class Agent(object):
@@ -394,7 +417,7 @@ class Agent(object):
"""Called when a management request is received."""
# Coarse locking, handle one request at a time.
with self.request_lock:
- self.entities.update_from_c()
+ self.entities.refresh_from_c()
self.log(LOG_DEBUG, "Agent request %s on link %s"%(request, link_id))
def error(e, trace):
"""Raise an error"""
@@ -525,7 +548,7 @@ class Agent(object):
raise InternalServerErrorStatus(
"Duplicate (%s) entities with %s=%r" % (len(found), k, v))
else:
- raise NotFoundStatus("No entity with %s'" % attrvals())
+ raise NotFoundStatus("No entity with %s" % attrvals())
for k, v in ids.iteritems():
if entity[k] != v: raise BadRequestStatus("Conflicting %s" % attrvals())
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=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py (original)
+++ qpid/dispatch/trunk/python/qpid_dispatch_internal/management/config.py Tue Nov 18 15:55:41 2014
@@ -144,23 +144,26 @@ def configure_dispatch(dispatch, filenam
dispatch = qd.qd_dispatch_p(dispatch)
config = Config(filename)
- # NOTE: Can't import agent till till dispatch C extension module is initialized.
- from .agent import Agent
- agent = Agent(dispatch, config.entities)
- qd.qd_dispatch_set_agent(dispatch, agent)
-
# Configure any DEFAULT log entities first so we can report errors in non-
# default log configurations to the correct place.
for l in config.by_type('log'):
if l['module'].upper() == 'DEFAULT': qd.qd_log_entity(l)
for l in config.by_type('log'):
if l['module'].upper() != 'DEFAULT': qd.qd_log_entity(l)
+
+ # NOTE: Can't import agent till till dispatch C extension module is initialized.
+ from .agent import Agent
+ agent = Agent(dispatch, config.entities)
+ qd.qd_dispatch_set_agent(dispatch, agent)
+
+ # Configure and prepare container and router before activating agent
qd.qd_dispatch_configure_container(dispatch, config.by_type('container').next())
qd.qd_dispatch_configure_router(dispatch, config.by_type('router').next())
qd.qd_dispatch_prepare(dispatch)
agent.activate("$management")
- qd.qd_router_setup_late(dispatch)
+
+ qd.qd_router_setup_late(dispatch) # Actions requiring management agent.
# Note must configure addresses, waypoints, listeners and connectors after qd_dispatch_prepare
for a in config.by_type('fixedAddress'): qd.qd_dispatch_configure_address(dispatch, a)
Modified: qpid/dispatch/trunk/src/alloc.c
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/src/alloc.c?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/src/alloc.c (original)
+++ qpid/dispatch/trunk/src/alloc.c Tue Nov 18 15:55:41 2014
@@ -347,7 +347,7 @@ void qd_alloc_finalize(void)
}
-qd_error_t qd_c_entity_update_allocator(qd_entity_t* entity, void *impl) {
+qd_error_t qd_c_entity_refresh_allocator(qd_entity_t* entity, void *impl) {
qd_alloc_type_t *alloc_type = (qd_alloc_type_t*) impl;
if ((qd_entity_has(entity, "identity") ||
qd_entity_set_string(entity, "identity", alloc_type->desc->type_name) == 0) &&
Modified: qpid/dispatch/trunk/src/c_entity.c
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/src/c_entity.c?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/src/c_entity.c (original)
+++ qpid/dispatch/trunk/src/c_entity.c Tue Nov 18 15:55:41 2014
@@ -73,8 +73,8 @@ void qd_c_entity_remove(const char *type
// Get events in the add/remove cache into a python list of (action, type, pointer)
// Locks the entity cache so entities can be updated safely (prevent entities from being deleted.)
// Do not processs any entities if return error code != 0
-// Must call qd_c_entity_update_end when done, regardless of error code.
-qd_error_t qd_c_entity_update_begin(PyObject *list) {
+// Must call qd_c_entity_refresh_end when done, regardless of error code.
+qd_error_t qd_c_entity_refresh_begin(PyObject *list) {
if (!event_lock) return QD_ERROR_NONE; /* Unit tests don't call qd_c_entity_initialize */
qd_error_clear();
sys_mutex_lock(event_lock);
@@ -92,7 +92,7 @@ qd_error_t qd_c_entity_update_begin(PyOb
return qd_error_code();
}
-void qd_c_entity_update_end() {
+void qd_c_entity_refresh_end() {
sys_mutex_unlock(event_lock);
}
Modified: qpid/dispatch/trunk/src/log.c
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/src/log.c?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/src/log.c (original)
+++ qpid/dispatch/trunk/src/log.c Tue Nov 18 15:55:41 2014
@@ -239,6 +239,14 @@ static void write_log(qd_log_source_t *l
}
}
+/// Reset the log source to the default state
+static void qd_log_source_defaults(qd_log_source_t *log_source) {
+ log_source->mask = -1;
+ log_source->timestamp = -1;
+ log_source->source = -1;
+ log_source->sink = 0;
+}
+
/// Caller must hold the log_source_lock
static qd_log_source_t *qd_log_source_lh(const char *module)
{
@@ -249,10 +257,7 @@ static qd_log_source_t *qd_log_source_lh
memset(log_source, 0, sizeof(qd_log_source_t));
DEQ_ITEM_INIT(log_source);
log_source->module = module;
- log_source->mask = -1;
- log_source->timestamp = -1;
- log_source->source = -1;
- log_source->sink = 0;
+ qd_log_source_defaults(log_source);
DEQ_INSERT_TAIL(source_list, log_source);
}
return log_source;
@@ -266,6 +271,15 @@ qd_log_source_t *qd_log_source(const cha
return src;
}
+qd_log_source_t *qd_log_source_reset(const char *module)
+{
+ sys_mutex_lock(log_source_lock);
+ qd_log_source_t* src = qd_log_source_lh(module);
+ qd_log_source_defaults(src);
+ sys_mutex_unlock(log_source_lock);
+ return src;
+}
+
static void qd_log_source_free_lh(qd_log_source_t* src) {
DEQ_REMOVE_HEAD(source_list);
log_sink_free_lh(src->sink);
Modified: qpid/dispatch/trunk/src/router_agent.c
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/src/router_agent.c?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/src/router_agent.c (original)
+++ qpid/dispatch/trunk/src/router_agent.c Tue Nov 18 15:55:41 2014
@@ -36,7 +36,7 @@ static const char *qd_router_mode_names[
};
ENUM_DEFINE(qd_router_mode, qd_router_mode_names);
-qd_error_t qd_c_entity_update_router(qd_entity_t* entity, void *impl) {
+qd_error_t qd_c_entity_refresh_router(qd_entity_t* entity, void *impl) {
qd_dispatch_t *qd = (qd_dispatch_t*) impl;
qd_router_t *router = qd->router;
if (qd_entity_set_stringf(entity, "name", "%s:%s", QD_ROUTER_TYPE, router->router_id) == 0 &&
@@ -57,7 +57,7 @@ static const char *address_text(qd_addre
return addr ? (const char*) qd_hash_key_by_handle(addr->hash_handle) : 0;
}
-qd_error_t qd_c_entity_update_router_address(qd_entity_t* entity, void *impl) {
+qd_error_t qd_c_entity_refresh_router_address(qd_entity_t* entity, void *impl) {
qd_address_t *addr = (qd_address_t*) impl;
if ((qd_entity_has(entity, "identity") ||
qd_entity_set_string(entity, "identity", address_text(addr)) == 0) &&
@@ -76,7 +76,7 @@ qd_error_t qd_c_entity_update_router_add
#define CHECK(err) if (err != 0) return qd_error_code()
-qd_error_t qd_c_entity_update_router_node(qd_entity_t* entity, void *impl) {
+qd_error_t qd_c_entity_refresh_router_node(qd_entity_t* entity, void *impl) {
qd_router_node_t *rnode = (qd_router_node_t*) impl;
if (!qd_entity_has(entity, "identity")) {
@@ -251,7 +251,7 @@ static const char *qd_router_addr_text(q
return addr ? (const char*)qd_hash_key_by_handle(addr->hash_handle) : NULL;
}
-qd_error_t qd_c_entity_update_router_link(qd_entity_t* entity, void *impl)
+qd_error_t qd_c_entity_refresh_router_link(qd_entity_t* entity, void *impl)
{
qd_router_link_t *link = (qd_router_link_t*) impl;
/* FIXME aconway 2014-10-17: old management used link->bit_mask as name/identity,
Modified: qpid/dispatch/trunk/src/server.c
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/src/server.c?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/src/server.c (original)
+++ qpid/dispatch/trunk/src/server.c Tue Nov 18 15:55:41 2014
@@ -78,7 +78,7 @@ static qd_error_t connection_entity_upda
return qd_entity_set_string(entity, "host", pn_connector_name(conn->pn_cxtr));
}
-qd_error_t qd_c_entity_update_connection(qd_entity_t* entity, void *impl)
+qd_error_t qd_c_entity_refresh_connection(qd_entity_t* entity, void *impl)
{
qd_connection_t *conn = (qd_connection_t*)impl;
const qd_server_config_t *config =
Modified: qpid/dispatch/trunk/tests/system_tests_management.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/trunk/tests/system_tests_management.py?rev=1640379&r1=1640378&r2=1640379&view=diff
==============================================================================
--- qpid/dispatch/trunk/tests/system_tests_management.py (original)
+++ qpid/dispatch/trunk/tests/system_tests_management.py Tue Nov 18 15:55:41 2014
@@ -94,7 +94,7 @@ class ManagementTest(system_test.TestCas
expect = [[LISTENER, 'l%s' % i, str(self.router.ports[i])] for i in xrange(3)]
for r in expect: # We might have extras in results due to create tests
self.assertTrue(r in response.results)
- for name in ['router:' + self.router.name, 'log:0']:
+ for name in ['router:' + self.router.name, 'log:DEFAULT']:
self.assertTrue([r for r in response.get_dicts() if r['name'] == name],
msg="Can't find result with name '%s'" % name)
@@ -123,16 +123,44 @@ class ManagementTest(system_test.TestCas
router = node3.query(type=ROUTER).get_entities()
self.assertEqual(self.__class__.router.name, router[0]['routerId'])
- def test_create_log(self):
- """Create a log entity"""
+ def test_log(self):
+ """Create, update and query log entities"""
+
+ self.assertRaises(NotFoundStatus, self.node.read, identity='log:AGENT') # Not configured
+
+ default = self.node.read(identity='log:DEFAULT')
+ self.assertEqual(default.attributes,
+ {u'identity': u'log:DEFAULT',
+ u'level': u'trace',
+ u'module': u'DEFAULT',
+ u'name': u'log:DEFAULT',
+ u'output': u'ManagementTest.log',
+ u'source': True,
+ u'timestamp': True,
+ u'type': u'org.apache.qpid.dispatch.log'})
+
+
+ def check_log(log, bad_type='nosuch'):
+ # Cause an error and verify it shows up in the log file.
+ self.assertRaises(ManagementError, self.node.create, type=bad_type, name=bad_type)
+ f = self.cleanup(open(log))
+ logstr = f.read()
+ self.assertTrue(re.search(r'ValidationError.*%s' % bad_type, logstr),
+ msg="Can't find expected ValidationError.*%s in '%r'" % (bad_type, logstr))
+
+ # Create a log entity, verify logging is as expected
log = os.path.abspath("test_create_log.log")
- self.assert_create_ok('log', 'log.1', dict(module='AGENT', level="error", output=log))
- # Cause an error and verify it shows up in the log file.
- self.assertRaises(ManagementError, self.node.create, type='nosuch', name='nosuch')
- f = self.cleanup(open(log))
- logstr = f.read()
- self.assertTrue(re.search(r'ValidationError.*nosuch', logstr),
- msg="Can't find expected ValidationError.*nosuch in '%r'" % logstr)
+ agent_log = self.assert_create_ok('log', 'log.1', dict(module='AGENT', level="error", output=log))
+ check_log(log)
+
+ # Update the log entity to output to a different file
+ log = os.path.abspath("test_create_log2.log")
+ self.node.update(dict(module='AGENT', level="error", output=log), identity='log:AGENT')
+ check_log(log)
+
+ # Delete the log entity - return to default state.
+ self.node.delete(identity='log:AGENT')
+ self.assertRaises(AssertionError, check_log, log, 'nosuch2')
def test_create_fixed_address(self):
self.assert_create_ok(FIXED_ADDRESS, 'fixed1', dict(prefix='fixed1'))
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org