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.

146 lines
4.7 KiB

  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. import base64
  13. import copy
  14. import cStringIO
  15. from pkg_resources import resource_filename
  16. import flask
  17. import flask.ext.restless
  18. import flask.ext.sqlalchemy
  19. from flask_bootstrap import Bootstrap
  20. from kombu import Connection
  21. from kombu.pools import producers
  22. from oslo_config import cfg
  23. from oslo_log import log
  24. from PIL import Image
  25. from sqlalchemy.dialects import mysql
  26. from faafo import queues
  27. from faafo import version
  28. LOG = log.getLogger('faafo.api')
  29. CONF = cfg.CONF
  30. api_opts = [
  31. cfg.StrOpt('listen-address',
  32. default='0.0.0.0',
  33. help='Listen address.'),
  34. cfg.IntOpt('bind-port',
  35. default='80',
  36. help='Bind port.'),
  37. cfg.StrOpt('database-url',
  38. default='sqlite:////tmp/sqlite.db',
  39. help='Database connection URL.')
  40. ]
  41. CONF.register_opts(api_opts)
  42. log.register_options(CONF)
  43. log.set_defaults()
  44. CONF(project='api', prog='faafo-api',
  45. default_config_files=['/etc/faafo/faafo.conf'],
  46. version=version.version_info.version_string())
  47. log.setup(CONF, 'api',
  48. version=version.version_info.version_string())
  49. template_path = resource_filename(__name__, "templates")
  50. app = flask.Flask('faafo.api', template_folder=template_path)
  51. app.config['DEBUG'] = CONF.debug
  52. app.config['SQLALCHEMY_DATABASE_URI'] = CONF.database_url
  53. db = flask.ext.sqlalchemy.SQLAlchemy(app)
  54. Bootstrap(app)
  55. def list_opts():
  56. """Entry point for oslo-config-generator."""
  57. return [(None, copy.deepcopy(api_opts))]
  58. class Fractal(db.Model):
  59. uuid = db.Column(db.String(36), primary_key=True)
  60. checksum = db.Column(db.String(256), unique=True)
  61. url = db.Column(db.String(256), nullable=True)
  62. duration = db.Column(db.Float)
  63. size = db.Column(db.Integer, nullable=True)
  64. width = db.Column(db.Integer, nullable=False)
  65. height = db.Column(db.Integer, nullable=False)
  66. iterations = db.Column(db.Integer, nullable=False)
  67. xa = db.Column(db.Float, nullable=False)
  68. xb = db.Column(db.Float, nullable=False)
  69. ya = db.Column(db.Float, nullable=False)
  70. yb = db.Column(db.Float, nullable=False)
  71. if CONF.database_url.startswith('mysql'):
  72. LOG.debug('Using MySQL database backend')
  73. image = db.Column(mysql.MEDIUMBLOB, nullable=True)
  74. else:
  75. image = db.Column(db.LargeBinary, nullable=True)
  76. generated_by = db.Column(db.String(256), nullable=True)
  77. def __repr__(self):
  78. return '<Fractal %s>' % self.uuid
  79. db.create_all()
  80. manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db)
  81. connection = Connection(CONF.transport_url)
  82. @app.route('/', methods=['GET'])
  83. @app.route('/index', methods=['GET'])
  84. @app.route('/index/<int:page>', methods=['GET'])
  85. def index(page=1):
  86. fractals = Fractal.query.filter(
  87. (Fractal.checksum != None) & (Fractal.size != None)).paginate( # noqa
  88. page, 5, error_out=False)
  89. return flask.render_template('index.html', fractals=fractals)
  90. @app.route('/fractal/<string:fractalid>', methods=['GET'])
  91. def get_fractal(fractalid):
  92. fractal = Fractal.query.filter_by(uuid=fractalid).first()
  93. if not fractal:
  94. response = flask.jsonify({'code': 404,
  95. 'message': 'Fracal not found'})
  96. response.status_code = 404
  97. else:
  98. image_data = base64.b64decode(fractal.image)
  99. image = Image.open(cStringIO.StringIO(image_data))
  100. output = cStringIO.StringIO()
  101. image.save(output, "PNG")
  102. image.seek(0)
  103. response = flask.make_response(output.getvalue())
  104. response.content_type = "image/png"
  105. return response
  106. def generate_fractal(**kwargs):
  107. with producers[connection].acquire(block=True) as producer:
  108. producer.publish(kwargs['result'],
  109. serializer='json',
  110. exchange=queues.task_exchange,
  111. declare=[queues.task_exchange],
  112. routing_key='normal')
  113. def main():
  114. manager.create_api(Fractal, methods=['GET', 'POST', 'DELETE', 'PUT'],
  115. postprocessors={'POST': [generate_fractal]},
  116. exclude_columns=['image'],
  117. url_prefix='/v1')
  118. app.run(host=CONF.listen_address, port=CONF.bind_port)