def _invalid_request_test(self, msg, disposition=Disposition.REJECTED): bc = BlockingConnection(self.INT_A.edge_listener, timeout=TIMEOUT) srr = SyncRequestResponse(bc, self.QD_TERMINUS_ADDRESS_LOOKUP) # @TODO(kgiusti) - self.assertRaises does not work here: try: srr.call(msg) self.assertTrue(False) except SendException as exc: self.assertEqual(disposition, exc.state) bc.close()
def test_link_route_lookup_not_found(self): """ verify correct handling of lookup misses """ bc = BlockingConnection(self.INT_A.edge_listener, timeout=TIMEOUT) srr = SyncRequestResponse(bc, self.QD_TERMINUS_ADDRESS_LOOKUP) rsp = self._check_response(srr.call(self._lookup_request("not.found.address", True))) self.assertEqual(self.QCM_ADDR_LOOKUP_NOT_FOUND, rsp[0])
def query( self, entity_type: str = 'org.apache.qpid.dispatch.router.node' ) -> NamedTuple: """ Queries the related router instance, retrieving information for the provided Entity Type. The result is an array of a named tuple, whose fields are the attribute names returned by the router. In example, if querying entity type: org.apache.qpid.dispatch.allocator, the results can be accessed as: result.typeName, result.typeSize, ... same names returned by the router. :param entity_type: :return: """ # Scheme to use scheme = 'amqp' if self._connection_options['ssl_domain']: scheme = 'amqps' # URL to test url = Url("%s://%s:%s/$management" % (scheme, self.host, self.port)) self._logger.info("Querying router at: %s://%s:%s/$management" % (scheme, self.host, self.port)) # Proton connection self._logger.debug("Connection options: %s" % self._connection_options) connection = BlockingConnection(url, **self._connection_options) # Proton sync client client = SyncRequestResponse(connection, url.path) # Request message object request = proton.Message() request.properties = { u'operation': u'QUERY', u'entityType': u'%s' % entity_type } request.body = {u'attributeNames': []} # Sending the request response = client.call(request) # Closing connection client.connection.close() # Namedtuple that represents the query response from the router # so fields can be read based on their attribute names. RouterQueryResults = namedtuple('RouterQueryResults', response.body["attributeNames"]) records = [] for record in response.body["results"]: records.append(RouterQueryResults(*record)) return records
def test_link_route_lookup_no_dest(self): """ verify correct handling of matches to mobile addresses """ bc = BlockingConnection(self.INT_A.edge_listener, timeout=TIMEOUT) srr = SyncRequestResponse(bc, self.QD_TERMINUS_ADDRESS_LOOKUP) rsp = self._check_response(srr.call(self._lookup_request("org.apache.A.nope", True))) self.assertEqual(self.QCM_ADDR_LOOKUP_OK, rsp[0]) self.assertEqual(True, rsp[1]) self.assertEqual(False, rsp[2]) bc.close()
def test_link_route_lookup_ok(self): """ verify a link route address can be looked up successfully for both locally attached and remotely attached containers """ # fire up a fake broker attached to the router local to the edge router # wait until both in and out addresses are ready fb = FakeBroker(self.INT_A.broker_connector, container_id='FakeBrokerA') self.INT_A.wait_address("org.apache.A", containers=1, count=2) # create a fake edge and lookup the target address bc = BlockingConnection(self.INT_A.edge_listener, timeout=TIMEOUT) srr = SyncRequestResponse(bc, self.QD_TERMINUS_ADDRESS_LOOKUP) for direction in [True, False]: # True = direction inbound (receiver) False = direction outbound (sender) rsp = self._check_response( srr.call(self._lookup_request("org.apache.A.foo", direction))) self.assertEqual(self.QCM_ADDR_LOOKUP_OK, rsp[0]) self.assertTrue(rsp[1]) self.assertTrue(rsp[2]) # shutdown fake router fb.join() # now fire up a fake broker attached to the remote router fb = FakeBroker(self.INT_B.broker_connector, container_id='FakeBrokerB') self.INT_A.wait_address("org.apache.B", remotes=1, count=2) for direction in [True, False]: rsp = self._check_response( srr.call(self._lookup_request("org.apache.B.foo", direction))) self.assertEqual(self.QCM_ADDR_LOOKUP_OK, rsp[0]) self.assertTrue(rsp[1]) self.assertTrue(rsp[2]) fb.join() bc.close()
def test_link_route_lookup_not_link_route(self): """ verify correct handling of matches to mobile addresses """ addr = "not.a.linkroute" client = AsyncTestReceiver(self.INT_A.listener, addr) self.INT_A.wait_address(addr) bc = BlockingConnection(self.INT_A.edge_listener, timeout=TIMEOUT) srr = SyncRequestResponse(bc, self.QD_TERMINUS_ADDRESS_LOOKUP) rsp = self._check_response(srr.call(self._lookup_request(addr, True))) # self.assertEqual(self.QCM_ADDR_LOOKUP_OK, rsp[0]) self.assertEqual(False, rsp[1]) bc.close() client.stop()
def test_sync_request_response_blocking_connection_no_object_leaks(self): with test_broker() as tb: sockname = tb.get_acceptor_sockname() url = "{0}:{1}".format(*sockname) opts = namedtuple('Opts', ['address', 'timeout'])(address=url, timeout=3) with no_fd_leaks(self): client = SyncRequestResponse( BlockingConnection(url, opts.timeout, allowed_mechs="ANONYMOUS"), "somequeue") try: request = "One Two Three Four" response = client.call(Message(body=request)) finally: client.connection.close() gc.collect()
def get_metrics(self): client = SyncRequestResponse(BlockingConnection("127.0.0.1:5672", 30), "$management") try: properties = {} properties["entityType"] = "org.apache.qpid.dispatch.connection" properties["operation"] = "QUERY" properties["name"] = "self" message = Message(body=None, properties=properties) response = client.call(message) return response except: e = sys.exc_info()[0] print("Error querying router for metrics: %s" % e) return None finally: client.connection.close()
def collect_metric(self, entityType): try: client = SyncRequestResponse( BlockingConnection("127.0.0.1:5672", 30), "$management") try: properties = {} properties["entityType"] = entityType properties["operation"] = "QUERY" properties["name"] = "self" message = Message(body=None, properties=properties) response = client.call(message) if response == None: return response else: return RouterResponse(response) finally: client.connection.close() except: e = sys.exc_info()[0] print("Error querying router for metrics: %s" % e) return None
class Node(object): """Client proxy for an AMQP management node""" @staticmethod def connection(url=None, router=None, timeout=10, ssl_domain=None, sasl=None): """Return a BlockingConnection suitable for connecting to a management node @param url: URL of the management node. @param router: If address does not contain a path, use the management node for this router ID. If not specified and address does not contain a path, use the default management node. """ url = Url(url) # Convert string to Url class. if url.path is None: if router: url.path = u'_topo/0/%s/$management' % router else: url.path = u'$management' if ssl_domain: sasl_enabled = True else: sasl_enabled = True if sasl else False # if sasl_mechanism is unicode, convert it to python string return BlockingConnection( url, timeout=timeout, ssl_domain=ssl_domain, sasl_enabled=sasl_enabled, allowed_mechs=str(sasl.mechs) if sasl and sasl.mechs != None else None, user=str(sasl.user) if sasl and sasl.user != None else None, password=str(sasl.password) if sasl and sasl.password != None else None) @staticmethod def connect(url=None, router=None, timeout=10, ssl_domain=None, sasl=None): """Return a Node connected with the given parameters, see L{connection}""" return Node(Node.connection(url, router, timeout, ssl_domain, sasl)) def __init__(self, connection, locales=None): """ Create a management node proxy using the given connection. @param locales: Default list of locales for management operations. @param connection: a L{BlockingConnection} to the management agent. """ self.name = self.identity = u'self' self.type = u'org.amqp.management' # AMQP management node type self.locales = locales self.locales = locales self.url = connection.url self.client = SyncRequestResponse(connection, self.url.path) self.reply_to = self.client.reply_to def close(self): """Shut down the node""" if self.client: self.client.connection.close() self.client = None def __repr__(self): return "%s(%s)" % (self.__class__.__name__, self.url) @staticmethod def check_response(response, expect=OK): """ Check a management response message for errors and correlation ID. """ code = response.properties.get(u'statusCode') if code != expect: if 200 <= code <= 299: raise ValueError( "Response was %s(%s) but expected %s(%s): %s" % (code, STATUS_TEXT[code], expect, STATUS_TEXT[expect], response.properties.get(u'statusDescription'))) else: raise ManagementError.create( code, response.properties.get(u'statusDescription')) def request(self, body=None, **properties): """ Make a L{proton.Message} containining a management request. @param body: The request body, a dict or list. @param properties: Keyword arguments for application-properties of the request. @return: L{proton.Message} containining the management request. """ if self.locales: properties.setdefault(u'locales', self.locales) request = proton.Message() request.properties = clean_dict(properties) request.body = body or {} return request def node_request(self, body=None, **properties): """Construct a request for the managment node itself""" return self.request(body, name=self.name, type=self.type, **properties) def call(self, request, expect=OK): """ Send a management request message, wait for a response. @return: Response message. """ response = self.client.call(request) self.check_response(response, expect=expect) return response class QueryResponse(object): """ Result returned by L{query}. @ivar attribute_names: List of attribute names for the results. @ivar results: list of lists of attribute values in same order as attribute_names """ def __init__(self, node, attribute_names, results): """ @param response: the respose message to a query. """ self.node = node self.attribute_names = attribute_names self.results = results def iter_dicts(self, clean=False): """ Return an iterator that yields a dictionary for each result. @param clean: if True remove any None values from returned dictionaries. """ for r in self.results: if clean: yield clean_dict(zip(self.attribute_names, r)) else: yield dict(zip(self.attribute_names, r)) def iter_entities(self, clean=False): """ Return an iterator that yields an L{Entity} for each result. @param clean: if True remove any None values from returned dictionaries. """ for d in self.iter_dicts(clean=clean): yield Entity(self.node, d) def get_dicts(self, clean=False): """Results as list of dicts.""" return [d for d in self.iter_dicts(clean=clean)] def get_entities(self, clean=False): """Results as list of entities.""" return [d for d in self.iter_entities(clean=clean)] def __repr__(self): return "QueryResponse(attribute_names=%r, results=%r" % ( self.attribute_names, self.results) def query(self, type=None, attribute_names=None, offset=None, count=None): """ Send an AMQP management query message and return the response. At least one of type, attribute_names must be specified. @keyword type: The type of entity to query. @keyword attribute_names: A list of attribute names to query. @keyword offset: An integer offset into the list of results to return. @keyword count: A count of the maximum number of results to return. @return: A L{QueryResponse} """ # There is a bug in proton (PROTON-1846) wherein we cannot ask for # too many rows. So, as a safety we are going to ask only for # MAX_ALLOWED_COUNT_PER_REQUEST. Since this is used by both qdstat # and qdmanage, we have determined that the optimal value for # MAX_ALLOWED_COUNT_PER_REQUEST is 700 MAX_ALLOWED_COUNT_PER_REQUEST = 700 response_results = [] response_attr_names = [] if offset is None: offset = 0 if count is None: # count has not been specified. For each request the # maximum number of rows we can get without proton # failing is MAX_ALLOWED_COUNT_PER_REQUEST request_count = MAX_ALLOWED_COUNT_PER_REQUEST else: request_count = min(MAX_ALLOWED_COUNT_PER_REQUEST, count) while True: request = self.node_request( {u'attributeNames': attribute_names or []}, operation=u'QUERY', entityType=type, offset=offset, count=request_count) response = self.call(request) if not response_attr_names: response_attr_names += response.body[u'attributeNames'] response_results += response.body[u'results'] if len(response.body[u'results']) < request_count: break if count: len_response_results = len(response_results) if count == len_response_results: break if count - len_response_results < request_count: request_count = count - len_response_results offset += request_count query_reponse = Node.QueryResponse(self, response_attr_names, response_results) return query_reponse def create(self, attributes=None, type=None, name=None): """ Create an entity. type and name can be specified in the attributes. @param attributes: Attributes for the new entity. @param type: Type of entity to create. @param name: Name for the new entity. @return: Entity proxy for the new entity. """ attributes = attributes or {} type = type or attributes.get(u'type') name = name or attributes.get(u'name') request = self.request(operation=u'CREATE', type=type, name=name, body=attributes) return Entity(self, self.call(request, expect=CREATED).body) def read(self, type=None, name=None, identity=None): """ Read an AMQP entity. If both name and identity are specified, only identity is used. @param type: Entity type. @param name: Entity name. @param identity: Entity identity. @return: An L{Entity} """ if name and identity: name = None # Only specify one request = self.request(operation=u'READ', type=type, name=name, identity=identity) return Entity(self, self.call(request).body) def update(self, attributes, type=None, name=None, identity=None): """ Update an entity with attributes. type, name and identity can be specified in the attributes. If both name and identity are specified, only identity is used. @param attributes: Attributes for the new entity. @param type: Entity type. @param name: Entity name. @param identity: Entity identity. @return: L{Entity} for the updated entity. """ attributes = attributes or {} type = type or attributes.get(u'type') name = name or attributes.get(u'name') identity = identity or attributes.get(u'identity') if name and identity: name = None # Only send one request = self.request(operation=U'UPDATE', type=type, name=name, identity=identity, body=attributes) return Entity(self, self.call(request).body) def delete(self, type=None, name=None, identity=None): """ Delete the remote entity. If both name and identity are specified, only identity is used. @param type: Entity type. @param name: Entity name. @param identity: Entity identity. """ if name and identity: name = None # Only specify one request = self.request(operation=U'DELETE', type=type, name=name, identity=identity) self.call(request, expect=NO_CONTENT) def get_types(self, type=None): return self.call( self.node_request(operation=u"GET-TYPES", entityType=type)).body def get_annotations(self, type=None): return self.call( self.node_request(operation=u"GET-ANNOTATIONS", entityType=type)).body def get_attributes(self, type=None): return self.call( self.node_request(operation=u"GET-ATTRIBUTES", entityType=type)).body def get_operations(self, type=None): return self.call( self.node_request(operation=u"GET-OPERATIONS", entityType=type)).body def get_mgmt_nodes(self, type=None): return self.call( self.node_request(operation=u"GET-MGMT-NODES", entityType=type)).body def get_log(self, limit=None, type=None): return self.call( self.node_request(operation=u"GET-LOG", entityType=type, limit=limit)).body
class QdrouterdClient(object): """ Class to interface with the Qdrouterd AMQP 1.0 API """ @staticmethod def connection(url=None, timeout=10, ssl_domain=None, sasl=None): """ Return a BlockingConnection for connection to a managemenet node """ url = Url(url) url.path = u'$management' if ssl_domain: sasl_enabled = True else: sasl_enabled = True if sasl else False return BlockingConnection(url, timeout=timeout, ssl_domain=ssl_domain, sasl_enabled=sasl_enabled, allowed_mechs=str(sasl.mechs) if sasl and sasl.mechs != None else None, user=str(sasl.user) if sasl else None, password=str(sasl.password) if sasl else None) @staticmethod def connect(url=None, timeout=10, ssl_domain=None, sasl=None): """ Return a Node connected with the given parameters, see L{connection} """ return QdrouterdClient(QdrouterdClient.connection(url, timeout, ssl_domain, sasl)) def __init__(self, connection): """ Create a management client proxy using the given connection. """ self.name = self.identity = u'self' self.type = u'org.amqp.management' # AMQP management node type self.url = connection.url self.client = SyncRequestResponse(connection, self.url.path) self.reply_to = self.client.reply_to def close(self): """ Shut down the client """ if self.client: self.client.connection.close() self.client = None def __repr__(self): return "%s(%s)"%(self.__class__.__name__, self.url) def request(self, body=None, **properties): """ Make a L{proton.Message} containining a management request. """ request = proton.Message() request.properties = properties request.body = body or {} return request def client_request(self, body=None, **properties): """ Construct a request """ return self.request(body, name=self.name, type=self.type, **properties) def call(self, request): """ Send a management request message, wait for a response. """ response = self.client.call(request) return response class QueryResponse(object): """ Result returned by L{query}. """ def __init__(self, qdrouterd_client, attribute_names, results): """ @param response: the respose message to a query. """ self.qdrouterd_client = qdrouterd_client self.attribute_names = attribute_names self.results = results def iter_dicts(self, clean=False): """ Return an iterator that yields a dictionary for each result. """ for r in self.results: if clean: yield clean_dict(zip(self.attribute_names, r)) else: yield dict(zip(self.attribute_names, r)) def iter_entities(self, clean=False): """ Return an iterator that yields an L{Entity} for each result. """ for d in self.iter_dicts(clean=clean): yield Entity(d) def get_dicts(self, clean=False): """ Results as list of dicts. """ return [d for d in self.iter_dicts(clean=clean)] def get_entities(self, clean=False): """ Results as list of entities. """ return [d for d in self.iter_entities(clean=clean)] def __repr__(self): return "QueryResponse(attribute_names=%r, results=%r"%(self.attribute_names, self.results) def query(self, type=None, attribute_names=None, offset=None, count=None): """ Send an AMQP management query message and return the response. At least one of type, attribute_names must be specified. """ request = self.client_request( {u'attributeNames': attribute_names or []}, operation=u'QUERY', entityType=type, offset=offset, count=count) response = self.call(request) return QdrouterdClient.QueryResponse(self, response.body[u'attributeNames'], response.body[u'results'])
class Node(object): """Client proxy for an AMQP management node""" @staticmethod def connection(url=None, router=None, timeout=10, ssl_domain=None): """Return a BlockingConnection suitable for connecting to a management node @param url: URL of the management node. @param router: If address does not contain a path, use the management node for this router ID. If not specified and address does not contain a path, use the default management node. """ url = Url(url) # Convert string to Url class. if url.path is None: if router: url.path = u'_topo/0/%s/$management' % router else: url.path = u'$management' return BlockingConnection(url, timeout=timeout, ssl_domain=ssl_domain) @staticmethod def connect(url=None, router=None, timeout=10, ssl_domain=None): """Return a Node connected with the given parameters, see L{connection}""" return Node(Node.connection(url, router, timeout, ssl_domain)) def __init__(self, connection, locales=None): """ Create a management node proxy using the given connection. @param locales: Default list of locales for management operations. @param connection: a L{BlockingConnection} to the management agent. """ self.name = self.identity = u'self' self.type = u'org.amqp.management' # AMQP management node type self.locales = locales self.locales = locales self.url = connection.url self.client = SyncRequestResponse(connection, self.url.path) self.reply_to = self.client.reply_to def close(self): """Shut down the node""" if self.client: self.client.connection.close() self.client = None def __repr__(self): return "%s(%s)"%(self.__class__.__name__, self.url) @staticmethod def check_response(response, expect=OK): """ Check a management response message for errors and correlation ID. """ code = response.properties.get(u'statusCode') if code != expect: if 200 <= code <= 299: raise ValueError("Response was %s(%s) but expected %s(%s): %s" % ( code, STATUS_TEXT[code], expect, STATUS_TEXT[expect], response.properties.get(u'statusDescription'))) else: raise ManagementError.create(code, response.properties.get(u'statusDescription')) def request(self, body=None, **properties): """ Make a L{proton.Message} containining a management request. @param body: The request body, a dict or list. @param properties: Keyword arguments for application-properties of the request. @return: L{proton.Message} containining the management request. """ if self.locales: properties.setdefault(u'locales', self.locales) request = proton.Message() request.properties = clean_dict(properties) request.body = body or {} return request def node_request(self, body=None, **properties): """Construct a request for the managment node itself""" return self.request(body, name=self.name, type=self.type, **properties) def call(self, request, expect=OK): """ Send a management request message, wait for a response. @return: Response message. """ response = self.client.call(request) self.check_response(response, expect=expect) return response class QueryResponse(object): """ Result returned by L{query}. @ivar attribute_names: List of attribute names for the results. @ivar results: list of lists of attribute values in same order as attribute_names """ def __init__(self, node, attribute_names, results): """ @param response: the respose message to a query. """ self.node = node self.attribute_names = attribute_names self.results = results def iter_dicts(self, clean=False): """ Return an iterator that yields a dictionary for each result. @param clean: if True remove any None values from returned dictionaries. """ for r in self.results: if clean: yield clean_dict(zip(self.attribute_names, r)) else: yield dict(zip(self.attribute_names, r)) def iter_entities(self, clean=False): """ Return an iterator that yields an L{Entity} for each result. @param clean: if True remove any None values from returned dictionaries. """ for d in self.iter_dicts(clean=clean): yield Entity(self.node, d) def get_dicts(self, clean=False): """Results as list of dicts.""" return [d for d in self.iter_dicts(clean=clean)] def get_entities(self, clean=False): """Results as list of entities.""" return [d for d in self.iter_entities(clean=clean)] def __repr__(self): return "QueryResponse(attribute_names=%r, results=%r"%(self.attribute_names, self.results) def query(self, type=None, attribute_names=None, offset=None, count=None): """ Send an AMQP management query message and return the response. At least one of type, attribute_names must be specified. @keyword type: The type of entity to query. @keyword attribute_names: A list of attribute names to query. @keyword offset: An integer offset into the list of results to return. @keyword count: A count of the maximum number of results to return. @return: A L{QueryResponse} """ request = self.node_request( {u'attributeNames': attribute_names or []}, operation=u'QUERY', entityType=type, offset=offset, count=count) response = self.call(request) return Node.QueryResponse(self, response.body[u'attributeNames'], response.body[u'results']) def create(self, attributes=None, type=None, name=None): """ Create an entity. type and name can be specified in the attributes. @param attributes: Attributes for the new entity. @param type: Type of entity to create. @param name: Name for the new entity. @return: Entity proxy for the new entity. """ attributes = attributes or {} type = type or attributes.get(u'type') name = name or attributes.get(u'name') request = self.request(operation=u'CREATE', type=type, name=name, body=attributes) return Entity(self, self.call(request, expect=CREATED).body) def read(self, type=None, name=None, identity=None): """ Read an AMQP entity. If both name and identity are specified, only identity is used. @param type: Entity type. @param name: Entity name. @param identity: Entity identity. @return: An L{Entity} """ if name and identity: name = None # Only specify one request = self.request(operation=u'READ', type=type, name=name, identity=identity) return Entity(self, self.call(request).body) def update(self, attributes, type=None, name=None, identity=None): """ Update an entity with attributes. type, name and identity can be specified in the attributes. If both name and identity are specified, only identity is used. @param attributes: Attributes for the new entity. @param type: Entity type. @param name: Entity name. @param identity: Entity identity. @return: L{Entity} for the updated entity. """ attributes = attributes or {} type = type or attributes.get(u'type') name = name or attributes.get(u'name') identity = identity or attributes.get(u'identity') if name and identity: name = None # Only send one request = self.request(operation=U'UPDATE', type=type, name=name, identity=identity, body=attributes) return Entity(self, self.call(request).body) def delete(self, type=None, name=None, identity=None): """ Delete the remote entity. If both name and identity are specified, only identity is used. @param type: Entity type. @param name: Entity name. @param identity: Entity identity. """ if name and identity: name = None # Only specify one request = self.request(operation=U'DELETE', type=type, name=name, identity=identity) self.call(request, expect=NO_CONTENT) def get_types(self, type=None): return self.call(self.node_request(operation=u"GET-TYPES", entityType=type)).body def get_annotations(self, type=None): return self.call(self.node_request(operation=u"GET-ANNOTATIONS", entityType=type)).body def get_attributes(self, type=None): return self.call(self.node_request(operation=u"GET-ATTRIBUTES", entityType=type)).body def get_operations(self, type=None): return self.call(self.node_request(operation=u"GET-OPERATIONS", entityType=type)).body def get_mgmt_nodes(self, type=None): return self.call(self.node_request(operation=u"GET-MGMT-NODES", entityType=type)).body def get_log(self, limit=None, type=None): return self.call(self.node_request(operation=u"GET-LOG", entityType=type, limit=limit)).body
class MgmtClient(object): """ Provides a synchronous API for management operations on a router. """ def __init__(self, router_address, timeout, router_id=None, edge_id=None): """ @param router_address: the network address of the router to manage. @param timeout: raise an error if a management operation blocks longer than timeout seconds. @param router_id: identity of remote router. Use this if the router to be managed is not the router at router_address. This can be used to access a remote router using a local router as a proxy. @param edge_id: like router_id except remote is an edge router. Note that router_id and edge_id are mutually exclusive """ assert (not (edge_id and router_id)) self._conn = BlockingConnection(router_address, timeout=timeout) if router_id: self._mgmt_address = u'_topo/0/%s/$management' % router_id elif edge_id: self._mgmt_address = u'_edge/%s/$management' % edge_id else: self._mgmt_address = u'$management' self._client = SyncRequestResponse(self._conn, self._mgmt_address) def close(self): if self._conn: try: self._conn.close() except Exception: pass self._conn = None def _request_msg(self, properties, body=None): """ Create a management request message """ req = Message() req.properties = properties req.body = body or {} return req def read(self, type, identity): """ Return the entity of type 'type' with identity 'identity' as a map """ request = self._request_msg(properties={ u'operation': u'READ', u'type': type, u'identity': identity }) try: response = self._client.call(request) except ProtonException: LOG.error("Read error - connection to router failed.") return None if response.properties.get(u'statusDescription') != 'OK': LOG.warning("Management read of type %s id %s failed: %s" % type, identity, response.properties) return None return response.body def delete(self, type, identity): """ Delete the configuration entry of type 'type' with identity 'identity' """ request = self._request_msg(properties={ u'operation': u'DELETE', u'type': type, u'identity': identity }) try: response = self._client.call(request) except ProtonException: LOG.error("Delete error - connection to router failed.") return None status = response.properties.get(u'statusCode') if status != 204: # delete failed LOG.warning("Management delete of type %s id %s failed: %s", type, identity, response.properties) return status return None def query(self, type, attribute_names): """ Query the router for all entities of type 'type'. For each entity only return the values for the given attributes """ class QueryIterator(object): """ Helper object that provides an iterator over the results returned by the QUERY operation. Each iteration returns a single entity represented by a map of values keyed by the attribute name @param attribute_names: ordered list of attribute names returned by QUERY @param values: a list of ordered lists of values that correspond to a single entity. The values are in the same order as attributes """ def __init__(self, attribute_names, values): self._attribute_names = attribute_names self._values = values def __iter__(self): return self # for 2.x capatibility def next(self): return self.__next__() def __next__(self): try: v = self._values.pop() except IndexError: raise StopIteration return dict(zip(self._attribute_names, v)) MAX_BATCH = 500 # limit per request message (see bug PROTON-1846) response_results = [] response_attr_names = [] offset = 0 while True: request = self._request_msg( properties={ u'operation': u'QUERY', u'entityType': type, u'offset': offset, u'count': MAX_BATCH }, body={u'attributeNames': attribute_names}) try: response = self._client.call(request) except ProtonException: LOG.error("Query error - connection to router failed.") return None if response.properties.get(u'statusDescription') != 'OK': LOG.warning("Management query for type %s failed: %s" % type, response.properties) return None if not response_attr_names: response_attr_names.extend(response.body[u'attributeNames']) response_results.extend(response.body[u'results']) if len(response.body[u'results']) < MAX_BATCH: break offset += MAX_BATCH return QueryIterator(response_attr_names, response_results)
parser = optparse.OptionParser( usage="usage: %prog [options]", description="Send requests to the supplied address and print responses.") parser.add_option("-a", "--address", default="localhost:5672/examples", help="address to which messages are sent (default %default)") parser.add_option("-t", "--timeout", type="float", default=5, help="Give up after this time out (default %default)") opts, args = parser.parse_args() url = Url(opts.address) client = SyncRequestResponse(BlockingConnection(url, timeout=opts.timeout), url.path) try: REQUESTS = [ "Twas brillig, and the slithy toves", "Did gire and gymble in the wabe.", "All mimsy were the borogroves,", "And the mome raths outgrabe." ] for request in REQUESTS: response = client.call(Message(body=request)) print "%s => %s" % (request, response.body) finally: client.connection.close()
from __future__ import print_function, unicode_literals import optparse from proton import Message, Url, ConnectionException, Timeout from proton.utils import SyncRequestResponse, BlockingConnection from proton.handlers import IncomingMessageHandler import sys parser = optparse.OptionParser(usage="usage: %prog [options]", description="Send requests to the supplied address and print responses.") parser.add_option("-a", "--address", default="localhost:5672/examples", help="address to which messages are sent (default %default)") parser.add_option("-t", "--timeout", type="float", default=5, help="Give up after this time out (default %default)") opts, args = parser.parse_args() url = Url(opts.address) client = SyncRequestResponse(BlockingConnection(url, timeout=opts.timeout), url.path) try: REQUESTS= ["Twas brillig, and the slithy toves", "Did gire and gymble in the wabe.", "All mimsy were the borogroves,", "And the mome raths outgrabe."] for request in REQUESTS: response = client.call(Message(body=request)) print("%s => %s" % (request, response.body)) finally: client.connection.close()
class RouterCollector(object): def __init__(self, router_host, router_port, cert_dir): self.router_host = router_host self.router_port = router_port self.cert_dir = cert_dir ssl_domain = None allowed_mechs = [] sasl_enabled = False if self.cert_dir != None: ssl_domain = SSLDomain(SSLDomain.MODE_CLIENT) ssl_domain.set_trusted_ca_db( str(os.path.join(self.cert_dir, 'ca.crt'))) ssl_domain.set_credentials( str(os.path.join(self.cert_dir, "tls.crt")), str(os.path.join(self.cert_dir, "tls.key")), None) ssl_domain.set_peer_authentication(SSLDomain.VERIFY_PEER) allowed_mechs = str("EXTERNAL") sasl_enabled = True self.client = SyncRequestResponse( BlockingConnection("amqps://" + self.router_host + ":" + str(self.router_port), 30, None, ssl_domain, allowed_mechs=allowed_mechs, sasl_enabled=sasl_enabled), "$management") def create_collector_map(self): metrics = [ MetricCollector('connectionCount', 'Number of connections to router', ['container']), MetricCollector('connectionCount', 'Total number of connections to router', ['routerId'], id="totalConnectionCount"), MetricCollector('linkCount', 'Number of links to router', ['address']), MetricCollector('linkCount', 'Number of consumers to router', ['address'], id="consumerCount", filter={"linkDir": "out"}), MetricCollector('linkCount', 'Number of producers to router', ['address'], id="producerCount", filter={"linkDir": "in"}), MetricCollector('linkCount', 'Total number of links to router', ['routerId'], id="totalLinkCount"), MetricCollector('addrCount', 'Number of addresses defined in router', ['routerId']), MetricCollector('autoLinkCount', 'Number of auto links defined in router', ['routerId']), MetricCollector('linkRouteCount', 'Number of link routers defined in router', ['routerId']), MetricCollector('unsettledCount', 'Number of unsettled messages', ['address']), MetricCollector('deliveryCount', 'Number of delivered messages', ['address'], "COUNTER"), MetricCollector('releasedCount', 'Number of released messages', ['address'], "COUNTER"), MetricCollector('rejectedCount', 'Number of rejected messages', ['address'], "COUNTER"), MetricCollector('acceptedCount', 'Number of accepted messages', ['address'], "COUNTER"), MetricCollector('undeliveredCount', 'Number of undelivered messages', ['address']), MetricCollector('capacity', 'Capacity of link', ['address']) ] m = {} for metric in metrics: m[metric.id] = metric return m def create_entity_map(self, collector_map): return { self.get_router: [ collector_map['totalConnectionCount'], collector_map['totalLinkCount'], collector_map['addrCount'], collector_map['autoLinkCount'], collector_map['linkRouteCount'] ], self.get_connections: [collector_map['connectionCount']], self.get_links: [ collector_map['linkCount'], collector_map['unsettledCount'], collector_map['deliveryCount'], collector_map['releasedCount'], collector_map['rejectedCount'], collector_map['acceptedCount'], collector_map['undeliveredCount'], collector_map['capacity'], collector_map['consumerCount'], collector_map['producerCount'] ] } def get_router(self): return self.collect_metric('org.apache.qpid.dispatch.router') def get_links(self): links = self.collect_metric('org.apache.qpid.dispatch.router.link') if links == None: return links connections = self.get_connections() if connections == None: return connections links.add_field_from("address", "owningAddr", clean_address) links.add_field("linkCount", 1) links.add_field_from( "container", "connectionId", lambda connection_id: get_container_from_connections( connection_id, connections)) return links def get_connections(self): response = self.collect_metric('org.apache.qpid.dispatch.connection') if response == None: return response response.add_field("connectionCount", 1) return response def collect(self): collector_map = self.create_collector_map() fetcher_map = self.create_entity_map(collector_map) for fetcher in fetcher_map: response = fetcher() if response != None: for collector in fetcher_map[fetcher]: for entity in response.get_results(): if response.contains(entity, collector.filter): labels = [] for l in collector.labels: label_idx = response.get_index(l) if label_idx != None and entity[ label_idx] != None: labels.append(entity[label_idx]) else: labels.append("") value = entity[response.get_index(collector.name)] collector.add(labels, int(value)) for collector in collector_map.itervalues(): yield collector.metric() def collect_metric(self, entityType): try: properties = {} properties["entityType"] = entityType properties["operation"] = "QUERY" properties["name"] = "self" message = Message(body=None, properties=properties) response = self.client.call(message) if response == None: return response else: return RouterResponse(response) except NameError as e: print("Error querying router for metrics: %s" % e) return None