Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

qpid.py 70KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740
  1. """
  2. kombu.transport.qpid
  3. =======================
  4. `Qpid`_ transport using `qpid-python`_ as the client and `qpid-tools`_ for
  5. broker management.
  6. The use this transport you must install the necessary dependencies. These
  7. dependencies are available via PyPI and can be installed using the pip
  8. command:
  9. .. code-block:: console
  10. $ pip install kombu[qpid]
  11. or to install the requirements manually:
  12. .. code-block:: console
  13. $ pip install qpid-tools qpid-python
  14. .. admonition:: Python 3 and PyPy Limitations
  15. The Qpid transport does not support Python 3 or PyPy environments due
  16. to underlying dependencies not being compatible. This version is
  17. tested and works with with Python 2.7.
  18. .. _`Qpid`: http://qpid.apache.org/
  19. .. _`qpid-python`: http://pypi.python.org/pypi/qpid-python/
  20. .. _`qpid-tools`: http://pypi.python.org/pypi/qpid-tools/
  21. Authentication
  22. ==============
  23. This transport supports SASL authentication with the Qpid broker. Normally,
  24. SASL mechanisms are negotiated from a client list and a server list of
  25. possible mechanisms, but in practice, different SASL client libraries give
  26. different behaviors. These different behaviors cause the expected SASL
  27. mechanism to not be selected in many cases. As such, this transport restricts
  28. the mechanism types based on Kombu's configuration according to the following
  29. table.
  30. +------------------------------------+--------------------+
  31. | **Broker String** | **SASL Mechanism** |
  32. +------------------------------------+--------------------+
  33. | qpid://hostname/ | ANONYMOUS |
  34. +------------------------------------+--------------------+
  35. | qpid://username:password@hostname/ | PLAIN |
  36. +------------------------------------+--------------------+
  37. | see instructions below | EXTERNAL |
  38. +------------------------------------+--------------------+
  39. The user can override the above SASL selection behaviors and specify the SASL
  40. string using the :attr:`~kombu.Connection.login_method` argument to the
  41. :class:`~kombu.Connection` object. The string can be a single SASL mechanism
  42. or a space separated list of SASL mechanisms. If you are using Celery with
  43. Kombu, this can be accomplished by setting the *BROKER_LOGIN_METHOD* Celery
  44. option.
  45. .. note::
  46. While using SSL, Qpid users may want to override the SASL mechanism to
  47. use *EXTERNAL*. In that case, Qpid requires a username to be presented
  48. that matches the *CN* of the SSL client certificate. Ensure that the
  49. broker string contains the corresponding username. For example, if the
  50. client certificate has *CN=asdf* and the client connects to *example.com*
  51. on port 5671, the broker string should be:
  52. **qpid://asdf@example.com:5671/**
  53. Transport Options
  54. =================
  55. The :attr:`~kombu.Connection.transport_options` argument to the
  56. :class:`~kombu.Connection` object are passed directly to the
  57. :class:`qpid.messaging.endpoints.Connection` as keyword arguments. These
  58. options override and replace any other default or specified values. If using
  59. Celery, this can be accomplished by setting the
  60. *BROKER_TRANSPORT_OPTIONS* Celery option.
  61. """
  62. from __future__ import absolute_import
  63. import os
  64. import select
  65. import socket
  66. import ssl
  67. import sys
  68. import time
  69. import uuid
  70. from gettext import gettext as _
  71. import amqp.protocol
  72. try:
  73. import fcntl
  74. except ImportError:
  75. fcntl = None # noqa
  76. try:
  77. import qpidtoollibs
  78. except ImportError: # pragma: no cover
  79. qpidtoollibs = None # noqa
  80. try:
  81. from qpid.messaging.exceptions import ConnectionError, NotFound
  82. from qpid.messaging.exceptions import Empty as QpidEmpty
  83. from qpid.messaging.exceptions import SessionClosed
  84. except ImportError: # pragma: no cover
  85. ConnectionError = None
  86. NotFound = None
  87. QpidEmpty = None
  88. SessionClosed = None
  89. try:
  90. import qpid
  91. except ImportError: # pragma: no cover
  92. qpid = None
  93. from kombu.five import Empty, items
  94. from kombu.log import get_logger
  95. from kombu.transport.virtual import Base64, Message
  96. from kombu.transport import base
  97. from kombu.utils.compat import OrderedDict
  98. logger = get_logger(__name__)
  99. OBJECT_ALREADY_EXISTS_STRING = 'object already exists'
  100. VERSION = (1, 0, 0)
  101. __version__ = '.'.join(map(str, VERSION))
  102. PY3 = sys.version_info[0] == 3
  103. def dependency_is_none(dependency):
  104. """Return True if the dependency is None, otherwise False. This is done
  105. using a function so that tests can mock this behavior easily.
  106. :param dependency: The module to check if it is None
  107. :return: True if dependency is None otherwise False.
  108. """
  109. return dependency is None
  110. class AuthenticationFailure(Exception):
  111. pass
  112. class QoS(object):
  113. """A helper object for message prefetch and ACKing purposes.
  114. :keyword prefetch_count: Initial prefetch count, hard set to 1.
  115. :type prefetch_count: int
  116. NOTE: prefetch_count is currently hard set to 1, and needs to be improved
  117. This object is instantiated 1-for-1 with a
  118. :class:`~.kombu.transport.qpid.Channel` instance. QoS allows
  119. ``prefetch_count`` to be set to the number of outstanding messages
  120. the corresponding :class:`~kombu.transport.qpid.Channel` should be
  121. allowed to prefetch. Setting ``prefetch_count`` to 0 disables
  122. prefetch limits, and the object can hold an arbitrary number of messages.
  123. Messages are added using :meth:`append`, which are held until they are
  124. ACKed asynchronously through a call to :meth:`ack`. Messages that are
  125. received, but not ACKed will not be delivered by the broker to another
  126. consumer until an ACK is received, or the session is closed. Messages
  127. are referred to using delivery_tag, which are unique per
  128. :class:`Channel`. Delivery tags are managed outside of this object and
  129. are passed in with a message to :meth:`append`. Un-ACKed messages can
  130. be looked up from QoS using :meth:`get` and can be rejected and
  131. forgotten using :meth:`reject`.
  132. """
  133. def __init__(self, session, prefetch_count=1):
  134. self.session = session
  135. self.prefetch_count = 1
  136. self._not_yet_acked = OrderedDict()
  137. def can_consume(self):
  138. """Return True if the :class:`~kombu.transport.qpid.Channel` can
  139. consume more messages, else False.
  140. Used to ensure the client adheres to currently active prefetch
  141. limits.
  142. :returns: True, if this QoS object can accept more messages
  143. without violating the prefetch_count. If prefetch_count is 0,
  144. can_consume will always return True.
  145. :rtype: bool
  146. """
  147. return (
  148. not self.prefetch_count or
  149. len(self._not_yet_acked) < self.prefetch_count
  150. )
  151. def can_consume_max_estimate(self):
  152. """Return the remaining message capacity for the associated
  153. :class:`kombu.transport.qpid.Channel`.
  154. Returns an estimated number of outstanding messages that a
  155. :class:`kombu.transport.qpid.Channel` can accept without
  156. exceeding ``prefetch_count``. If ``prefetch_count`` is 0, then
  157. this method returns 1.
  158. :returns: The number of estimated messages that can be fetched
  159. without violating the prefetch_count.
  160. :rtype: int
  161. """
  162. return 1 if not self.prefetch_count else (
  163. self.prefetch_count - len(self._not_yet_acked)
  164. )
  165. def append(self, message, delivery_tag):
  166. """Append message to the list of un-ACKed messages.
  167. Add a message, referenced by the delivery_tag, for ACKing,
  168. rejecting, or getting later. Messages are saved into an
  169. :class:`collections.OrderedDict` by delivery_tag.
  170. :param message: A received message that has not yet been ACKed.
  171. :type message: qpid.messaging.Message
  172. :param delivery_tag: A UUID to refer to this message by
  173. upon receipt.
  174. :type delivery_tag: uuid.UUID
  175. """
  176. self._not_yet_acked[delivery_tag] = message
  177. def get(self, delivery_tag):
  178. """Get an un-ACKed message by delivery_tag. If called with an invalid
  179. delivery_tag a :exc:`KeyError` is raised.
  180. :param delivery_tag: The delivery tag associated with the message
  181. to be returned.
  182. :type delivery_tag: uuid.UUID
  183. :return: An un-ACKed message that is looked up by delivery_tag.
  184. :rtype: qpid.messaging.Message
  185. """
  186. return self._not_yet_acked[delivery_tag]
  187. def ack(self, delivery_tag):
  188. """Acknowledge a message by delivery_tag.
  189. Called asynchronously once the message has been handled and can be
  190. forgotten by the broker.
  191. :param delivery_tag: the delivery tag associated with the message
  192. to be acknowledged.
  193. :type delivery_tag: uuid.UUID
  194. """
  195. message = self._not_yet_acked.pop(delivery_tag)
  196. self.session.acknowledge(message=message)
  197. def reject(self, delivery_tag, requeue=False):
  198. """Reject a message by delivery_tag.
  199. Explicitly notify the broker that the channel associated
  200. with this QoS object is rejecting the message that was previously
  201. delivered.
  202. If requeue is False, then the message is not requeued for delivery
  203. to another consumer. If requeue is True, then the message is
  204. requeued for delivery to another consumer.
  205. :param delivery_tag: The delivery tag associated with the message
  206. to be rejected.
  207. :type delivery_tag: uuid.UUID
  208. :keyword requeue: If True, the broker will be notified to requeue
  209. the message. If False, the broker will be told to drop the
  210. message entirely. In both cases, the message will be removed
  211. from this object.
  212. :type requeue: bool
  213. """
  214. message = self._not_yet_acked.pop(delivery_tag)
  215. QpidDisposition = qpid.messaging.Disposition
  216. if requeue:
  217. disposition = QpidDisposition(qpid.messaging.RELEASED)
  218. else:
  219. disposition = QpidDisposition(qpid.messaging.REJECTED)
  220. self.session.acknowledge(message=message, disposition=disposition)
  221. class Channel(base.StdChannel):
  222. """Supports broker configuration and messaging send and receive.
  223. :param connection: A Connection object that this Channel can
  224. reference. Currently only used to access callbacks.
  225. :type connection: kombu.transport.qpid.Connection
  226. :param transport: The Transport this Channel is associated with.
  227. :type transport: kombu.transport.qpid.Transport
  228. A channel object is designed to have method-parity with a Channel as
  229. defined in AMQP 0-10 and earlier, which allows for the following broker
  230. actions:
  231. - exchange declare and delete
  232. - queue declare and delete
  233. - queue bind and unbind operations
  234. - queue length and purge operations
  235. - sending/receiving/rejecting messages
  236. - structuring, encoding, and decoding messages
  237. - supports synchronous and asynchronous reads
  238. - reading state about the exchange, queues, and bindings
  239. Channels are designed to all share a single TCP connection with a
  240. broker, but provide a level of isolated communication with the broker
  241. while benefiting from a shared TCP connection. The Channel is given
  242. its :class:`~kombu.transport.qpid.Connection` object by the
  243. :class:`~kombu.transport.qpid.Transport` that
  244. instantiates the channel.
  245. This channel inherits from :class:`~kombu.transport.base.StdChannel`,
  246. which makes this a 'native' channel versus a 'virtual' channel which
  247. would inherit from :class:`kombu.transports.virtual`.
  248. Messages sent using this channel are assigned a delivery_tag. The
  249. delivery_tag is generated for a message as they are prepared for
  250. sending by :meth:`basic_publish`. The delivery_tag is unique per
  251. channel instance. The delivery_tag has no meaningful context in other
  252. objects, and is only maintained in the memory of this object, and the
  253. underlying :class:`QoS` object that provides support.
  254. Each channel object instantiates exactly one :class:`QoS` object for
  255. prefetch limiting, and asynchronous ACKing. The :class:`QoS` object is
  256. lazily instantiated through a property method :meth:`qos`. The
  257. :class:`QoS` object is a supporting object that should not be accessed
  258. directly except by the channel itself.
  259. Synchronous reads on a queue are done using a call to :meth:`basic_get`
  260. which uses :meth:`_get` to perform the reading. These methods read
  261. immediately and do not accept any form of timeout. :meth:`basic_get`
  262. reads synchronously and ACKs messages before returning them. ACKing is
  263. done in all cases, because an application that reads messages using
  264. qpid.messaging, but does not ACK them will experience a memory leak.
  265. The no_ack argument to :meth:`basic_get` does not affect ACKing
  266. functionality.
  267. Asynchronous reads on a queue are done by starting a consumer using
  268. :meth:`basic_consume`. Each call to :meth:`basic_consume` will cause a
  269. :class:`~qpid.messaging.endpoints.Receiver` to be created on the
  270. :class:`~qpid.messaging.endpoints.Session` started by the :class:
  271. `Transport`. The receiver will asynchronously read using
  272. qpid.messaging, and prefetch messages before the call to
  273. :meth:`Transport.basic_drain` occurs. The prefetch_count value of the
  274. :class:`QoS` object is the capacity value of the new receiver. The new
  275. receiver capacity must always be at least 1, otherwise none of the
  276. receivers will appear to be ready for reading, and will never be read
  277. from.
  278. Each call to :meth:`basic_consume` creates a consumer, which is given a
  279. consumer tag that is identified by the caller of :meth:`basic_consume`.
  280. Already started consumers can be cancelled using by their consumer_tag
  281. using :meth:`basic_cancel`. Cancellation of a consumer causes the
  282. :class:`~qpid.messaging.endpoints.Receiver` object to be closed.
  283. Asynchronous message ACKing is supported through :meth:`basic_ack`,
  284. and is referenced by delivery_tag. The Channel object uses its
  285. :class:`QoS` object to perform the message ACKing.
  286. """
  287. #: A class reference that will be instantiated using the qos property.
  288. QoS = QoS
  289. #: A class reference that identifies
  290. # :class:`~kombu.transport.virtual.Message` as the message class type
  291. Message = Message
  292. #: Default body encoding.
  293. #: NOTE: ``transport_options['body_encoding']`` will override this value.
  294. body_encoding = 'base64'
  295. #: Binary <-> ASCII codecs.
  296. codecs = {'base64': Base64()}
  297. def __init__(self, connection, transport):
  298. self.connection = connection
  299. self.transport = transport
  300. qpid_connection = connection.get_qpid_connection()
  301. self._broker = qpidtoollibs.BrokerAgent(qpid_connection)
  302. self.closed = False
  303. self._tag_to_queue = {}
  304. self._receivers = {}
  305. self._qos = None
  306. def _get(self, queue):
  307. """Non-blocking, single-message read from a queue.
  308. An internal method to perform a non-blocking, single-message read
  309. from a queue by name. This method creates a
  310. :class:`~qpid.messaging.endpoints.Receiver` to read from the queue
  311. using the :class:`~qpid.messaging.endpoints.Session` saved on the
  312. associated :class:`~kombu.transport.qpid.Transport`. The receiver
  313. is closed before the method exits. If a message is available, a
  314. :class:`qpid.messaging.Message` object is returned. If no message is
  315. available, a :class:`qpid.messaging.exceptions.Empty` exception is
  316. raised.
  317. This is an internal method. External calls for get functionality
  318. should be done using :meth:`basic_get`.
  319. :param queue: The queue name to get the message from
  320. :type queue: str
  321. :return: The received message.
  322. :rtype: :class:`qpid.messaging.Message`
  323. :raises: :class:`qpid.messaging.exceptions.Empty` if no
  324. message is available.
  325. """
  326. rx = self.transport.session.receiver(queue)
  327. try:
  328. message = rx.fetch(timeout=0)
  329. finally:
  330. rx.close()
  331. return message
  332. def _put(self, routing_key, message, exchange=None, **kwargs):
  333. """Synchronous send of a single message onto a queue or exchange.
  334. An internal method which synchronously sends a single message onto
  335. a given queue or exchange. If exchange is not specified,
  336. the message is sent directly to a queue specified by routing_key.
  337. If no queue is found by the name of routing_key while exchange is
  338. not specified an exception is raised. If an exchange is specified,
  339. then the message is delivered onto the requested
  340. exchange using routing_key. Message sending is synchronous using
  341. sync=True because large messages in kombu funtests were not being
  342. fully sent before the receiver closed.
  343. This method creates a :class:`qpid.messaging.endpoints.Sender` to
  344. send the message to the queue using the
  345. :class:`qpid.messaging.endpoints.Session` created and referenced by
  346. the associated :class:`~kombu.transport.qpid.Transport`. The sender
  347. is closed before the method exits.
  348. External calls for put functionality should be done using
  349. :meth:`basic_publish`.
  350. :param routing_key: If exchange is None, treated as the queue name
  351. to send the message to. If exchange is not None, treated as the
  352. routing_key to use as the message is submitted onto the exchange.
  353. :type routing_key: str
  354. :param message: The message to be sent as prepared by
  355. :meth:`basic_publish`.
  356. :type message: dict
  357. :keyword exchange: keyword parameter of the exchange this message
  358. should be sent on. If no exchange is specified, the message is
  359. sent directly to a queue specified by routing_key.
  360. :type exchange: str
  361. """
  362. if not exchange:
  363. address = '%s; {assert: always, node: {type: queue}}' % (
  364. routing_key,)
  365. msg_subject = None
  366. else:
  367. address = '%s/%s; {assert: always, node: {type: topic}}' % (
  368. exchange, routing_key)
  369. msg_subject = str(routing_key)
  370. sender = self.transport.session.sender(address)
  371. qpid_message = qpid.messaging.Message(content=message,
  372. subject=msg_subject)
  373. try:
  374. sender.send(qpid_message, sync=True)
  375. finally:
  376. sender.close()
  377. def _purge(self, queue):
  378. """Purge all undelivered messages from a queue specified by name.
  379. An internal method to purge all undelivered messages from a queue
  380. specified by name. If the queue does not exist a
  381. :class:`qpid.messaging.exceptions.NotFound` exception is raised.
  382. The queue message depth is first checked, and then the broker is
  383. asked to purge that number of messages. The integer number of
  384. messages requested to be purged is returned. The actual number of
  385. messages purged may be different than the requested number of
  386. messages to purge (see below).
  387. Sometimes delivered messages are asked to be purged, but are not.
  388. This case fails silently, which is the correct behavior when a
  389. message that has been delivered to a different consumer, who has
  390. not ACKed the message, and still has an active session with the
  391. broker. Messages in that case are not safe for purging and will be
  392. retained by the broker. The client is unable to change this
  393. delivery behavior.
  394. This is an internal method. External calls for purge functionality
  395. should be done using :meth:`queue_purge`.
  396. :param queue: the name of the queue to be purged
  397. :type queue: str
  398. :return: The number of messages requested to be purged.
  399. :rtype: int
  400. :raises: :class:`qpid.messaging.exceptions.NotFound` if the queue
  401. being purged cannot be found.
  402. """
  403. queue_to_purge = self._broker.getQueue(queue)
  404. if queue_to_purge is None:
  405. error_text = "NOT_FOUND - no queue '{0}'".format(queue)
  406. raise NotFound(code=404, text=error_text)
  407. message_count = queue_to_purge.values['msgDepth']
  408. if message_count > 0:
  409. queue_to_purge.purge(message_count)
  410. return message_count
  411. def _size(self, queue):
  412. """Get the number of messages in a queue specified by name.
  413. An internal method to return the number of messages in a queue
  414. specified by name. It returns an integer count of the number
  415. of messages currently in the queue.
  416. :param queue: The name of the queue to be inspected for the number
  417. of messages
  418. :type queue: str
  419. :return the number of messages in the queue specified by name.
  420. :rtype: int
  421. """
  422. queue_to_check = self._broker.getQueue(queue)
  423. message_depth = queue_to_check.values['msgDepth']
  424. return message_depth
  425. def _delete(self, queue, *args, **kwargs):
  426. """Delete a queue and all messages on that queue.
  427. An internal method to delete a queue specified by name and all the
  428. messages on it. First, all messages are purged from a queue using a
  429. call to :meth:`_purge`. Second, the broker is asked to delete the
  430. queue.
  431. This is an internal method. External calls for queue delete
  432. functionality should be done using :meth:`queue_delete`.
  433. :param queue: The name of the queue to be deleted.
  434. :type queue: str
  435. """
  436. self._purge(queue)
  437. self._broker.delQueue(queue)
  438. def _has_queue(self, queue, **kwargs):
  439. """Determine if the broker has a queue specified by name.
  440. :param queue: The queue name to check if the queue exists.
  441. :type queue: str
  442. :return: True if a queue exists on the broker, and false
  443. otherwise.
  444. :rtype: bool
  445. """
  446. if self._broker.getQueue(queue):
  447. return True
  448. else:
  449. return False
  450. def queue_declare(self, queue, passive=False, durable=False,
  451. exclusive=False, auto_delete=True, nowait=False,
  452. arguments=None):
  453. """Create a new queue specified by name.
  454. If the queue already exists, no change is made to the queue,
  455. and the return value returns information about the existing queue.
  456. The queue name is required and specified as the first argument.
  457. If passive is True, the server will not create the queue. The
  458. client can use this to check whether a queue exists without
  459. modifying the server state. Default is False.
  460. If durable is True, the queue will be durable. Durable queues
  461. remain active when a server restarts. Non-durable queues (
  462. transient queues) are purged if/when a server restarts. Note that
  463. durable queues do not necessarily hold persistent messages,
  464. although it does not make sense to send persistent messages to a
  465. transient queue. Default is False.
  466. If exclusive is True, the queue will be exclusive. Exclusive queues
  467. may only be consumed by the current connection. Setting the
  468. 'exclusive' flag always implies 'auto-delete'. Default is False.
  469. If auto_delete is True, the queue is deleted when all consumers
  470. have finished using it. The last consumer can be cancelled either
  471. explicitly or because its channel is closed. If there was no
  472. consumer ever on the queue, it won't be deleted. Default is True.
  473. The nowait parameter is unused. It was part of the 0-9-1 protocol,
  474. but this AMQP client implements 0-10 which removed the nowait option.
  475. The arguments parameter is a set of arguments for the declaration of
  476. the queue. Arguments are passed as a dict or None. This field is
  477. ignored if passive is True. Default is None.
  478. This method returns a :class:`~collections.namedtuple` with the name
  479. 'queue_declare_ok_t' and the queue name as 'queue', message count
  480. on the queue as 'message_count', and the number of active consumers
  481. as 'consumer_count'. The named tuple values are ordered as queue,
  482. message_count, and consumer_count respectively.
  483. Due to Celery's non-ACKing of events, a ring policy is set on any
  484. queue that starts with the string 'celeryev' or ends with the string
  485. 'pidbox'. These are celery event queues, and Celery does not ack
  486. them, causing the messages to build-up. Eventually Qpid stops serving
  487. messages unless the 'ring' policy is set, at which point the buffer
  488. backing the queue becomes circular.
  489. :param queue: The name of the queue to be created.
  490. :type queue: str
  491. :param passive: If True, the sever will not create the queue.
  492. :type passive: bool
  493. :param durable: If True, the queue will be durable.
  494. :type durable: bool
  495. :param exclusive: If True, the queue will be exclusive.
  496. :type exclusive: bool
  497. :param auto_delete: If True, the queue is deleted when all
  498. consumers have finished using it.
  499. :type auto_delete: bool
  500. :param nowait: This parameter is unused since the 0-10
  501. specification does not include it.
  502. :type nowait: bool
  503. :param arguments: A set of arguments for the declaration of the
  504. queue.
  505. :type arguments: dict or None
  506. :return: A named tuple representing the declared queue as a named
  507. tuple. The tuple values are ordered as queue, message count,
  508. and the active consumer count.
  509. :rtype: :class:`~collections.namedtuple`
  510. """
  511. options = {'passive': passive,
  512. 'durable': durable,
  513. 'exclusive': exclusive,
  514. 'auto-delete': auto_delete,
  515. 'arguments': arguments}
  516. if queue.startswith('celeryev') or queue.endswith('pidbox'):
  517. options['qpid.policy_type'] = 'ring'
  518. try:
  519. self._broker.addQueue(queue, options=options)
  520. except Exception as exc:
  521. if OBJECT_ALREADY_EXISTS_STRING not in str(exc):
  522. raise exc
  523. queue_to_check = self._broker.getQueue(queue)
  524. message_count = queue_to_check.values['msgDepth']
  525. consumer_count = queue_to_check.values['consumerCount']
  526. return amqp.protocol.queue_declare_ok_t(queue, message_count,
  527. consumer_count)
  528. def queue_delete(self, queue, if_unused=False, if_empty=False, **kwargs):
  529. """Delete a queue by name.
  530. Delete a queue specified by name. Using the if_unused keyword
  531. argument, the delete can only occur if there are 0 consumers bound
  532. to it. Using the if_empty keyword argument, the delete can only
  533. occur if there are 0 messages in the queue.
  534. :param queue: The name of the queue to be deleted.
  535. :type queue: str
  536. :keyword if_unused: If True, delete only if the queue has 0
  537. consumers. If False, delete a queue even with consumers bound
  538. to it.
  539. :type if_unused: bool
  540. :keyword if_empty: If True, only delete the queue if it is empty. If
  541. False, delete the queue if it is empty or not.
  542. :type if_empty: bool
  543. """
  544. if self._has_queue(queue):
  545. if if_empty and self._size(queue):
  546. return
  547. queue_obj = self._broker.getQueue(queue)
  548. consumer_count = queue_obj.getAttributes()['consumerCount']
  549. if if_unused and consumer_count > 0:
  550. return
  551. self._delete(queue)
  552. def exchange_declare(self, exchange='', type='direct', durable=False,
  553. **kwargs):
  554. """Create a new exchange.
  555. Create an exchange of a specific type, and optionally have the
  556. exchange be durable. If an exchange of the requested name already
  557. exists, no action is taken and no exceptions are raised. Durable
  558. exchanges will survive a broker restart, non-durable exchanges will
  559. not.
  560. Exchanges provide behaviors based on their type. The expected
  561. behaviors are those defined in the AMQP 0-10 and prior
  562. specifications including 'direct', 'topic', and 'fanout'
  563. functionality.
  564. :keyword type: The exchange type. Valid values include 'direct',
  565. 'topic', and 'fanout'.
  566. :type type: str
  567. :keyword exchange: The name of the exchange to be created. If no
  568. exchange is specified, then a blank string will be used as the
  569. name.
  570. :type exchange: str
  571. :keyword durable: True if the exchange should be durable, or False
  572. otherwise.
  573. :type durable: bool
  574. """
  575. options = {'durable': durable}
  576. try:
  577. self._broker.addExchange(type, exchange, options)
  578. except Exception as exc:
  579. if OBJECT_ALREADY_EXISTS_STRING not in str(exc):
  580. raise exc
  581. def exchange_delete(self, exchange_name, **kwargs):
  582. """Delete an exchange specified by name
  583. :param exchange_name: The name of the exchange to be deleted.
  584. :type exchange_name: str
  585. """
  586. self._broker.delExchange(exchange_name)
  587. def queue_bind(self, queue, exchange, routing_key, **kwargs):
  588. """Bind a queue to an exchange with a bind key.
  589. Bind a queue specified by name, to an exchange specified by name,
  590. with a specific bind key. The queue and exchange must already
  591. exist on the broker for the bind to complete successfully. Queues
  592. may be bound to exchanges multiple times with different keys.
  593. :param queue: The name of the queue to be bound.
  594. :type queue: str
  595. :param exchange: The name of the exchange that the queue should be
  596. bound to.
  597. :type exchange: str
  598. :param routing_key: The bind key that the specified queue should
  599. bind to the specified exchange with.
  600. :type routing_key: str
  601. """
  602. self._broker.bind(exchange, queue, routing_key)
  603. def queue_unbind(self, queue, exchange, routing_key, **kwargs):
  604. """Unbind a queue from an exchange with a given bind key.
  605. Unbind a queue specified by name, from an exchange specified by
  606. name, that is already bound with a bind key. The queue and
  607. exchange must already exist on the broker, and bound with the bind
  608. key for the operation to complete successfully. Queues may be
  609. bound to exchanges multiple times with different keys, thus the
  610. bind key is a required field to unbind in an explicit way.
  611. :param queue: The name of the queue to be unbound.
  612. :type queue: str
  613. :param exchange: The name of the exchange that the queue should be
  614. unbound from.
  615. :type exchange: str
  616. :param routing_key: The existing bind key between the specified
  617. queue and a specified exchange that should be unbound.
  618. :type routing_key: str
  619. """
  620. self._broker.unbind(exchange, queue, routing_key)
  621. def queue_purge(self, queue, **kwargs):
  622. """Remove all undelivered messages from queue.
  623. Purge all undelivered messages from a queue specified by name. If the
  624. queue does not exist an exception is raised. The queue message
  625. depth is first checked, and then the broker is asked to purge that
  626. number of messages. The integer number of messages requested to be
  627. purged is returned. The actual number of messages purged may be
  628. different than the requested number of messages to purge.
  629. Sometimes delivered messages are asked to be purged, but are not.
  630. This case fails silently, which is the correct behavior when a
  631. message that has been delivered to a different consumer, who has
  632. not ACKed the message, and still has an active session with the
  633. broker. Messages in that case are not safe for purging and will be
  634. retained by the broker. The client is unable to change this
  635. delivery behavior.
  636. Internally, this method relies on :meth:`_purge`.
  637. :param queue: The name of the queue which should have all messages
  638. removed.
  639. :type queue: str
  640. :return: The number of messages requested to be purged.
  641. :rtype: int
  642. :raises: :class:`qpid.messaging.exceptions.NotFound` if the queue
  643. being purged cannot be found.
  644. """
  645. return self._purge(queue)
  646. def basic_get(self, queue, no_ack=False, **kwargs):
  647. """Non-blocking single message get and ACK from a queue by name.
  648. Internally this method uses :meth:`_get` to fetch the message. If
  649. an :class:`~qpid.messaging.exceptions.Empty` exception is raised by
  650. :meth:`_get`, this method silences it and returns None. If
  651. :meth:`_get` does return a message, that message is ACKed. The no_ack
  652. parameter has no effect on ACKing behavior, and all messages are
  653. ACKed in all cases. This method never adds fetched Messages to the
  654. internal QoS object for asynchronous ACKing.
  655. This method converts the object type of the method as it passes
  656. through. Fetching from the broker, :meth:`_get` returns a
  657. :class:`qpid.messaging.Message`, but this method takes the payload
  658. of the :class:`qpid.messaging.Message` and instantiates a
  659. :class:`~kombu.transport.virtual.Message` object with the payload
  660. based on the class setting of self.Message.
  661. :param queue: The queue name to fetch a message from.
  662. :type queue: str
  663. :keyword no_ack: The no_ack parameter has no effect on the ACK
  664. behavior of this method. Un-ACKed messages create a memory leak in
  665. qpid.messaging, and need to be ACKed in all cases.
  666. :type noack: bool
  667. :return: The received message.
  668. :rtype: :class:`~kombu.transport.virtual.Message`
  669. """
  670. try:
  671. qpid_message = self._get(queue)
  672. raw_message = qpid_message.content
  673. message = self.Message(self, raw_message)
  674. self.transport.session.acknowledge(message=qpid_message)
  675. return message
  676. except Empty:
  677. pass
  678. def basic_ack(self, delivery_tag):
  679. """Acknowledge a message by delivery_tag.
  680. Acknowledges a message referenced by delivery_tag. Messages can
  681. only be ACKed using :meth:`basic_ack` if they were acquired using
  682. :meth:`basic_consume`. This is the ACKing portion of the
  683. asynchronous read behavior.
  684. Internally, this method uses the :class:`QoS` object, which stores
  685. messages and is responsible for the ACKing.
  686. :param delivery_tag: The delivery tag associated with the message
  687. to be acknowledged.
  688. :type delivery_tag: uuid.UUID
  689. """
  690. self.qos.ack(delivery_tag)
  691. def basic_reject(self, delivery_tag, requeue=False):
  692. """Reject a message by delivery_tag.
  693. Rejects a message that has been received by the Channel, but not
  694. yet acknowledged. Messages are referenced by their delivery_tag.
  695. If requeue is False, the rejected message will be dropped by the
  696. broker and not delivered to any other consumers. If requeue is
  697. True, then the rejected message will be requeued for delivery to
  698. another consumer, potentially to the same consumer who rejected the
  699. message previously.
  700. :param delivery_tag: The delivery tag associated with the message
  701. to be rejected.
  702. :type delivery_tag: uuid.UUID
  703. :keyword requeue: If False, the rejected message will be dropped by
  704. the broker and not delivered to any other consumers. If True,
  705. then the rejected message will be requeued for delivery to
  706. another consumer, potentially to the same consumer who rejected
  707. the message previously.
  708. :type requeue: bool
  709. """
  710. self.qos.reject(delivery_tag, requeue=requeue)
  711. def basic_consume(self, queue, no_ack, callback, consumer_tag, **kwargs):
  712. """Start an asynchronous consumer that reads from a queue.
  713. This method starts a consumer of type
  714. :class:`~qpid.messaging.endpoints.Receiver` using the
  715. :class:`~qpid.messaging.endpoints.Session` created and referenced by
  716. the :class:`Transport` that reads messages from a queue
  717. specified by name until stopped by a call to :meth:`basic_cancel`.
  718. Messages are available later through a synchronous call to
  719. :meth:`Transport.drain_events`, which will drain from the consumer
  720. started by this method. :meth:`Transport.drain_events` is
  721. synchronous, but the receiving of messages over the network occurs
  722. asynchronously, so it should still perform well.
  723. :meth:`Transport.drain_events` calls the callback provided here with
  724. the Message of type self.Message.
  725. Each consumer is referenced by a consumer_tag, which is provided by
  726. the caller of this method.
  727. This method sets up the callback onto the self.connection object in a
  728. dict keyed by queue name. :meth:`~Transport.drain_events` is
  729. responsible for calling that callback upon message receipt.
  730. All messages that are received are added to the QoS object to be
  731. saved for asynchronous ACKing later after the message has been
  732. handled by the caller of :meth:`~Transport.drain_events`. Messages
  733. can be ACKed after being received through a call to :meth:`basic_ack`.
  734. If no_ack is True, The no_ack flag indicates that the receiver of
  735. the message will not call :meth:`basic_ack` later. Since the
  736. message will not be ACKed later, it is ACKed immediately.
  737. :meth:`basic_consume` transforms the message object type prior to
  738. calling the callback. Initially the message comes in as a
  739. :class:`qpid.messaging.Message`. This method unpacks the payload
  740. of the :class:`qpid.messaging.Message` and creates a new object of
  741. type self.Message.
  742. This method wraps the user delivered callback in a runtime-built
  743. function which provides the type transformation from
  744. :class:`qpid.messaging.Message` to
  745. :class:`~kombu.transport.virtual.Message`, and adds the message to
  746. the associated :class:`QoS` object for asynchronous ACKing
  747. if necessary.
  748. :param queue: The name of the queue to consume messages from
  749. :type queue: str
  750. :param no_ack: If True, then messages will not be saved for ACKing
  751. later, but will be ACKed immediately. If False, then messages
  752. will be saved for ACKing later with a call to :meth:`basic_ack`.
  753. :type no_ack: bool
  754. :param callback: a callable that will be called when messages
  755. arrive on the queue.
  756. :type callback: a callable object
  757. :param consumer_tag: a tag to reference the created consumer by.
  758. This consumer_tag is needed to cancel the consumer.
  759. :type consumer_tag: an immutable object
  760. """
  761. self._tag_to_queue[consumer_tag] = queue
  762. def _callback(qpid_message):
  763. raw_message = qpid_message.content
  764. message = self.Message(self, raw_message)
  765. delivery_tag = message.delivery_tag
  766. self.qos.append(qpid_message, delivery_tag)
  767. if no_ack:
  768. # Celery will not ack this message later, so we should ack now
  769. self.basic_ack(delivery_tag)
  770. return callback(message)
  771. self.connection._callbacks[queue] = _callback
  772. new_receiver = self.transport.session.receiver(queue)
  773. new_receiver.capacity = self.qos.prefetch_count
  774. self._receivers[consumer_tag] = new_receiver
  775. def basic_cancel(self, consumer_tag):
  776. """Cancel consumer by consumer tag.
  777. Request the consumer stops reading messages from its queue. The
  778. consumer is a :class:`~qpid.messaging.endpoints.Receiver`, and it is
  779. closed using :meth:`~qpid.messaging.endpoints.Receiver.close`.
  780. This method also cleans up all lingering references of the consumer.
  781. :param consumer_tag: The tag which refers to the consumer to be
  782. cancelled. Originally specified when the consumer was created
  783. as a parameter to :meth:`basic_consume`.
  784. :type consumer_tag: an immutable object
  785. """
  786. if consumer_tag in self._receivers:
  787. receiver = self._receivers.pop(consumer_tag)
  788. receiver.close()
  789. queue = self._tag_to_queue.pop(consumer_tag, None)
  790. self.connection._callbacks.pop(queue, None)
  791. def close(self):
  792. """Cancel all associated messages and close the Channel.
  793. This cancels all consumers by calling :meth:`basic_cancel` for each
  794. known consumer_tag. It also closes the self._broker sessions. Closing
  795. the sessions implicitly causes all outstanding, un-ACKed messages to
  796. be considered undelivered by the broker.
  797. """
  798. if not self.closed:
  799. self.closed = True
  800. for consumer_tag in self._receivers.keys():
  801. self.basic_cancel(consumer_tag)
  802. if self.connection is not None:
  803. self.connection.close_channel(self)
  804. self._broker.close()
  805. @property
  806. def qos(self):
  807. """:class:`QoS` manager for this channel.
  808. Lazily instantiates an object of type :class:`QoS` upon access to
  809. the self.qos attribute.
  810. :return: An already existing, or newly created QoS object
  811. :rtype: :class:`QoS`
  812. """
  813. if self._qos is None:
  814. self._qos = self.QoS(self.transport.session)
  815. return self._qos
  816. def basic_qos(self, prefetch_count, *args):
  817. """Change :class:`QoS` settings for this Channel.
  818. Set the number of un-acknowledged messages this Channel can fetch and
  819. hold. The prefetch_value is also used as the capacity for any new
  820. :class:`~qpid.messaging.endpoints.Receiver` objects.
  821. Currently, this value is hard coded to 1.
  822. :param prefetch_count: Not used. This method is hard-coded to 1.
  823. :type prefetch_count: int
  824. """
  825. self.qos.prefetch_count = 1
  826. def prepare_message(self, body, priority=None, content_type=None,
  827. content_encoding=None, headers=None, properties=None):
  828. """Prepare message data for sending.
  829. This message is typically called by
  830. :meth:`kombu.messaging.Producer._publish` as a preparation step in
  831. message publication.
  832. :param body: The body of the message
  833. :type body: str
  834. :keyword priority: A number between 0 and 9 that sets the priority of
  835. the message.
  836. :type priority: int
  837. :keyword content_type: The content_type the message body should be
  838. treated as. If this is unset, the
  839. :class:`qpid.messaging.endpoints.Sender` object tries to
  840. autodetect the content_type from the body.
  841. :type content_type: str
  842. :keyword content_encoding: The content_encoding the message body is
  843. encoded as.
  844. :type content_encoding: str
  845. :keyword headers: Additional Message headers that should be set.
  846. Passed in as a key-value pair.
  847. :type headers: dict
  848. :keyword properties: Message properties to be set on the message.
  849. :type properties: dict
  850. :return: Returns a dict object that encapsulates message
  851. attributes. See parameters for more details on attributes that
  852. can be set.
  853. :rtype: dict
  854. """
  855. properties = properties or {}
  856. info = properties.setdefault('delivery_info', {})
  857. info['priority'] = priority or 0
  858. return {'body': body,
  859. 'content-encoding': content_encoding,
  860. 'content-type': content_type,
  861. 'headers': headers or {},
  862. 'properties': properties or {}}
  863. def basic_publish(self, message, exchange, routing_key, **kwargs):
  864. """Publish message onto an exchange using a routing key.
  865. Publish a message onto an exchange specified by name using a
  866. routing key specified by routing_key. Prepares the message in the
  867. following ways before sending:
  868. - encodes the body using :meth:`encode_body`
  869. - wraps the body as a buffer object, so that
  870. :class:`qpid.messaging.endpoints.Sender` uses a content type
  871. that can support arbitrarily large messages.
  872. - sets delivery_tag to a random uuid.UUID
  873. - sets the exchange and routing_key info as delivery_info
  874. Internally uses :meth:`_put` to send the message synchronously. This
  875. message is typically called by
  876. :class:`kombu.messaging.Producer._publish` as the final step in
  877. message publication.
  878. :param message: A dict containing key value pairs with the message
  879. data. A valid message dict can be generated using the
  880. :meth:`prepare_message` method.
  881. :type message: dict
  882. :param exchange: The name of the exchange to submit this message
  883. onto.
  884. :type exchange: str
  885. :param routing_key: The routing key to be used as the message is
  886. submitted onto the exchange.
  887. :type routing_key: str
  888. """
  889. message['body'], body_encoding = self.encode_body(
  890. message['body'], self.body_encoding,
  891. )
  892. message['body'] = buffer(message['body'])
  893. props = message['properties']
  894. props.update(
  895. body_encoding=body_encoding,
  896. delivery_tag=uuid.uuid4(),
  897. )
  898. props['delivery_info'].update(
  899. exchange=exchange,
  900. routing_key=routing_key,
  901. )
  902. self._put(routing_key, message, exchange, **kwargs)
  903. def encode_body(self, body, encoding=None):
  904. """Encode a body using an optionally specified encoding.
  905. The encoding can be specified by name, and is looked up in
  906. self.codecs. self.codecs uses strings as its keys which specify
  907. the name of the encoding, and then the value is an instantiated
  908. object that can provide encoding/decoding of that type through
  909. encode and decode methods.
  910. :param body: The body to be encoded.
  911. :type body: str
  912. :keyword encoding: The encoding type to be used. Must be a supported
  913. codec listed in self.codecs.
  914. :type encoding: str
  915. :return: If encoding is specified, return a tuple with the first
  916. position being the encoded body, and the second position the
  917. encoding used. If encoding is not specified, the body is passed
  918. through unchanged.
  919. :rtype: tuple
  920. """
  921. if encoding:
  922. return self.codecs.get(encoding).encode(body), encoding
  923. return body, encoding
  924. def decode_body(self, body, encoding=None):
  925. """Decode a body using an optionally specified encoding.
  926. The encoding can be specified by name, and is looked up in
  927. self.codecs. self.codecs uses strings as its keys which specify
  928. the name of the encoding, and then the value is an instantiated
  929. object that can provide encoding/decoding of that type through
  930. encode and decode methods.
  931. :param body: The body to be encoded.
  932. :type body: str
  933. :keyword encoding: The encoding type to be used. Must be a supported
  934. codec listed in self.codecs.
  935. :type encoding: str
  936. :return: If encoding is specified, the decoded body is returned.
  937. If encoding is not specified, the body is returned unchanged.
  938. :rtype: str
  939. """
  940. if encoding:
  941. return self.codecs.get(encoding).decode(body)
  942. return body
  943. def typeof(self, exchange, default='direct'):
  944. """Get the exchange type.
  945. Lookup and return the exchange type for an exchange specified by
  946. name. Exchange types are expected to be 'direct', 'topic',
  947. and 'fanout', which correspond with exchange functionality as
  948. specified in AMQP 0-10 and earlier. If the exchange cannot be
  949. found, the default exchange type is returned.
  950. :param exchange: The exchange to have its type lookup up.
  951. :type exchange: str
  952. :keyword default: The type of exchange to assume if the exchange does
  953. not exist.
  954. :type default: str
  955. :return: The exchange type either 'direct', 'topic', or 'fanout'.
  956. :rtype: str
  957. """
  958. qpid_exchange = self._broker.getExchange(exchange)
  959. if qpid_exchange:
  960. qpid_exchange_attributes = qpid_exchange.getAttributes()
  961. return qpid_exchange_attributes['type']
  962. else:
  963. return default
  964. class Connection(object):
  965. """Encapsulate a connection object for the
  966. :class:`~kombu.transport.qpid.Transport`.
  967. :param host: The host that connections should connect to.
  968. :param port: The port that connection should connect to.
  969. :param username: The username that connections should connect with.
  970. Optional.
  971. :param password: The password that connections should connect with.
  972. Optional but requires a username.
  973. :param transport: The transport type that connections should use.
  974. Either 'tcp', or 'ssl' are expected as values.
  975. :param timeout: the timeout used when a Connection connects
  976. to the broker.
  977. :param sasl_mechanisms: The sasl authentication mechanism type to use.
  978. refer to SASL documentation for an explanation of valid
  979. values.
  980. .. note::
  981. qpid.messaging has an AuthenticationFailure exception type, but
  982. instead raises a ConnectionError with a message that indicates an
  983. authentication failure occurred in those situations.
  984. ConnectionError is listed as a recoverable error type, so kombu
  985. will attempt to retry if a ConnectionError is raised. Retrying
  986. the operation without adjusting the credentials is not correct,
  987. so this method specifically checks for a ConnectionError that
  988. indicates an Authentication Failure occurred. In those
  989. situations, the error type is mutated while preserving the
  990. original message and raised so kombu will allow the exception to
  991. not be considered recoverable.
  992. A connection object is created by a
  993. :class:`~kombu.transport.qpid.Transport` during a call to
  994. :meth:`~kombu.transport.qpid.Transport.establish_connection`. The
  995. :class:`~kombu.transport.qpid.Transport` passes in
  996. connection options as keywords that should be used for any connections
  997. created. Each :class:`~kombu.transport.qpid.Transport` creates exactly
  998. one Connection.
  999. A Connection object maintains a reference to a
  1000. :class:`~qpid.messaging.endpoints.Connection` which can be accessed
  1001. through a bound getter method named :meth:`get_qpid_connection` method.
  1002. Each Channel uses a the Connection for each
  1003. :class:`~qpidtoollibs.BrokerAgent`, and the Transport maintains a session
  1004. for all senders and receivers.
  1005. The Connection object is also responsible for maintaining the
  1006. dictionary of references to callbacks that should be called when
  1007. messages are received. These callbacks are saved in _callbacks,
  1008. and keyed on the queue name associated with the received message. The
  1009. _callbacks are setup in :meth:`Channel.basic_consume`, removed in
  1010. :meth:`Channel.basic_cancel`, and called in
  1011. :meth:`Transport.drain_events`.
  1012. The following keys are expected to be passed in as keyword arguments
  1013. at a minimum:
  1014. All keyword arguments are collected into the connection_options dict
  1015. and passed directly through to
  1016. :meth:`qpid.messaging.endpoints.Connection.establish`.
  1017. """
  1018. # A class reference to the :class:`Channel` object
  1019. Channel = Channel
  1020. def __init__(self, **connection_options):
  1021. self.connection_options = connection_options
  1022. self.channels = []
  1023. self._callbacks = {}
  1024. self._qpid_conn = None
  1025. establish = qpid.messaging.Connection.establish
  1026. # There are several inconsistent behaviors in the sasl libraries
  1027. # used on different systems. Although qpid.messaging allows
  1028. # multiple space separated sasl mechanisms, this implementation
  1029. # only advertises one type to the server. These are either
  1030. # ANONYMOUS, PLAIN, or an overridden value specified by the user.
  1031. sasl_mech = connection_options['sasl_mechanisms']
  1032. try:
  1033. msg = _('Attempting to connect to qpid with '
  1034. 'SASL mechanism %s') % sasl_mech
  1035. logger.debug(msg)
  1036. self._qpid_conn = establish(**self.connection_options)
  1037. # connection was successful if we got this far
  1038. msg = _('Connected to qpid with SASL '
  1039. 'mechanism %s') % sasl_mech
  1040. logger.info(msg)
  1041. except ConnectionError as conn_exc:
  1042. # if we get one of these errors, do not raise an exception.
  1043. # Raising will cause the connection to be retried. Instead,
  1044. # just continue on to the next mech.
  1045. coded_as_auth_failure = getattr(conn_exc, 'code', None) == 320
  1046. contains_auth_fail_text = \
  1047. 'Authentication failed' in conn_exc.text
  1048. contains_mech_fail_text = \
  1049. 'sasl negotiation failed: no mechanism agreed' \
  1050. in conn_exc.text
  1051. contains_mech_unavail_text = 'no mechanism available' \
  1052. in conn_exc.text
  1053. if coded_as_auth_failure or \
  1054. contains_auth_fail_text or contains_mech_fail_text or \
  1055. contains_mech_unavail_text:
  1056. msg = _('Unable to connect to qpid with SASL '
  1057. 'mechanism %s') % sasl_mech
  1058. logger.error(msg)
  1059. raise AuthenticationFailure(sys.exc_info()[1])
  1060. raise
  1061. def get_qpid_connection(self):
  1062. """Return the existing connection (singleton).
  1063. :return: The existing qpid.messaging.Connection
  1064. :rtype: :class:`qpid.messaging.endpoints.Connection`
  1065. """
  1066. return self._qpid_conn
  1067. def close(self):
  1068. """Close the connection
  1069. Closing the connection will close all associated session, senders, or
  1070. receivers used by the Connection.
  1071. """
  1072. self._qpid_conn.close()
  1073. def close_channel(self, channel):
  1074. """Close a Channel.
  1075. Close a channel specified by a reference to the
  1076. :class:`~kombu.transport.qpid.Channel` object.
  1077. :param channel: Channel that should be closed.
  1078. :type channel: :class:`~kombu.transport.qpid.Channel`.
  1079. """
  1080. try:
  1081. self.channels.remove(channel)
  1082. except ValueError:
  1083. pass
  1084. finally:
  1085. channel.connection = None
  1086. class Transport(base.Transport):
  1087. """Kombu native transport for a Qpid broker.
  1088. Provide a native transport for Kombu that allows consumers and
  1089. producers to read and write messages to/from a broker. This Transport
  1090. is capable of supporting both synchronous and asynchronous reading.
  1091. All writes are synchronous through the :class:`Channel` objects that
  1092. support this Transport.
  1093. Asynchronous reads are done using a call to :meth:`drain_events`,
  1094. which synchronously reads messages that were fetched asynchronously, and
  1095. then handles them through calls to the callback handlers maintained on
  1096. the :class:`Connection` object.
  1097. The Transport also provides methods to establish and close a connection
  1098. to the broker. This Transport establishes a factory-like pattern that
  1099. allows for singleton pattern to consolidate all Connections into a single
  1100. one.
  1101. The Transport can create :class:`Channel` objects to communicate with the
  1102. broker with using the :meth:`create_channel` method.
  1103. The Transport identifies recoverable connection errors and recoverable
  1104. channel errors according to the Kombu 3.0 interface. These exception are
  1105. listed as tuples and store in the Transport class attribute
  1106. `recoverable_connection_errors` and `recoverable_channel_errors`
  1107. respectively. Any exception raised that is not a member of one of these
  1108. tuples is considered non-recoverable. This allows Kombu support for
  1109. automatic retry of certain operations to function correctly.
  1110. For backwards compatibility to the pre Kombu 3.0 exception interface, the
  1111. recoverable errors are also listed as `connection_errors` and
  1112. `channel_errors`.
  1113. """
  1114. # Reference to the class that should be used as the Connection object
  1115. Connection = Connection
  1116. # This Transport does not specify a polling interval.
  1117. polling_interval = None
  1118. # This Transport does support the Celery asynchronous event model.
  1119. supports_ev = True
  1120. # The driver type and name for identification purposes.
  1121. driver_type = 'qpid'
  1122. driver_name = 'qpid'
  1123. # Exceptions that can be recovered from, but where the connection must be
  1124. # closed and re-established first.
  1125. recoverable_connection_errors = (
  1126. ConnectionError,
  1127. select.error,
  1128. )
  1129. # Exceptions that can be automatically recovered from without
  1130. # re-establishing the connection.
  1131. recoverable_channel_errors = (
  1132. NotFound,
  1133. )
  1134. # Support the pre 3.0 Kombu exception labeling interface which treats
  1135. # connection_errors and channel_errors both as recoverable via a
  1136. # reconnect.
  1137. connection_errors = recoverable_connection_errors
  1138. channel_errors = recoverable_channel_errors
  1139. def __init__(self, *args, **kwargs):
  1140. self.verify_runtime_environment()
  1141. super(Transport, self).__init__(*args, **kwargs)
  1142. self.use_async_interface = False
  1143. def verify_runtime_environment(self):
  1144. """Verify that the runtime environment is acceptable.
  1145. This method is called as part of __init__ and raises a RuntimeError
  1146. in Python3 or PyPi environments. This module is not compatible with
  1147. Python3 or PyPi. The RuntimeError identifies this to the user up
  1148. front along with suggesting Python 2.6+ be used instead.
  1149. This method also checks that the dependencies qpidtoollibs and
  1150. qpid.messaging are installed. If either one is not installed a
  1151. RuntimeError is raised.
  1152. :raises: RuntimeError if the runtime environment is not acceptable.
  1153. """
  1154. if getattr(sys, 'pypy_version_info', None):
  1155. raise RuntimeError(
  1156. 'The Qpid transport for Kombu does not '
  1157. 'support PyPy. Try using Python 2.6+',
  1158. )
  1159. if PY3:
  1160. raise RuntimeError(
  1161. 'The Qpid transport for Kombu does not '
  1162. 'support Python 3. Try using Python 2.6+',
  1163. )
  1164. if dependency_is_none(qpidtoollibs):
  1165. raise RuntimeError(
  1166. 'The Python package "qpidtoollibs" is missing. Install it '
  1167. 'with your package manager. You can also try `pip install '
  1168. 'qpid-tools`.')
  1169. if dependency_is_none(qpid):
  1170. raise RuntimeError(
  1171. 'The Python package "qpid.messaging" is missing. Install it '
  1172. 'with your package manager. You can also try `pip install '
  1173. 'qpid-python`.')
  1174. def _qpid_message_ready_handler(self, session):
  1175. if self.use_async_interface:
  1176. os.write(self._w, '0')
  1177. def _qpid_async_exception_notify_handler(self, obj_with_exception, exc):
  1178. if self.use_async_interface:
  1179. os.write(self._w, 'e')
  1180. def on_readable(self, connection, loop):
  1181. """Handle any messages associated with this Transport.
  1182. This method clears a single message from the externally monitored
  1183. file descriptor by issuing a read call to the self.r file descriptor
  1184. which removes a single '0' character that was placed into the pipe
  1185. by the Qpid session message callback handler. Once a '0' is read,
  1186. all available events are drained through a call to
  1187. :meth:`drain_events`.
  1188. The file descriptor self.r is modified to be non-blocking, ensuring
  1189. that an accidental call to this method when no more messages will
  1190. not cause indefinite blocking.
  1191. Nothing is expected to be returned from :meth:`drain_events` because
  1192. :meth:`drain_events` handles messages by calling callbacks that are
  1193. maintained on the :class:`~kombu.transport.qpid.Connection` object.
  1194. When :meth:`drain_events` returns, all associated messages have been
  1195. handled.
  1196. This method calls drain_events() which reads as many messages as are
  1197. available for this Transport, and then returns. It blocks in the
  1198. sense that reading and handling a large number of messages may take
  1199. time, but it does not block waiting for a new message to arrive. When
  1200. :meth:`drain_events` is called a timeout is not specified, which
  1201. causes this behavior.
  1202. One interesting behavior of note is where multiple messages are
  1203. ready, and this method removes a single '0' character from
  1204. self.r, but :meth:`drain_events` may handle an arbitrary amount of
  1205. messages. In that case, extra '0' characters may be left on self.r
  1206. to be read, where messages corresponding with those '0' characters
  1207. have already been handled. The external epoll loop will incorrectly
  1208. think additional data is ready for reading, and will call
  1209. on_readable unnecessarily, once for each '0' to be read. Additional
  1210. calls to :meth:`on_readable` produce no negative side effects,
  1211. and will eventually clear out the symbols from the self.r file
  1212. descriptor. If new messages show up during this draining period,
  1213. they will also be properly handled.
  1214. :param connection: The connection associated with the readable
  1215. events, which contains the callbacks that need to be called for
  1216. the readable objects.
  1217. :type connection: kombu.transport.qpid.Connection
  1218. :param loop: The asynchronous loop object that contains epoll like
  1219. functionality.
  1220. :type loop: kombu.async.Hub
  1221. """
  1222. os.read(self.r, 1)
  1223. try:
  1224. self.drain_events(connection)
  1225. except socket.timeout:
  1226. pass
  1227. def register_with_event_loop(self, connection, loop):
  1228. """Register a file descriptor and callback with the loop.
  1229. Register the callback self.on_readable to be called when an
  1230. external epoll loop sees that the file descriptor registered is
  1231. ready for reading. The file descriptor is created by this Transport,
  1232. and is written to when a message is available.
  1233. Because supports_ev == True, Celery expects to call this method to
  1234. give the Transport an opportunity to register a read file descriptor
  1235. for external monitoring by celery using an Event I/O notification
  1236. mechanism such as epoll. A callback is also registered that is to
  1237. be called once the external epoll loop is ready to handle the epoll
  1238. event associated with messages that are ready to be handled for
  1239. this Transport.
  1240. The registration call is made exactly once per Transport after the
  1241. Transport is instantiated.
  1242. :param connection: A reference to the connection associated with
  1243. this Transport.
  1244. :type connection: kombu.transport.qpid.Connection
  1245. :param loop: A reference to the external loop.
  1246. :type loop: kombu.async.hub.Hub
  1247. """
  1248. self.r, self._w = os.pipe()
  1249. if fcntl is not None:
  1250. fcntl.fcntl(self.r, fcntl.F_SETFL, os.O_NONBLOCK)
  1251. self.use_async_interface = True
  1252. loop.add_reader(self.r, self.on_readable, connection, loop)
  1253. def establish_connection(self):
  1254. """Establish a Connection object.
  1255. Determines the correct options to use when creating any
  1256. connections needed by this Transport, and create a
  1257. :class:`Connection` object which saves those values for
  1258. connections generated as they are needed. The options are a
  1259. mixture of what is passed in through the creator of the
  1260. Transport, and the defaults provided by
  1261. :meth:`default_connection_params`. Options cover broker network
  1262. settings, timeout behaviors, authentication, and identity
  1263. verification settings.
  1264. This method also creates and stores a
  1265. :class:`~qpid.messaging.endpoints.Session` using the
  1266. :class:`~qpid.messaging.endpoints.Connection` created by this
  1267. method. The Session is stored on self.
  1268. :return: The created :class:`Connection` object is returned.
  1269. :rtype: :class:`Connection`
  1270. """
  1271. conninfo = self.client
  1272. for name, default_value in items(self.default_connection_params):
  1273. if not getattr(conninfo, name, None):
  1274. setattr(conninfo, name, default_value)
  1275. if conninfo.ssl:
  1276. conninfo.qpid_transport = 'ssl'
  1277. conninfo.transport_options['ssl_keyfile'] = conninfo.ssl[
  1278. 'keyfile']
  1279. conninfo.transport_options['ssl_certfile'] = conninfo.ssl[
  1280. 'certfile']
  1281. conninfo.transport_options['ssl_trustfile'] = conninfo.ssl[
  1282. 'ca_certs']
  1283. if conninfo.ssl['cert_reqs'] == ssl.CERT_REQUIRED:
  1284. conninfo.transport_options['ssl_skip_hostname_check'] = False
  1285. else:
  1286. conninfo.transport_options['ssl_skip_hostname_check'] = True
  1287. else:
  1288. conninfo.qpid_transport = 'tcp'
  1289. credentials = {}
  1290. if conninfo.login_method is None:
  1291. if conninfo.userid is not None and conninfo.password is not None:
  1292. sasl_mech = 'PLAIN'
  1293. credentials['username'] = conninfo.userid
  1294. credentials['password'] = conninfo.password
  1295. elif conninfo.userid is None and conninfo.password is not None:
  1296. raise Exception(
  1297. 'Password configured but no username. SASL PLAIN '
  1298. 'requires a username when using a password.')
  1299. elif conninfo.userid is not None and conninfo.password is None:
  1300. raise Exception(
  1301. 'Username configured but no password. SASL PLAIN '
  1302. 'requires a password when using a username.')
  1303. else:
  1304. sasl_mech = 'ANONYMOUS'
  1305. else:
  1306. sasl_mech = conninfo.login_method
  1307. if conninfo.userid is not None:
  1308. credentials['username'] = conninfo.userid
  1309. opts = {
  1310. 'host': conninfo.hostname,
  1311. 'port': conninfo.port,
  1312. 'sasl_mechanisms': sasl_mech,
  1313. 'timeout': conninfo.connect_timeout,
  1314. 'transport': conninfo.qpid_transport
  1315. }
  1316. opts.update(credentials)
  1317. opts.update(conninfo.transport_options)
  1318. conn = self.Connection(**opts)
  1319. conn.client = self.client
  1320. self.session = conn.get_qpid_connection().session()
  1321. self.session.set_message_received_notify_handler(
  1322. self._qpid_message_ready_handler
  1323. )
  1324. conn.get_qpid_connection().set_async_exception_notify_handler(
  1325. self._qpid_async_exception_notify_handler
  1326. )
  1327. self.session.set_async_exception_notify_handler(
  1328. self._qpid_async_exception_notify_handler
  1329. )
  1330. return conn
  1331. def close_connection(self, connection):
  1332. """Close the :class:`Connection` object.
  1333. :param connection: The Connection that should be closed.
  1334. :type connection: :class:`kombu.transport.qpid.Connection`
  1335. """
  1336. connection.close()
  1337. def drain_events(self, connection, timeout=0, **kwargs):
  1338. """Handle and call callbacks for all ready Transport messages.
  1339. Drains all events that are ready from all
  1340. :class:`~qpid.messaging.endpoints.Receiver` that are asynchronously
  1341. fetching messages.
  1342. For each drained message, the message is called to the appropriate
  1343. callback. Callbacks are organized by queue name.
  1344. :param connection: The :class:`~kombu.transport.qpid.Connection` that
  1345. contains the callbacks, indexed by queue name, which will be called
  1346. by this method.
  1347. :type connection: kombu.transport.qpid.Connection
  1348. :keyword timeout: The timeout that limits how long this method will
  1349. run for. The timeout could interrupt a blocking read that is
  1350. waiting for a new message, or cause this method to return before
  1351. all messages are drained. Defaults to 0.
  1352. :type timeout: int
  1353. """
  1354. start_time = time.time()
  1355. elapsed_time = -1
  1356. while elapsed_time < timeout:
  1357. try:
  1358. receiver = self.session.next_receiver(timeout=timeout)
  1359. message = receiver.fetch()
  1360. queue = receiver.source
  1361. except QpidEmpty:
  1362. raise socket.timeout()
  1363. else:
  1364. connection._callbacks[queue](message)
  1365. elapsed_time = time.time() - start_time
  1366. raise socket.timeout()
  1367. def create_channel(self, connection):
  1368. """Create and return a :class:`~kombu.transport.qpid.Channel`.
  1369. Creates a new channel, and appends the channel to the
  1370. list of channels known by the Connection. Once the new
  1371. channel is created, it is returned.
  1372. :param connection: The connection that should support the new
  1373. :class:`~kombu.transport.qpid.Channel`.
  1374. :type connection: kombu.transport.qpid.Connection
  1375. :return: The new Channel that is made.
  1376. :rtype: :class:`kombu.transport.qpid.Channel`.
  1377. """
  1378. channel = connection.Channel(connection, self)
  1379. connection.channels.append(channel)
  1380. return channel
  1381. @property
  1382. def default_connection_params(self):
  1383. """Return a dict with default connection parameters.
  1384. These connection parameters will be used whenever the creator of
  1385. Transport does not specify a required parameter.
  1386. :return: A dict containing the default parameters.
  1387. :rtype: dict
  1388. """
  1389. return {
  1390. 'hostname': 'localhost',
  1391. 'port': 5672,
  1392. }
  1393. def __del__(self):
  1394. """Ensure file descriptors opened in __init__() are closed."""
  1395. if self.use_async_interface:
  1396. for fd in (self.r, self._w):
  1397. try:
  1398. os.close(fd)
  1399. except OSError:
  1400. # ignored
  1401. pass