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.

control.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.app.control
  4. ~~~~~~~~~~~~~~~~~~~
  5. Client for worker remote control commands.
  6. Server implementation is in :mod:`celery.worker.control`.
  7. """
  8. from __future__ import absolute_import
  9. import warnings
  10. from kombu.pidbox import Mailbox
  11. from kombu.utils import cached_property
  12. from celery.exceptions import DuplicateNodenameWarning
  13. from celery.utils.text import pluralize
  14. __all__ = ['Inspect', 'Control', 'flatten_reply']
  15. W_DUPNODE = """\
  16. Received multiple replies from node {0}: {1}.
  17. Please make sure you give each node a unique nodename using the `-n` option.\
  18. """
  19. def flatten_reply(reply):
  20. nodes, dupes = {}, set()
  21. for item in reply:
  22. [dupes.add(name) for name in item if name in nodes]
  23. nodes.update(item)
  24. if dupes:
  25. warnings.warn(DuplicateNodenameWarning(
  26. W_DUPNODE.format(
  27. pluralize(len(dupes), 'name'), ', '.join(sorted(dupes)),
  28. ),
  29. ))
  30. return nodes
  31. class Inspect(object):
  32. app = None
  33. def __init__(self, destination=None, timeout=1, callback=None,
  34. connection=None, app=None, limit=None):
  35. self.app = app or self.app
  36. self.destination = destination
  37. self.timeout = timeout
  38. self.callback = callback
  39. self.connection = connection
  40. self.limit = limit
  41. def _prepare(self, reply):
  42. if not reply:
  43. return
  44. by_node = flatten_reply(reply)
  45. if self.destination and \
  46. not isinstance(self.destination, (list, tuple)):
  47. return by_node.get(self.destination)
  48. return by_node
  49. def _request(self, command, **kwargs):
  50. return self._prepare(self.app.control.broadcast(
  51. command,
  52. arguments=kwargs,
  53. destination=self.destination,
  54. callback=self.callback,
  55. connection=self.connection,
  56. limit=self.limit,
  57. timeout=self.timeout, reply=True,
  58. ))
  59. def report(self):
  60. return self._request('report')
  61. def clock(self):
  62. return self._request('clock')
  63. def active(self, safe=False):
  64. return self._request('dump_active', safe=safe)
  65. def scheduled(self, safe=False):
  66. return self._request('dump_schedule', safe=safe)
  67. def reserved(self, safe=False):
  68. return self._request('dump_reserved', safe=safe)
  69. def stats(self):
  70. return self._request('stats')
  71. def revoked(self):
  72. return self._request('dump_revoked')
  73. def registered(self, *taskinfoitems):
  74. return self._request('dump_tasks', taskinfoitems=taskinfoitems)
  75. registered_tasks = registered
  76. def ping(self):
  77. return self._request('ping')
  78. def active_queues(self):
  79. return self._request('active_queues')
  80. def query_task(self, ids):
  81. return self._request('query_task', ids=ids)
  82. def conf(self, with_defaults=False):
  83. return self._request('dump_conf', with_defaults=with_defaults)
  84. def hello(self, from_node, revoked=None):
  85. return self._request('hello', from_node=from_node, revoked=revoked)
  86. def memsample(self):
  87. return self._request('memsample')
  88. def memdump(self, samples=10):
  89. return self._request('memdump', samples=samples)
  90. def objgraph(self, type='Request', n=200, max_depth=10):
  91. return self._request('objgraph', num=n, max_depth=max_depth, type=type)
  92. class Control(object):
  93. Mailbox = Mailbox
  94. def __init__(self, app=None):
  95. self.app = app
  96. self.mailbox = self.Mailbox('celery', type='fanout', accept=['json'])
  97. @cached_property
  98. def inspect(self):
  99. return self.app.subclass_with_self(Inspect, reverse='control.inspect')
  100. def purge(self, connection=None):
  101. """Discard all waiting tasks.
  102. This will ignore all tasks waiting for execution, and they will
  103. be deleted from the messaging server.
  104. :returns: the number of tasks discarded.
  105. """
  106. with self.app.connection_or_acquire(connection) as conn:
  107. return self.app.amqp.TaskConsumer(conn).purge()
  108. discard_all = purge
  109. def election(self, id, topic, action=None, connection=None):
  110. self.broadcast('election', connection=connection, arguments={
  111. 'id': id, 'topic': topic, 'action': action,
  112. })
  113. def revoke(self, task_id, destination=None, terminate=False,
  114. signal='SIGTERM', **kwargs):
  115. """Tell all (or specific) workers to revoke a task by id.
  116. If a task is revoked, the workers will ignore the task and
  117. not execute it after all.
  118. :param task_id: Id of the task to revoke.
  119. :keyword terminate: Also terminate the process currently working
  120. on the task (if any).
  121. :keyword signal: Name of signal to send to process if terminate.
  122. Default is TERM.
  123. See :meth:`broadcast` for supported keyword arguments.
  124. """
  125. return self.broadcast('revoke', destination=destination,
  126. arguments={'task_id': task_id,
  127. 'terminate': terminate,
  128. 'signal': signal}, **kwargs)
  129. def ping(self, destination=None, timeout=1, **kwargs):
  130. """Ping all (or specific) workers.
  131. Will return the list of answers.
  132. See :meth:`broadcast` for supported keyword arguments.
  133. """
  134. return self.broadcast('ping', reply=True, destination=destination,
  135. timeout=timeout, **kwargs)
  136. def rate_limit(self, task_name, rate_limit, destination=None, **kwargs):
  137. """Tell all (or specific) workers to set a new rate limit
  138. for task by type.
  139. :param task_name: Name of task to change rate limit for.
  140. :param rate_limit: The rate limit as tasks per second, or a rate limit
  141. string (`'100/m'`, etc.
  142. see :attr:`celery.task.base.Task.rate_limit` for
  143. more information).
  144. See :meth:`broadcast` for supported keyword arguments.
  145. """
  146. return self.broadcast('rate_limit', destination=destination,
  147. arguments={'task_name': task_name,
  148. 'rate_limit': rate_limit},
  149. **kwargs)
  150. def add_consumer(self, queue, exchange=None, exchange_type='direct',
  151. routing_key=None, options=None, **kwargs):
  152. """Tell all (or specific) workers to start consuming from a new queue.
  153. Only the queue name is required as if only the queue is specified
  154. then the exchange/routing key will be set to the same name (
  155. like automatic queues do).
  156. .. note::
  157. This command does not respect the default queue/exchange
  158. options in the configuration.
  159. :param queue: Name of queue to start consuming from.
  160. :keyword exchange: Optional name of exchange.
  161. :keyword exchange_type: Type of exchange (defaults to 'direct')
  162. command to, when empty broadcast to all workers.
  163. :keyword routing_key: Optional routing key.
  164. :keyword options: Additional options as supported
  165. by :meth:`kombu.entitiy.Queue.from_dict`.
  166. See :meth:`broadcast` for supported keyword arguments.
  167. """
  168. return self.broadcast(
  169. 'add_consumer',
  170. arguments=dict({'queue': queue, 'exchange': exchange,
  171. 'exchange_type': exchange_type,
  172. 'routing_key': routing_key}, **options or {}),
  173. **kwargs
  174. )
  175. def cancel_consumer(self, queue, **kwargs):
  176. """Tell all (or specific) workers to stop consuming from ``queue``.
  177. Supports the same keyword arguments as :meth:`broadcast`.
  178. """
  179. return self.broadcast(
  180. 'cancel_consumer', arguments={'queue': queue}, **kwargs
  181. )
  182. def time_limit(self, task_name, soft=None, hard=None, **kwargs):
  183. """Tell all (or specific) workers to set time limits for
  184. a task by type.
  185. :param task_name: Name of task to change time limits for.
  186. :keyword soft: New soft time limit (in seconds).
  187. :keyword hard: New hard time limit (in seconds).
  188. Any additional keyword arguments are passed on to :meth:`broadcast`.
  189. """
  190. return self.broadcast(
  191. 'time_limit',
  192. arguments={'task_name': task_name,
  193. 'hard': hard, 'soft': soft}, **kwargs)
  194. def enable_events(self, destination=None, **kwargs):
  195. """Tell all (or specific) workers to enable events."""
  196. return self.broadcast('enable_events', {}, destination, **kwargs)
  197. def disable_events(self, destination=None, **kwargs):
  198. """Tell all (or specific) workers to disable events."""
  199. return self.broadcast('disable_events', {}, destination, **kwargs)
  200. def pool_grow(self, n=1, destination=None, **kwargs):
  201. """Tell all (or specific) workers to grow the pool by ``n``.
  202. Supports the same arguments as :meth:`broadcast`.
  203. """
  204. return self.broadcast('pool_grow', {'n': n}, destination, **kwargs)
  205. def pool_shrink(self, n=1, destination=None, **kwargs):
  206. """Tell all (or specific) workers to shrink the pool by ``n``.
  207. Supports the same arguments as :meth:`broadcast`.
  208. """
  209. return self.broadcast('pool_shrink', {'n': n}, destination, **kwargs)
  210. def autoscale(self, max, min, destination=None, **kwargs):
  211. """Change worker(s) autoscale setting.
  212. Supports the same arguments as :meth:`broadcast`.
  213. """
  214. return self.broadcast(
  215. 'autoscale', {'max': max, 'min': min}, destination, **kwargs)
  216. def broadcast(self, command, arguments=None, destination=None,
  217. connection=None, reply=False, timeout=1, limit=None,
  218. callback=None, channel=None, **extra_kwargs):
  219. """Broadcast a control command to the celery workers.
  220. :param command: Name of command to send.
  221. :param arguments: Keyword arguments for the command.
  222. :keyword destination: If set, a list of the hosts to send the
  223. command to, when empty broadcast to all workers.
  224. :keyword connection: Custom broker connection to use, if not set,
  225. a connection will be established automatically.
  226. :keyword reply: Wait for and return the reply.
  227. :keyword timeout: Timeout in seconds to wait for the reply.
  228. :keyword limit: Limit number of replies.
  229. :keyword callback: Callback called immediately for each reply
  230. received.
  231. """
  232. with self.app.connection_or_acquire(connection) as conn:
  233. arguments = dict(arguments or {}, **extra_kwargs)
  234. return self.mailbox(conn)._broadcast(
  235. command, arguments, destination, reply, timeout,
  236. limit, callback, channel=channel,
  237. )