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.

166 lines
5.1 KiB

  1. #!/usr/bin/env python
  2. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  3. # not use this file except in compliance with the License. You may obtain
  4. # a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  10. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  11. # License for the specific language governing permissions and limitations
  12. # under the License.
  13. # based on http://code.activestate.com/recipes/577120-julia-fractals/
  14. import base64
  15. import copy
  16. import hashlib
  17. import json
  18. import os
  19. import random
  20. import socket
  21. import tempfile
  22. import time
  23. from PIL import Image
  24. from kombu.mixins import ConsumerMixin
  25. from oslo_config import cfg
  26. from oslo_log import log
  27. import requests
  28. from faafo import queues
  29. LOG = log.getLogger('faafo.worker')
  30. CONF = cfg.CONF
  31. worker_opts = {
  32. cfg.StrOpt('endpoint-url',
  33. default='http://localhost',
  34. help='API connection URL')
  35. }
  36. CONF.register_opts(worker_opts)
  37. def list_opts():
  38. """Entry point for oslo-config-generator."""
  39. return [(None, copy.deepcopy(worker_opts))]
  40. class JuliaSet(object):
  41. def __init__(self, width, height, xa=-2.0, xb=2.0, ya=-1.5, yb=1.5,
  42. iterations=255):
  43. self.xa = xa
  44. self.xb = xb
  45. self.ya = ya
  46. self.yb = yb
  47. self.iterations = iterations
  48. self.width = width
  49. self.height = height
  50. self.draw()
  51. def draw(self):
  52. self.image = Image.new("RGB", (self.width, self.height))
  53. c, z = self._set_point()
  54. for y in range(self.height):
  55. zy = y * (self.yb - self.ya) / (self.height - 1) + self.ya
  56. for x in range(self.width):
  57. zx = x * (self.xb - self.xa) / (self.width - 1) + self.xa
  58. z = zx + zy * 1j
  59. for i in range(self.iterations):
  60. if abs(z) > 2.0:
  61. break
  62. z = z * z + c
  63. self.image.putpixel((x, y),
  64. (i % 8 * 32, i % 16 * 16, i % 32 * 8))
  65. def get_file(self):
  66. with tempfile.NamedTemporaryFile(delete=False) as fp:
  67. self.image.save(fp, "PNG")
  68. return fp.name
  69. def _set_point(self):
  70. random.seed()
  71. while True:
  72. cx = random.random() * (self.xb - self.xa) + self.xa
  73. cy = random.random() * (self.yb - self.ya) + self.ya
  74. c = cx + cy * 1j
  75. z = c
  76. for i in range(self.iterations):
  77. if abs(z) > 2.0:
  78. break
  79. z = z * z + c
  80. if i > 10 and i < 100:
  81. break
  82. return (c, z)
  83. class Worker(ConsumerMixin):
  84. def __init__(self, connection):
  85. self.connection = connection
  86. def get_consumers(self, Consumer, channel):
  87. return [Consumer(queues=queues.task_queue,
  88. accept=['json'],
  89. callbacks=[self.process])]
  90. def process(self, task_def, message):
  91. task = task_def['data']['attributes']
  92. LOG.info("processing task %s" % task['uuid'])
  93. LOG.debug(task)
  94. start_time = time.time()
  95. juliaset = JuliaSet(task['width'],
  96. task['height'],
  97. task['xa'],
  98. task['xb'],
  99. task['ya'],
  100. task['yb'],
  101. task['iterations'])
  102. elapsed_time = time.time() - start_time
  103. LOG.info("task %s processed in %f seconds" %
  104. (task['uuid'], elapsed_time))
  105. filename = juliaset.get_file()
  106. LOG.debug("saved result of task %s to temporary file %s" %
  107. (task['uuid'], filename))
  108. with open(filename, "rb") as fp:
  109. size = os.fstat(fp.fileno()).st_size
  110. image = base64.b64encode(fp.read())
  111. checksum = hashlib.sha256(open(filename, 'rb').read()).hexdigest()
  112. os.remove(filename)
  113. LOG.debug("removed temporary file %s" % filename)
  114. result = {
  115. 'data': {
  116. 'type': 'fractal',
  117. 'id': task['uuid'],
  118. 'attributes': {
  119. 'uuid': task['uuid'],
  120. 'duration': elapsed_time,
  121. 'image': image.decode("ascii"),
  122. 'checksum': checksum,
  123. 'size': size,
  124. 'generated_by': socket.gethostname()
  125. }
  126. }
  127. }
  128. headers = {'Content-Type': 'application/vnd.api+json',
  129. 'Accept': 'application/vnd.api+json'}
  130. resp = requests.patch("%s/v1/fractal/%s" %
  131. (CONF.endpoint_url, str(task['uuid'])),
  132. json.dumps(result),
  133. headers=headers,
  134. timeout=30)
  135. LOG.debug("Result: %s" % resp.text)
  136. message.ack()
  137. return result