Sebastian Rieger
5 years ago
38 changed files with 1851 additions and 0 deletions
-
14faafo/.gitignore
-
16faafo/CONTRIBUTING.rst
-
202faafo/LICENSE
-
30faafo/README.rst
-
49faafo/Vagrantfile
-
267faafo/bin/faafo
-
52faafo/bin/faafo-worker
-
198faafo/contrib/install.sh
-
56faafo/contrib/test_api.py
-
17faafo/doc/source/conf.py
-
68faafo/doc/source/development.rst
-
15faafo/doc/source/images/diagram.dot
-
BINfaafo/doc/source/images/diagram.png
-
3faafo/doc/source/images/dot2png.sh
-
BINfaafo/doc/source/images/example.png
-
BINfaafo/doc/source/images/screenshot_webinterface.png
-
11faafo/doc/source/implementation.rst
-
14faafo/doc/source/index.rst
-
8faafo/doc/source/installation.rst
-
6faafo/doc/source/references.rst
-
42faafo/doc/source/usage.rst
-
4faafo/doc/source/workflow.rst
-
110faafo/etc/faafo.conf
-
125faafo/etc/faafo.conf.sample
-
6faafo/etc/oslo-config-generator/faafo.conf
-
0faafo/faafo/__init__.py
-
0faafo/faafo/api/__init__.py
-
146faafo/faafo/api/service.py
-
60faafo/faafo/api/templates/index.html
-
32faafo/faafo/queues.py
-
15faafo/faafo/version.py
-
0faafo/faafo/worker/__init__.py
-
156faafo/faafo/worker/service.py
-
17faafo/requirements.txt
-
54faafo/setup.cfg
-
29faafo/setup.py
-
2faafo/test-requirements.txt
-
27faafo/tox.ini
@ -0,0 +1,14 @@ |
|||
.tox |
|||
.vagrant |
|||
*.pyc |
|||
.venv |
|||
*.egg-info |
|||
*.egg |
|||
.eggs |
|||
.*.swp |
|||
build |
|||
*.log |
|||
*.png |
|||
AUTHORS |
|||
ChangeLog |
|||
dist |
@ -0,0 +1,16 @@ |
|||
If you would like to contribute to the development of OpenStack, |
|||
you must follow the steps documented at: |
|||
|
|||
http://docs.openstack.org/infra/manual/developers.html#getting-started |
|||
|
|||
Once those steps have been completed, changes to OpenStack |
|||
should be submitted for review via the Gerrit tool, following |
|||
the workflow documented at: |
|||
|
|||
http://docs.openstack.org/infra/manual/developers.html#development-workflow |
|||
|
|||
Pull requests submitted through GitHub will be ignored. |
|||
|
|||
Bugs should be filed on Launchpad, not GitHub: |
|||
|
|||
https://bugs.launchpad.net/faafo |
@ -0,0 +1,202 @@ |
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, |
|||
and distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by |
|||
the copyright owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all |
|||
other entities that control, are controlled by, or are under common |
|||
control with that entity. For the purposes of this definition, |
|||
"control" means (i) the power, direct or indirect, to cause the |
|||
direction or management of such entity, whether by contract or |
|||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity |
|||
exercising permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, |
|||
including but not limited to software source code, documentation |
|||
source, and configuration files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical |
|||
transformation or translation of a Source form, including but |
|||
not limited to compiled object code, generated documentation, |
|||
and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or |
|||
Object form, made available under the License, as indicated by a |
|||
copyright notice that is included in or attached to the work |
|||
(an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object |
|||
form, that is based on (or derived from) the Work and for which the |
|||
editorial revisions, annotations, elaborations, or other modifications |
|||
represent, as a whole, an original work of authorship. For the purposes |
|||
of this License, Derivative Works shall not include works that remain |
|||
separable from, or merely link (or bind by name) to the interfaces of, |
|||
the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including |
|||
the original version of the Work and any modifications or additions |
|||
to that Work or Derivative Works thereof, that is intentionally |
|||
submitted to Licensor for inclusion in the Work by the copyright owner |
|||
or by an individual or Legal Entity authorized to submit on behalf of |
|||
the copyright owner. For the purposes of this definition, "submitted" |
|||
means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, |
|||
and issue tracking systems that are managed by, or on behalf of, the |
|||
Licensor for the purpose of discussing and improving the Work, but |
|||
excluding communication that is conspicuously marked or otherwise |
|||
designated in writing by the copyright owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity |
|||
on behalf of whom a Contribution has been received by Licensor and |
|||
subsequently incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the |
|||
Work and such Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
(except as stated in this section) patent license to make, have made, |
|||
use, offer to sell, sell, import, and otherwise transfer the Work, |
|||
where such license applies only to those patent claims licensable |
|||
by such Contributor that are necessarily infringed by their |
|||
Contribution(s) alone or by combination of their Contribution(s) |
|||
with the Work to which such Contribution(s) was submitted. If You |
|||
institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
|||
or a Contribution incorporated within the Work constitutes direct |
|||
or contributory patent infringement, then any patent licenses |
|||
granted to You under this License for that Work shall terminate |
|||
as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. You may reproduce and distribute copies of the |
|||
Work or Derivative Works thereof in any medium, with or without |
|||
modifications, and in Source or Object form, provided that You |
|||
meet the following conditions: |
|||
|
|||
(a) You must give any other recipients of the Work or |
|||
Derivative Works a copy of this License; and |
|||
|
|||
(b) You must cause any modified files to carry prominent notices |
|||
stating that You changed the files; and |
|||
|
|||
(c) You must retain, in the Source form of any Derivative Works |
|||
that You distribute, all copyright, patent, trademark, and |
|||
attribution notices from the Source form of the Work, |
|||
excluding those notices that do not pertain to any part of |
|||
the Derivative Works; and |
|||
|
|||
(d) If the Work includes a "NOTICE" text file as part of its |
|||
distribution, then any Derivative Works that You distribute must |
|||
include a readable copy of the attribution notices contained |
|||
within such NOTICE file, excluding those notices that do not |
|||
pertain to any part of the Derivative Works, in at least one |
|||
of the following places: within a NOTICE text file distributed |
|||
as part of the Derivative Works; within the Source form or |
|||
documentation, if provided along with the Derivative Works; or, |
|||
within a display generated by the Derivative Works, if and |
|||
wherever such third-party notices normally appear. The contents |
|||
of the NOTICE file are for informational purposes only and |
|||
do not modify the License. You may add Your own attribution |
|||
notices within Derivative Works that You distribute, alongside |
|||
or as an addendum to the NOTICE text from the Work, provided |
|||
that such additional attribution notices cannot be construed |
|||
as modifying the License. |
|||
|
|||
You may add Your own copyright statement to Your modifications and |
|||
may provide additional or different license terms and conditions |
|||
for use, reproduction, or distribution of Your modifications, or |
|||
for any such Derivative Works as a whole, provided Your use, |
|||
reproduction, and distribution of the Work otherwise complies with |
|||
the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. Unless You explicitly state otherwise, |
|||
any Contribution intentionally submitted for inclusion in the Work |
|||
by You to the Licensor shall be under the terms and conditions of |
|||
this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify |
|||
the terms of any separate license agreement you may have executed |
|||
with Licensor regarding such Contributions. |
|||
|
|||
6. Trademarks. This License does not grant permission to use the trade |
|||
names, trademarks, service marks, or product names of the Licensor, |
|||
except as required for reasonable and customary use in describing the |
|||
origin of the Work and reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. Unless required by applicable law or |
|||
agreed to in writing, Licensor provides the Work (and each |
|||
Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
implied, including, without limitation, any warranties or conditions |
|||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|||
PARTICULAR PURPOSE. You are solely responsible for determining the |
|||
appropriateness of using or redistributing the Work and assume any |
|||
risks associated with Your exercise of permissions under this License. |
|||
|
|||
8. Limitation of Liability. In no event and under no legal theory, |
|||
whether in tort (including negligence), contract, or otherwise, |
|||
unless required by applicable law (such as deliberate and grossly |
|||
negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, |
|||
incidental, or consequential damages of any character arising as a |
|||
result of this License or out of the use or inability to use the |
|||
Work (including but not limited to damages for loss of goodwill, |
|||
work stoppage, computer failure or malfunction, or any and all |
|||
other commercial damages or losses), even if such Contributor |
|||
has been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. While redistributing |
|||
the Work or Derivative Works thereof, You may choose to offer, |
|||
and charge a fee for, acceptance of support, warranty, indemnity, |
|||
or other liability obligations and/or rights consistent with this |
|||
License. However, in accepting such obligations, You may act only |
|||
on Your own behalf and on Your sole responsibility, not on behalf |
|||
of any other Contributor, and only if You agree to indemnify, |
|||
defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason |
|||
of your accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work. |
|||
|
|||
To apply the Apache License to your work, attach the following |
|||
boilerplate notice, with the fields enclosed by brackets "{}" |
|||
replaced with your own identifying information. (Don't include |
|||
the brackets!) The text should be enclosed in the appropriate |
|||
comment syntax for the file format. We also recommend that a |
|||
file or class name and description of purpose be included on the |
|||
same "printed page" as the copyright notice for easier |
|||
identification within third-party archives. |
|||
|
|||
Copyright {yyyy} {name of copyright owner} |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
@ -0,0 +1,30 @@ |
|||
Local copy of the former faafo OpenStack Tutorial from developer.openstack.org |
|||
|
|||
======================== |
|||
Team and repository tags |
|||
======================== |
|||
|
|||
.. image:: http://governance.openstack.org/badges/faafo.svg |
|||
:target: http://governance.openstack.org/reference/tags/index.html |
|||
|
|||
.. Change things from this point on |
|||
|
|||
=========================================== |
|||
First App Application for OpenStack (faafo) |
|||
=========================================== |
|||
|
|||
Developer documentation, the source of which is in ``doc/source/``, is |
|||
published at: |
|||
|
|||
http://docs.openstack.org/developer/faafo/ |
|||
|
|||
Bugs and feature requests are tracked on Launchpad at: |
|||
|
|||
https://bugs.launchpad.net/faafo |
|||
|
|||
Contributors are encouraged to join IRC (``#openstackfirstapp`` on freenode): |
|||
|
|||
https://wiki.openstack.org/wiki/IRC |
|||
|
|||
For information on contributing to the First App Application for OpenStack, |
|||
see ``CONTRIBUTING.rst``. |
@ -0,0 +1,49 @@ |
|||
# -*- mode: ruby -*- |
|||
# vi: set ft=ruby : |
|||
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
Vagrant.configure(2) do |config| |
|||
config.vm.box = 'ubuntu/trusty64' |
|||
config.vm.provider 'virtualbox' do |vb| |
|||
vb.memory = 1024 |
|||
vb.cpus = 1 |
|||
end |
|||
config.ssh.shell = 'bash -c "BASH_ENV=/etc/profile exec bash"' |
|||
config.cache.scope = :box if Vagrant.has_plugin?('vagrant-cachier') |
|||
config.vm.provision "shell", |
|||
inline: "apt-get update && apt-get upgrade -y" |
|||
|
|||
config.vm.define "services", primary: true do |node| |
|||
node.vm.hostname= "services" |
|||
node.vm.network :private_network, ip: '10.0.88.10' |
|||
node.vm.provision "shell", |
|||
inline: "/vagrant/contrib/install.sh -i messaging -i database" |
|||
node.vm.network 'forwarded_port', guest: 15_672, host: 15_672 |
|||
end |
|||
|
|||
config.vm.define "api" do |node| |
|||
node.vm.hostname= "api" |
|||
node.vm.network :private_network, ip: '10.0.88.20' |
|||
node.vm.provision "shell", |
|||
inline: "/vagrant/contrib/install.sh -i faafo -r api -d 'mysql://faafo:password@10.0.88.10:3306/faafo' -m 'amqp://guest:guest@10.0.88.10:5672/'" |
|||
node.vm.network 'forwarded_port', guest: 80, host: 1080 |
|||
end |
|||
|
|||
config.vm.define "worker" do |node| |
|||
node.vm.hostname= "worker" |
|||
node.vm.network :private_network, ip: '10.0.88.30' |
|||
node.vm.provision "shell", |
|||
inline: "/vagrant/contrib/install.sh -i faafo -r worker -m 'amqp://guest:guest@10.0.88.10:5672/' -e 'http://10.0.88.20'" |
|||
end |
|||
end |
@ -0,0 +1,267 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
import copy |
|||
import json |
|||
import random |
|||
import uuid |
|||
|
|||
from oslo_config import cfg |
|||
from oslo_log import log |
|||
from prettytable import PrettyTable |
|||
import requests |
|||
|
|||
from faafo import version |
|||
|
|||
|
|||
LOG = log.getLogger('faafo.client') |
|||
CONF = cfg.CONF |
|||
|
|||
|
|||
def get_random_task(): |
|||
random.seed() |
|||
|
|||
if CONF.command.width: |
|||
width = int(CONF.command.width) |
|||
else: |
|||
width = random.randint(int(CONF.command.min_width), |
|||
int(CONF.command.max_width)) |
|||
|
|||
if CONF.command.height: |
|||
height = int(CONF.command.height) |
|||
else: |
|||
height = random.randint(int(CONF.command.min_height), |
|||
int(CONF.command.max_height)) |
|||
|
|||
if CONF.command.iterations: |
|||
iterations = int(CONF.command.iterations) |
|||
else: |
|||
iterations = random.randint(int(CONF.command.min_iterations), |
|||
int(CONF.command.max_iterations)) |
|||
|
|||
if CONF.command.xa: |
|||
xa = float(CONF.command.xa) |
|||
else: |
|||
xa = random.uniform(float(CONF.command.min_xa), |
|||
float(CONF.command.max_xa)) |
|||
|
|||
if CONF.command.xb: |
|||
xb = float(CONF.command.xb) |
|||
else: |
|||
xb = random.uniform(float(CONF.command.min_xb), |
|||
float(CONF.command.max_xb)) |
|||
|
|||
if CONF.command.ya: |
|||
ya = float(CONF.command.ya) |
|||
else: |
|||
ya = random.uniform(float(CONF.command.min_ya), |
|||
float(CONF.command.max_ya)) |
|||
|
|||
if CONF.command.yb: |
|||
yb = float(CONF.command.yb) |
|||
else: |
|||
yb = random.uniform(float(CONF.command.min_yb), |
|||
float(CONF.command.max_yb)) |
|||
|
|||
task = { |
|||
'uuid': str(uuid.uuid4()), |
|||
'width': width, |
|||
'height': height, |
|||
'iterations': iterations, 'xa': xa, |
|||
'xb': xb, |
|||
'ya': ya, |
|||
'yb': yb |
|||
} |
|||
|
|||
return task |
|||
|
|||
|
|||
def do_get_fractal(): |
|||
LOG.error("command 'download' not yet implemented") |
|||
|
|||
|
|||
def do_show_fractal(): |
|||
LOG.info("showing fractal %s" % CONF.command.uuid) |
|||
result = requests.get("%s/v1/fractal/%s" % |
|||
(CONF.endpoint_url, CONF.command.uuid)) |
|||
if result.status_code == 200: |
|||
data = json.loads(result.text) |
|||
output = PrettyTable(["Parameter", "Value"]) |
|||
output.align["Parameter"] = "l" |
|||
output.align["Value"] = "l" |
|||
output.add_row(["uuid", data['uuid']]) |
|||
output.add_row(["duration", "%f seconds" % data['duration']]) |
|||
output.add_row(["dimensions", "%d x %d pixels" % |
|||
(data['width'], data['height'])]) |
|||
output.add_row(["iterations", data['iterations']]) |
|||
output.add_row(["xa", data['xa']]) |
|||
output.add_row(["xb", data['xb']]) |
|||
output.add_row(["ya", data['ya']]) |
|||
output.add_row(["yb", data['yb']]) |
|||
output.add_row(["size", "%d bytes" % data['size']]) |
|||
output.add_row(["checksum", data['checksum']]) |
|||
output.add_row(["generated_by", data['generated_by']]) |
|||
print(output) |
|||
else: |
|||
LOG.error("fractal '%s' not found" % CONF.command.uuid) |
|||
|
|||
|
|||
def do_list_fractals(): |
|||
LOG.info("listing all fractals") |
|||
|
|||
fractals = get_fractals() |
|||
output = PrettyTable(["UUID", "Dimensions", "Filesize"]) |
|||
for fractal in fractals: |
|||
output.add_row([ |
|||
fractal["uuid"], |
|||
"%d x %d pixels" % (fractal["width"], fractal["height"]), |
|||
"%d bytes" % (fractal["size"] or 0), |
|||
]) |
|||
print(output) |
|||
|
|||
|
|||
def get_fractals(page=1): |
|||
result = requests.get("%s/v1/fractal?page=%d" % |
|||
(CONF.endpoint_url, page)) |
|||
|
|||
fractals = [] |
|||
if result.status_code == 200: |
|||
data = json.loads(result.text) |
|||
if page < data['total_pages']: |
|||
fractals = data['objects'] + get_fractals(page + 1) |
|||
else: |
|||
return data['objects'] |
|||
|
|||
return fractals |
|||
|
|||
|
|||
def do_delete_fractal(): |
|||
LOG.info("deleting fractal %s" % CONF.command.uuid) |
|||
result = requests.delete("%s/v1/fractal/%s" % |
|||
(CONF.endpoint_url, CONF.command.uuid)) |
|||
LOG.debug("result: %s" %result) |
|||
|
|||
|
|||
def do_create_fractal(): |
|||
random.seed() |
|||
if CONF.command.tasks: |
|||
number = int(CONF.command.tasks) |
|||
else: |
|||
number = random.randint(int(CONF.command.min_tasks), |
|||
int(CONF.command.max_tasks)) |
|||
LOG.info("generating %d task(s)" % number) |
|||
for i in xrange(0, number): |
|||
task = get_random_task() |
|||
LOG.debug("created task %s" % task) |
|||
# NOTE(berendt): only necessary when using requests < 2.4.2 |
|||
headers = {'Content-type': 'application/json', |
|||
'Accept': 'text/plain'} |
|||
requests.post("%s/v1/fractal" % CONF.endpoint_url, |
|||
json.dumps(task), headers=headers) |
|||
|
|||
|
|||
def add_command_parsers(subparsers): |
|||
parser = subparsers.add_parser('create') |
|||
parser.set_defaults(func=do_create_fractal) |
|||
parser.add_argument("--height", default=None, |
|||
help="The height of the generate image.") |
|||
parser.add_argument("--min-height", default=256, |
|||
help="The minimum height of the generate image.") |
|||
parser.add_argument("--max-height", default=1024, |
|||
help="The maximum height of the generate image.") |
|||
parser.add_argument("--width", default=None, |
|||
help="The width of the generated image.") |
|||
parser.add_argument("--min-width", default=256, |
|||
help="The minimum width of the generated image.") |
|||
parser.add_argument("--max-width", default=1024, |
|||
help="The maximum width of the generated image.") |
|||
parser.add_argument("--iterations", default=None, |
|||
help="The number of iterations.") |
|||
parser.add_argument("--min-iterations", default=128, |
|||
help="The minimum number of iterations.") |
|||
parser.add_argument("--max-iterations", default=512, |
|||
help="The maximum number of iterations.") |
|||
parser.add_argument("--tasks", default=None, |
|||
help="The number of generated fractals.") |
|||
parser.add_argument("--min-tasks", default=1, |
|||
help="The minimum number of generated fractals.") |
|||
parser.add_argument("--max-tasks", default=10, |
|||
help="The maximum number of generated fractals.") |
|||
parser.add_argument("--xa", default=None, |
|||
help="The value for the parameter 'xa'.") |
|||
parser.add_argument("--min-xa", default=-1.0, |
|||
help="The minimum value for the parameter 'xa'.") |
|||
parser.add_argument("--max-xa", default=-4.0, |
|||
help="The maximum value for the parameter 'xa'.") |
|||
parser.add_argument("--xb", default=None, |
|||
help="The value for the parameter 'xb'.") |
|||
parser.add_argument("--min-xb", default=1.0, |
|||
help="The minimum value for the parameter 'xb'.") |
|||
parser.add_argument("--max-xb", default=4.0, |
|||
help="The maximum value for the parameter 'xb'.") |
|||
parser.add_argument("--ya", default=None, |
|||
help="The value for the parameter 'ya'.") |
|||
parser.add_argument("--min-ya", default=-0.5, |
|||
help="The minimum value for the parameter 'ya'.") |
|||
parser.add_argument("--max-ya", default=-3, |
|||
help="The maximum value for the parameter 'ya'.") |
|||
parser.add_argument("--yb", default=None, |
|||
help="The value for the parameter 'yb'.") |
|||
parser.add_argument("--min-yb", default=0.5, |
|||
help="The minimum value for the parameter 'yb'.") |
|||
parser.add_argument("--max-yb", default=3, |
|||
help="The maximum value for the parameter 'yb'.") |
|||
|
|||
parser = subparsers.add_parser('delete') |
|||
parser.set_defaults(func=do_delete_fractal) |
|||
parser.add_argument("uuid", help="Fractal to delete.") |
|||
|
|||
parser = subparsers.add_parser('show') |
|||
parser.set_defaults(func=do_show_fractal) |
|||
parser.add_argument("uuid", help="Fractal to show.") |
|||
|
|||
parser = subparsers.add_parser('get') |
|||
parser.set_defaults(func=do_get_fractal) |
|||
parser.add_argument("uuid", help="Fractal to download.") |
|||
|
|||
parser = subparsers.add_parser('list') |
|||
parser.set_defaults(func=do_list_fractals) |
|||
|
|||
|
|||
client_commands = cfg.SubCommandOpt('command', title='Commands', |
|||
help='Show available commands.', |
|||
handler=add_command_parsers) |
|||
|
|||
CONF.register_cli_opts([client_commands]) |
|||
|
|||
client_cli_opts = [ |
|||
cfg.StrOpt('endpoint-url', |
|||
default='http://localhost', |
|||
help='API connection URL') |
|||
] |
|||
|
|||
CONF.register_cli_opts(client_cli_opts) |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
log.register_options(CONF) |
|||
log.set_defaults() |
|||
|
|||
CONF(project='client', prog='faafo-client', |
|||
version=version.version_info.version_string()) |
|||
|
|||
log.setup(CONF, 'client', |
|||
version=version.version_info.version_string()) |
|||
|
|||
CONF.command.func() |
@ -0,0 +1,52 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
import os |
|||
import sys |
|||
|
|||
import kombu |
|||
from oslo_config import cfg |
|||
from oslo_log import log |
|||
|
|||
from faafo.worker import service as worker |
|||
from faafo import version |
|||
|
|||
LOG = log.getLogger('faafo.worker') |
|||
CONF = cfg.CONF |
|||
|
|||
# If ../faafo/__init__.py exists, add ../ to Python search path, so that |
|||
# it will override what happens to be installed in /usr/(local/)lib/python... |
|||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), |
|||
os.pardir, |
|||
os.pardir)) |
|||
if os.path.exists(os.path.join(possible_topdir, 'faafo', '__init__.py')): |
|||
sys.path.insert(0, possible_topdir) |
|||
|
|||
if __name__ == '__main__': |
|||
log.register_options(CONF) |
|||
log.set_defaults() |
|||
|
|||
CONF(project='worker', prog='faafo-worker', |
|||
default_config_files=['/etc/faafo/faafo.conf'], |
|||
version=version.version_info.version_string()) |
|||
|
|||
log.setup(CONF, 'worker', |
|||
version=version.version_info.version_string()) |
|||
|
|||
connection = kombu.Connection(CONF.transport_url) |
|||
server = worker.Worker(connection) |
|||
try: |
|||
server.run() |
|||
except KeyboardInterrupt: |
|||
LOG.info("Caught keyboard interrupt. Exiting.") |
@ -0,0 +1,198 @@ |
|||
#!/bin/bash |
|||
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
if [[ -e /etc/os-release ]]; then |
|||
|
|||
# NOTE(berendt): support for CentOS/RHEL/openSUSE/SLES will be added in the future |
|||
|
|||
source /etc/os-release |
|||
|
|||
INSTALL_DATABASE=0 |
|||
INSTALL_FAAFO=0 |
|||
INSTALL_MESSAGING=0 |
|||
RUN_API=0 |
|||
RUN_DEMO=0 |
|||
RUN_WORKER=0 |
|||
URL_DATABASE='sqlite:////tmp/sqlite.db' |
|||
URL_ENDPOINT='http://127.0.0.1' |
|||
URL_MESSAGING='amqp://guest:guest@localhost:5672/' |
|||
|
|||
while getopts e:m:d:i:r: FLAG; do |
|||
case $FLAG in |
|||
i) |
|||
case $OPTARG in |
|||
messaging) |
|||
INSTALL_MESSAGING=1 |
|||
;; |
|||
database) |
|||
INSTALL_DATABASE=1 |
|||
;; |
|||
faafo) |
|||
INSTALL_FAAFO=1 |
|||
;; |
|||
esac |
|||
;; |
|||
r) |
|||
case $OPTARG in |
|||
demo) |
|||
RUN_DEMO=1 |
|||
;; |
|||
api) |
|||
RUN_API=1 |
|||
;; |
|||
worker) |
|||
RUN_WORKER=1 |
|||
;; |
|||
esac |
|||
;; |
|||
e) |
|||
URL_ENDPOINT=$OPTARG |
|||
;; |
|||
|
|||
m) |
|||
URL_MESSAGING=$OPTARG |
|||
;; |
|||
|
|||
d) |
|||
URL_DATABASE=$OPTARG |
|||
;; |
|||
|
|||
*) |
|||
echo "error: unknown option $FLAG" |
|||
exit 1 |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
if [[ $ID = 'ubuntu' || $ID = 'debian' ]]; then |
|||
sudo apt-get update |
|||
elif [[ $ID = 'fedora' ]]; then |
|||
sudo dnf update -y |
|||
fi |
|||
|
|||
if [[ $INSTALL_DATABASE -eq 1 ]]; then |
|||
if [[ $ID = 'ubuntu' || $ID = 'debian' ]]; then |
|||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y mysql-server python-mysqldb |
|||
sudo sed -i -e "/bind-address/d" /etc/mysql/my.cnf |
|||
sudo service mysql restart |
|||
elif [[ $ID = 'fedora' ]]; then |
|||
sudo dnf install -y mariadb-server python-mysql |
|||
printf "[mysqld]\nbind-address = 127.0.0.1\n" | sudo tee /etc/my.cnf.d/faafo.conf |
|||
sudo systemctl enable mariadb |
|||
sudo systemctl start mariadb |
|||
else |
|||
echo "error: distribution $ID not supported" |
|||
exit 1 |
|||
fi |
|||
sudo mysqladmin password password |
|||
sudo mysql -uroot -ppassword mysql -e "CREATE DATABASE IF NOT EXISTS faafo; GRANT ALL PRIVILEGES ON faafo.* TO 'faafo'@'%' IDENTIFIED BY 'password';" |
|||
URL_DATABASE='mysql://root:password@localhost/faafo' |
|||
fi |
|||
|
|||
if [[ $INSTALL_MESSAGING -eq 1 ]]; then |
|||
if [[ $ID = 'ubuntu' || $ID = 'debian' ]]; then |
|||
sudo apt-get install -y rabbitmq-server |
|||
elif [[ $ID = 'fedora' ]]; then |
|||
sudo dnf install -y rabbitmq-server |
|||
sudo systemctl enable rabbitmq-server |
|||
sudo systemctl start rabbitmq-server |
|||
else |
|||
echo "error: distribution $ID not supported" |
|||
exit 1 |
|||
fi |
|||
fi |
|||
|
|||
if [[ $INSTALL_FAAFO -eq 1 ]]; then |
|||
if [[ $ID = 'ubuntu' || $ID = 'debian' ]]; then |
|||
sudo apt-get install -y python-dev python-pip supervisor git zlib1g-dev libmysqlclient-dev python-mysqldb |
|||
# Following is needed because of |
|||
# https://bugs.launchpad.net/ubuntu/+source/supervisor/+bug/1594740 |
|||
if [ $(lsb_release --short --codename) = xenial ]; then |
|||
# Make sure the daemon is enabled. |
|||
if ! systemctl --quiet is-enabled supervisor; then |
|||
systemctl enable supervisor |
|||
fi |
|||
# Make sure the daemon is started. |
|||
if ! systemctl --quiet is-active supervisor; then |
|||
systemctl start supervisor |
|||
fi |
|||
fi |
|||
elif [[ $ID = 'fedora' ]]; then |
|||
sudo dnf install -y python-devel python-pip supervisor git zlib-devel mariadb-devel gcc which python-mysql |
|||
sudo systemctl enable supervisord |
|||
sudo systemctl start supervisord |
|||
#elif [[ $ID = 'opensuse' || $ID = 'sles' ]]; then |
|||
# sudo zypper install -y python-devel python-pip |
|||
else |
|||
echo "error: distribution $ID not supported" |
|||
exit 1 |
|||
fi |
|||
|
|||
git clone https://git.openstack.org/openstack/faafo |
|||
cd faafo |
|||
# following line required by bug 1636150 |
|||
sudo pip install --upgrade pbr |
|||
sudo pip install -r requirements.txt |
|||
sudo python setup.py install |
|||
|
|||
sudo sed -i -e "s#transport_url = .*#transport_url = $URL_MESSAGING#" /etc/faafo/faafo.conf |
|||
sudo sed -i -e "s#database_url = .*#database_url = $URL_DATABASE#" /etc/faafo/faafo.conf |
|||
sudo sed -i -e "s#endpoint_url = .*#endpoint_url = $URL_ENDPOINT#" /etc/faafo/faafo.conf |
|||
fi |
|||
|
|||
|
|||
if [[ $RUN_API -eq 1 ]]; then |
|||
faafo_api=" |
|||
[program:faafo_api] |
|||
command=$(which faafo-api) |
|||
priority=10" |
|||
|
|||
if [[ $ID = 'ubuntu' || $ID = 'debian' ]]; then |
|||
echo "$faafo_api" | sudo tee -a /etc/supervisor/conf.d/faafo.conf |
|||
elif [[ $ID = 'fedora' ]]; then |
|||
echo "$faafo_api" | sudo tee -a /etc/supervisord.d/faafo.ini |
|||
else |
|||
echo "error: distribution $ID not supported" |
|||
exit 1 |
|||
fi |
|||
fi |
|||
|
|||
if [[ $RUN_WORKER -eq 1 ]]; then |
|||
faafo_worker=" |
|||
[program:faafo_worker] |
|||
command=$(which faafo-worker) |
|||
priority=20" |
|||
|
|||
if [[ $ID = 'ubuntu' || $ID = 'debian' ]]; then |
|||
echo "$faafo_worker" | sudo tee -a /etc/supervisor/conf.d/faafo.conf |
|||
elif [[ $ID = 'fedora' ]]; then |
|||
echo "$faafo_worker" | sudo tee -a /etc/supervisord.d/faafo.ini |
|||
else |
|||
echo "error: distribution $ID not supported" |
|||
exit 1 |
|||
fi |
|||
fi |
|||
|
|||
if [[ $RUN_WORKER -eq 1 || $RUN_API -eq 1 ]]; then |
|||
sudo supervisorctl reload |
|||
sleep 5 |
|||
fi |
|||
|
|||
if [[ $RUN_DEMO -eq 1 && $RUN_API -eq 1 ]]; then |
|||
faafo --endpoint-url $URL_ENDPOINT --debug create |
|||
fi |
|||
|
|||
else |
|||
exit 1 |
|||
fi |
@ -0,0 +1,56 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
import json |
|||
import requests |
|||
|
|||
url = 'http://127.0.0.1/api/fractal' |
|||
headers = {'Content-Type': 'application/json'} |
|||
|
|||
uuid = '13bf15a8-9f6c-4d59-956f-7d20f7484687' |
|||
data = { |
|||
'uuid': uuid, |
|||
'width': 100, |
|||
'height': 100, |
|||
'iterations': 10, |
|||
'xa': 1.0, |
|||
'xb': -1.0, |
|||
'ya': 1.0, |
|||
'yb': -1.0, |
|||
} |
|||
response = requests.post(url, data=json.dumps(data), headers=headers) |
|||
assert response.status_code == 201 |
|||
|
|||
response = requests.get(url, headers=headers) |
|||
assert response.status_code == 200 |
|||
print(response.json()) |
|||
|
|||
response = requests.get(url + '/' + uuid, headers=headers) |
|||
assert response.status_code == 200 |
|||
print(response.json()) |
|||
|
|||
data = { |
|||
'checksum': 'c6fef4ef13a577066c2281b53c82ce2c7e94e', |
|||
'duration': 10.12 |
|||
} |
|||
response = requests.put(url + '/' + uuid, data=json.dumps(data), |
|||
headers=headers) |
|||
assert response.status_code == 200 |
|||
|
|||
response = requests.get(url + '/' + uuid, headers=headers) |
|||
assert response.status_code == 200 |
|||
print(response.json()) |
|||
|
|||
response = requests.delete(url + '/' + uuid, headers=headers) |
|||
assert response.status_code == 204 |
@ -0,0 +1,17 @@ |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|||
# you may not use this file except in compliance with the License. |
|||
# You may obtain a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, |
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
# implied. |
|||
# See the License for the specific language governing permissions and |
|||
# limitations under the License. |
|||
|
|||
copyright = u'2015, OpenStack contributors' |
|||
master_doc = 'index' |
|||
project = u'First App Application for OpenStack' |
|||
source_suffix = '.rst' |
@ -0,0 +1,68 @@ |
|||
Development |
|||
=========== |
|||
|
|||
Vagrant environment |
|||
------------------- |
|||
|
|||
The `Vagrant <https://www.vagrantup.com/>`_ environment and the `Ansible <http://www.ansible.com/home>`_ |
|||
playbook is used only for local tests and development of the application. |
|||
|
|||
The installation of Vagrant is described at https://docs.vagrantup.com/v2/installation/index.html. |
|||
|
|||
To speedup the provisioning you can install the Vagrant plugin `vagrant-cachier <https://github.com/fgrehm/vagrant-cachier>`_. |
|||
|
|||
.. code:: |
|||
|
|||
$ vagrant plugin install vagrant-cachier |
|||
|
|||
Bootstrap the Vagrant environment. |
|||
|
|||
.. code:: |
|||
|
|||
$ vagrant up |
|||
|
|||
Now it is possible to login with SSH. |
|||
|
|||
.. code:: |
|||
|
|||
$ vagrant ssh |
|||
|
|||
Open a new screen or tmux session. Aftwards run the api, worker, and producer |
|||
services in the foreground, each service in a separate window. |
|||
|
|||
* :code:`faafo-api` |
|||
* :code:`faafo-worker` |
|||
* :code:`faafo-producer` |
|||
|
|||
RabbitMQ server |
|||
~~~~~~~~~~~~~~~ |
|||
|
|||
The webinterface of the RabbitMQ server is reachable on TCP port :code:`15672`. The login is |
|||
possible with the user :code:`guest` and the password :code:`guest`. |
|||
|
|||
MySQL server |
|||
~~~~~~~~~~~~ |
|||
|
|||
The password of the user :code:`root` is :code:`secretsecret`. The password of the user :code:`faafo` |
|||
for the database :code:`faafo` is also :code:`secretsecret`. |
|||
|
|||
Virtual environment |
|||
------------------- |
|||
|
|||
Create a new virtual environment, install all required dependencies and |
|||
the application itself. |
|||
|
|||
.. code:: |
|||
|
|||
$ virtualenv .venv |
|||
$ source .venv/bin/activate |
|||
$ pip install -r requirements.txt |
|||
$ python setup.py install |
|||
|
|||
Open a new screen or tmux session. Aftwards run the api and worker |
|||
services in the foreground, each service in a separate window. |
|||
|
|||
.. code:: |
|||
|
|||
$ source .venv/bin/activate; faafo-api |
|||
$ source .venv/bin/activate; faafo-worker |
@ -0,0 +1,15 @@ |
|||
digraph { |
|||
API -> Database [color=green]; |
|||
API -> Database [color=orange]; |
|||
Database -> API [color=red]; |
|||
API -> Webinterface [color=red]; |
|||
Producer -> API [color=orange]; |
|||
Producer -> API [color=green]; |
|||
Producer -> "Queue Service" [color=orange]; |
|||
"Queue Service" -> Worker [color=orange]; |
|||
Worker -> "Image File" [color=blue]; |
|||
Worker -> "Queue Service" [color=green]; |
|||
"Queue Service" -> Producer [color=green]; |
|||
"Image File" -> "Storage Backend" [color=blue]; |
|||
"Storage Backend" -> Webinterface [color=red]; |
|||
} |
After Width: 343 | Height: 443 | Size: 35 KiB |
@ -0,0 +1,3 @@ |
|||
#!/bin/sh |
|||
|
|||
dot -T png -o diagram.png diagram.dot |
After Width: 512 | Height: 512 | Size: 40 KiB |
After Width: 1365 | Height: 896 | Size: 202 KiB |
@ -0,0 +1,11 @@ |
|||
Implementation |
|||
============== |
|||
|
|||
Frameworks |
|||
---------- |
|||
|
|||
* http://flask.pocoo.org/ |
|||
* http://python-requests.org |
|||
* http://www.sqlalchemy.org/ |
|||
* https://github.com/celery/kombu |
|||
* https://pillow.readthedocs.org/ |
@ -0,0 +1,14 @@ |
|||
First App Application for OpenStack |
|||
=================================== |
|||
|
|||
Contents: |
|||
|
|||
.. toctree:: |
|||
:maxdepth: 2 |
|||
|
|||
workflow |
|||
implementation |
|||
installation |
|||
usage |
|||
development |
|||
references |
@ -0,0 +1,8 @@ |
|||
Installation |
|||
============ |
|||
|
|||
To install the ``First App Application for OpenStack`` run the following command. |
|||
|
|||
.. code:: |
|||
|
|||
pip install faafo |
@ -0,0 +1,6 @@ |
|||
References |
|||
========== |
|||
|
|||
* http://en.wikipedia.org/wiki/Julia_set |
|||
* http://en.wikipedia.org/wiki/Mandelbrot_set |
|||
* http://code.activestate.com/recipes/577120-julia-fractals/ |
@ -0,0 +1,42 @@ |
|||
Usage |
|||
===== |
|||
|
|||
Example image |
|||
------------- |
|||
|
|||
.. image:: images/example.png |
|||
|
|||
|
|||
Example outputs |
|||
--------------- |
|||
|
|||
Producer service |
|||
~~~~~~~~~~~~~~~~ |
|||
|
|||
.. code:: |
|||
|
|||
2015-03-25 23:01:29.308 22526 INFO faafo.producer [-] generating 1 task(s) |
|||
2015-03-25 23:01:29.344 22526 INFO faafo.producer [-] generated task: {'width': 510, 'yb': 2.478654026560605, 'uuid': '212e8c23-e67f-4bd3-86e1-5a5e811ee2f4', 'iterations': 281, 'xb': 1.1428457603077387, 'xa': -3.3528957195683087, 'ya': -2.1341119130263717, 'height': 278} |
|||
2015-03-25 23:01:30.295 22526 INFO faafo.producer [-] task 212e8c23-e67f-4bd3-86e1-5a5e811ee2f4 processed: {u'duration': 0.8725259304046631, u'checksum': u'b22d975c4f9dc77df5db96ce6264a4990d865dd8f800aba2ac03a065c2f09b1e', u'uuid': u'212e8c23-e67f-4bd3-86e1-5a5e811ee2f4'} |
|||
|
|||
Worker service |
|||
~~~~~~~~~~~~~~ |
|||
|
|||
.. code:: |
|||
|
|||
2015-03-25 23:01:29.378 22518 INFO faafo.worker [-] processing task 212e8c23-e67f-4bd3-86e1-5a5e811ee2f4 |
|||
2015-03-25 23:01:30.251 22518 INFO faafo.worker [-] task 212e8c23-e67f-4bd3-86e1-5a5e811ee2f4 processed in 0.872526 seconds |
|||
2015-03-25 23:01:30.268 22518 INFO faafo.worker [-] saved result of task 212e8c23-e67f-4bd3-86e1-5a5e811ee2f4 to file /home/vagrant/212e8c23-e67f-4bd3-86e1-5a5e811ee2f4.png |
|||
|
|||
|
|||
API Service |
|||
~~~~~~~~~~~ |
|||
.. code:: |
|||
|
|||
2015-03-25 23:01:29.342 22511 INFO werkzeug [-] 127.0.0.1 - - [25/Mar/2015 23:01:29] "POST /api/fractal HTTP/1.1" 201 - |
|||
2015-03-25 23:01:30.317 22511 INFO werkzeug [-] 127.0.0.1 - - [25/Mar/2015 23:01:30] "PUT /api/fractal/212e8c23-e67f-4bd3-86e1-5a5e811ee2f4 HTTP/1.1" 200 - |
|||
|
|||
Example webinterface view |
|||
------------------------- |
|||
|
|||
.. image:: images/screenshot_webinterface.png |
@ -0,0 +1,4 @@ |
|||
Workflow |
|||
-------- |
|||
|
|||
.. image:: images/diagram.png |
@ -0,0 +1,110 @@ |
|||
[DEFAULT] |
|||
|
|||
# |
|||
# From faafo.api |
|||
# |
|||
|
|||
# Listen address. (string value) |
|||
#listen_address = 0.0.0.0 |
|||
|
|||
# Bind port. (integer value) |
|||
#bind_port = 80 |
|||
|
|||
# Database connection URL. (string value) |
|||
database_url = sqlite:////tmp/sqlite.db |
|||
|
|||
# |
|||
# From faafo.queues |
|||
# |
|||
|
|||
# AMQP connection URL. (string value) |
|||
transport_url = amqp://guest:guest@localhost:5672// |
|||
|
|||
# |
|||
# From faafo.worker |
|||
# |
|||
|
|||
# API connection URL (string value) |
|||
endpoint_url = http://localhost |
|||
|
|||
# |
|||
# From oslo.log |
|||
# |
|||
|
|||
# Print debugging output (set logging level to DEBUG instead of |
|||
# default WARNING level). (boolean value) |
|||
#debug = false |
|||
|
|||
# The name of a logging configuration file. This file is appended to |
|||
# any existing logging configuration files. For details about logging |
|||
# configuration files, see the Python logging module documentation. |
|||
# (string value) |
|||
# Deprecated group/name - [DEFAULT]/log_config |
|||
#log_config_append = <None> |
|||
|
|||
# DEPRECATED. A logging.Formatter log message format string which may |
|||
# use any of the available logging.LogRecord attributes. This option |
|||
# is deprecated. Please use logging_context_format_string and |
|||
# logging_default_format_string instead. (string value) |
|||
#log_format = <None> |
|||
|
|||
# Format string for %%(asctime)s in log records. Default: %(default)s |
|||
# . (string value) |
|||
#log_date_format = %Y-%m-%d %H:%M:%S |
|||
|
|||
# (Optional) Name of log file to output to. If no default is set, |
|||
# logging will go to stdout. (string value) |
|||
# Deprecated group/name - [DEFAULT]/logfile |
|||
#log_file = <None> |
|||
|
|||
# (Optional) The base directory used for relative --log-file paths. |
|||
# (string value) |
|||
# Deprecated group/name - [DEFAULT]/logdir |
|||
#log_dir = <None> |
|||
|
|||
# Use syslog for logging. Existing syslog format is DEPRECATED during |
|||
# I, and will change in J to honor RFC5424. (boolean value) |
|||
#use_syslog = false |
|||
|
|||
# (Optional) Enables or disables syslog rfc5424 format for logging. If |
|||
# enabled, prefixes the MSG part of the syslog message with APP-NAME |
|||
# (RFC5424). The format without the APP-NAME is deprecated in I, and |
|||
# will be removed in J. (boolean value) |
|||
#use_syslog_rfc_format = false |
|||
|
|||
# Syslog facility to receive log lines. (string value) |
|||
#syslog_log_facility = LOG_USER |
|||
|
|||
# Log output to standard error. (boolean value) |
|||
#use_stderr = true |
|||
|
|||
# Format string to use for log messages with context. (string value) |
|||
#logging_context_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s |
|||
|
|||
# Format string to use for log messages without context. (string |
|||
# value) |
|||
#logging_default_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s |
|||
|
|||
# Data to append to log format when level is DEBUG. (string value) |
|||
#logging_debug_format_suffix = %(funcName)s %(pathname)s:%(lineno)d |
|||
|
|||
# Prefix each line of exception output with this format. (string |
|||
# value) |
|||
#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s |
|||
|
|||
# List of logger=LEVEL pairs. (list value) |
|||
#default_log_levels = amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,oslo.messaging=INFO,iso8601=WARN,requests.packages.urllib3.connectionpool=WARN,urllib3.connectionpool=WARN,websocket=WARN,requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN,keystonemiddleware=WARN,routes.middleware=WARN,stevedore=WARN |
|||
|
|||
# Enables or disables publication of error events. (boolean value) |
|||
#publish_errors = false |
|||
|
|||
# Enables or disables fatal status of deprecations. (boolean value) |
|||
#fatal_deprecations = false |
|||
|
|||
# The format for an instance that is passed with the log message. |
|||
# (string value) |
|||
#instance_format = "[instance: %(uuid)s] " |
|||
|
|||
# The format for an instance UUID that is passed with the log message. |
|||
# (string value) |
|||
#instance_uuid_format = "[instance: %(uuid)s] " |
@ -0,0 +1,125 @@ |
|||
[DEFAULT] |
|||
|
|||
# |
|||
# From faafo.api |
|||
# |
|||
|
|||
# Listen address. (string value) |
|||
#listen_address = 0.0.0.0 |
|||
|
|||
# Bind port. (integer value) |
|||
#bind_port = 80 |
|||
|
|||
# Database connection URL. (string value) |
|||
#database_url = sqlite:////tmp/sqlite.db |
|||
|
|||
# |
|||
# From faafo.queues |
|||
# |
|||
|
|||
# AMQP connection URL. (string value) |
|||
#transport_url = amqp://guest:guest@localhost:5672// |
|||
|
|||
# |
|||
# From faafo.worker |
|||
# |
|||
|
|||
# API connection URL (string value) |
|||
#endpoint_url = http://localhost |
|||
|
|||
# |
|||
# From oslo.log |
|||
# |
|||
|
|||
# If set to true, the logging level will be set to DEBUG instead of |
|||
# the default INFO level. (boolean value) |
|||
#debug = false |
|||
|
|||
# If set to false, the logging level will be set to WARNING instead of |
|||
# the default INFO level. (boolean value) |
|||
# This option is deprecated for removal. |
|||
# Its value may be silently ignored in the future. |
|||
#verbose = true |
|||
|
|||
# The name of a logging configuration file. This file is appended to |
|||
# any existing logging configuration files. For details about logging |
|||
# configuration files, see the Python logging module documentation. |
|||
# Note that when logging configuration files are used then all logging |
|||
# configuration is set in the configuration file and other logging |
|||
# configuration options are ignored (for example, |
|||
# logging_context_format_string). (string value) |
|||
# Deprecated group/name - [DEFAULT]/log_config |
|||
#log_config_append = <None> |
|||
|
|||
# Defines the format string for %%(asctime)s in log records. Default: |
|||
# %(default)s . This option is ignored if log_config_append is set. |
|||
# (string value) |
|||
#log_date_format = %Y-%m-%d %H:%M:%S |
|||
|
|||
# (Optional) Name of log file to send logging output to. If no default |
|||
# is set, logging will go to stderr as defined by use_stderr. This |
|||
# option is ignored if log_config_append is set. (string value) |
|||
# Deprecated group/name - [DEFAULT]/logfile |
|||
#log_file = <None> |
|||
|
|||
# (Optional) The base directory used for relative log_file paths. |
|||
# This option is ignored if log_config_append is set. (string value) |
|||
# Deprecated group/name - [DEFAULT]/logdir |
|||
#log_dir = <None> |
|||
|
|||
# Uses logging handler designed to watch file system. When log file is |
|||
# moved or removed this handler will open a new log file with |
|||
# specified path instantaneously. It makes sense only if log_file |
|||
# option is specified and Linux platform is used. This option is |
|||
# ignored if log_config_append is set. (boolean value) |
|||
#watch_log_file = false |
|||
|
|||
# Use syslog for logging. Existing syslog format is DEPRECATED and |
|||
# will be changed later to honor RFC5424. This option is ignored if |
|||
# log_config_append is set. (boolean value) |
|||
#use_syslog = false |
|||
|
|||
# Syslog facility to receive log lines. This option is ignored if |
|||
# log_config_append is set. (string value) |
|||
#syslog_log_facility = LOG_USER |
|||
|
|||
# Log output to standard error. This option is ignored if |
|||
# log_config_append is set. (boolean value) |
|||
#use_stderr = true |
|||
|
|||
# Format string to use for log messages with context. (string value) |
|||
#logging_context_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s |
|||
|
|||
# Format string to use for log messages when context is undefined. |
|||
# (string value) |
|||
#logging_default_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s |
|||
|
|||
# Additional data to append to log message when logging level for the |
|||
# message is DEBUG. (string value) |
|||
#logging_debug_format_suffix = %(funcName)s %(pathname)s:%(lineno)d |
|||
|
|||
# Prefix each line of exception output with this format. (string |
|||
# value) |
|||
#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d ERROR %(name)s %(instance)s |
|||
|
|||
# Defines the format string for %(user_identity)s that is used in |
|||
# logging_context_format_string. (string value) |
|||
#logging_user_identity_format = %(user)s %(tenant)s %(domain)s %(user_domain)s %(project_domain)s |
|||
|
|||
# List of package logging levels in logger=LEVEL pairs. This option is |
|||
# ignored if log_config_append is set. (list value) |
|||
#default_log_levels = amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,oslo.messaging=INFO,iso8601=WARN,requests.packages.urllib3.connectionpool=WARN,urllib3.connectionpool=WARN,websocket=WARN,requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN,keystonemiddleware=WARN,routes.middleware=WARN,stevedore=WARN,taskflow=WARN,keystoneauth=WARN,oslo.cache=INFO,dogpile.core.dogpile=INFO |
|||
|
|||
# Enables or disables publication of error events. (boolean value) |
|||
#publish_errors = false |
|||
|
|||
# The format for an instance that is passed with the log message. |
|||
# (string value) |
|||
#instance_format = "[instance: %(uuid)s] " |
|||
|
|||
# The format for an instance UUID that is passed with the log message. |
|||
# (string value) |
|||
#instance_uuid_format = "[instance: %(uuid)s] " |
|||
|
|||
# Enables or disables fatal status of deprecations. (boolean value) |
|||
#fatal_deprecations = false |
@ -0,0 +1,6 @@ |
|||
[DEFAULT] |
|||
output_file = etc/faafo.conf.sample |
|||
namespace = faafo.worker |
|||
namespace = faafo.api |
|||
namespace = oslo.log |
|||
namespace = faafo.queues |
@ -0,0 +1,146 @@ |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
import base64 |
|||
import copy |
|||
import cStringIO |
|||
from pkg_resources import resource_filename |
|||
|
|||
import flask |
|||
import flask.ext.restless |
|||
import flask.ext.sqlalchemy |
|||
from flask_bootstrap import Bootstrap |
|||
from kombu import Connection |
|||
from kombu.pools import producers |
|||
from oslo_config import cfg |
|||
from oslo_log import log |
|||
from PIL import Image |
|||
from sqlalchemy.dialects import mysql |
|||
|
|||
from faafo import queues |
|||
from faafo import version |
|||
|
|||
LOG = log.getLogger('faafo.api') |
|||
CONF = cfg.CONF |
|||
|
|||
api_opts = [ |
|||
cfg.StrOpt('listen-address', |
|||
default='0.0.0.0', |
|||
help='Listen address.'), |
|||
cfg.IntOpt('bind-port', |
|||
default='80', |
|||
help='Bind port.'), |
|||
cfg.StrOpt('database-url', |
|||
default='sqlite:////tmp/sqlite.db', |
|||
help='Database connection URL.') |
|||
] |
|||
|
|||
CONF.register_opts(api_opts) |
|||
|
|||
log.register_options(CONF) |
|||
log.set_defaults() |
|||
|
|||
CONF(project='api', prog='faafo-api', |
|||
default_config_files=['/etc/faafo/faafo.conf'], |
|||
version=version.version_info.version_string()) |
|||
|
|||
log.setup(CONF, 'api', |
|||
version=version.version_info.version_string()) |
|||
|
|||
template_path = resource_filename(__name__, "templates") |
|||
app = flask.Flask('faafo.api', template_folder=template_path) |
|||
app.config['DEBUG'] = CONF.debug |
|||
app.config['SQLALCHEMY_DATABASE_URI'] = CONF.database_url |
|||
db = flask.ext.sqlalchemy.SQLAlchemy(app) |
|||
Bootstrap(app) |
|||
|
|||
|
|||
def list_opts(): |
|||
"""Entry point for oslo-config-generator.""" |
|||
return [(None, copy.deepcopy(api_opts))] |
|||
|
|||
|
|||
class Fractal(db.Model): |
|||
uuid = db.Column(db.String(36), primary_key=True) |
|||
checksum = db.Column(db.String(256), unique=True) |
|||
url = db.Column(db.String(256), nullable=True) |
|||
duration = db.Column(db.Float) |
|||
size = db.Column(db.Integer, nullable=True) |
|||
width = db.Column(db.Integer, nullable=False) |
|||
height = db.Column(db.Integer, nullable=False) |
|||
iterations = db.Column(db.Integer, nullable=False) |
|||
xa = db.Column(db.Float, nullable=False) |
|||
xb = db.Column(db.Float, nullable=False) |
|||
ya = db.Column(db.Float, nullable=False) |
|||
yb = db.Column(db.Float, nullable=False) |
|||
|
|||
if CONF.database_url.startswith('mysql'): |
|||
LOG.debug('Using MySQL database backend') |
|||
image = db.Column(mysql.MEDIUMBLOB, nullable=True) |
|||
else: |
|||
image = db.Column(db.LargeBinary, nullable=True) |
|||
|
|||
generated_by = db.Column(db.String(256), nullable=True) |
|||
|
|||
def __repr__(self): |
|||
return '<Fractal %s>' % self.uuid |
|||
|
|||
|
|||
db.create_all() |
|||
manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db) |
|||
connection = Connection(CONF.transport_url) |
|||
|
|||
|
|||
@app.route('/', methods=['GET']) |
|||
@app.route('/index', methods=['GET']) |
|||
@app.route('/index/<int:page>', methods=['GET']) |
|||
def index(page=1): |
|||
fractals = Fractal.query.filter( |
|||
(Fractal.checksum != None) & (Fractal.size != None)).paginate( # noqa |
|||
page, 5, error_out=False) |
|||
return flask.render_template('index.html', fractals=fractals) |
|||
|
|||
|
|||
@app.route('/fractal/<string:fractalid>', methods=['GET']) |
|||
def get_fractal(fractalid): |
|||
fractal = Fractal.query.filter_by(uuid=fractalid).first() |
|||
if not fractal: |
|||
response = flask.jsonify({'code': 404, |
|||
'message': 'Fracal not found'}) |
|||
response.status_code = 404 |
|||
else: |
|||
image_data = base64.b64decode(fractal.image) |
|||
image = Image.open(cStringIO.StringIO(image_data)) |
|||
output = cStringIO.StringIO() |
|||
image.save(output, "PNG") |
|||
image.seek(0) |
|||
response = flask.make_response(output.getvalue()) |
|||
response.content_type = "image/png" |
|||
|
|||
return response |
|||
|
|||
|
|||
def generate_fractal(**kwargs): |
|||
with producers[connection].acquire(block=True) as producer: |
|||
producer.publish(kwargs['result'], |
|||
serializer='json', |
|||
exchange=queues.task_exchange, |
|||
declare=[queues.task_exchange], |
|||
routing_key='normal') |
|||
|
|||
|
|||
def main(): |
|||
manager.create_api(Fractal, methods=['GET', 'POST', 'DELETE', 'PUT'], |
|||
postprocessors={'POST': [generate_fractal]}, |
|||
exclude_columns=['image'], |
|||
url_prefix='/v1') |
|||
app.run(host=CONF.listen_address, port=CONF.bind_port) |
@ -0,0 +1,60 @@ |
|||
{% extends "bootstrap/base.html" %} |
|||
{% block title %}First App Application for OpenStack{% endblock %} |
|||
{% from "bootstrap/pagination.html" import render_pagination %} |
|||
|
|||
{% block content %} |
|||
{{render_pagination(fractals)}} |
|||
{% for fractal in fractals.items %} |
|||
<div class="row"> |
|||
<div class="col-xs-5 col-md-3"> |
|||
<a href="/fractal/{{ fractal.uuid }}" class="thumbnail"> |
|||
<img src="/fractal/{{ fractal.uuid }}" style="max-height: 300px; max-width: 300px"> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-7 col-md-9"> |
|||
<table class="table table-striped"> |
|||
<tbody> |
|||
<tr> |
|||
<td>UUID</td> |
|||
<td>{{ fractal.uuid }}</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Duration</td> |
|||
<td>{{ fractal.duration }} seconds</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Dimensions</td> |
|||
<td>{{ fractal.width }} x {{ fractal.height }} px</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Iterations</td> |
|||
<td>{{ fractal.iterations }}</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Parameters</td> |
|||
<td> |
|||
<pre>xa = {{ fractal.xa }} |
|||
xb = {{ fractal.xb }} |
|||
ya = {{ fractal.ya }} |
|||
yb = {{ fractal.yb }}</pre> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Filesize</td> |
|||
<td>{{ fractal.size}} bytes</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Checksum</td> |
|||
<td><pre>{{ fractal.checksum }}</pre></td> |
|||
</tr> |
|||
<tr> |
|||
<td>Generated by</td> |
|||
<td>{{ fractal.generated_by }}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
{% endfor %} |
|||
{{render_pagination(fractals)}} |
|||
{% endblock %} |
@ -0,0 +1,32 @@ |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
import copy |
|||
|
|||
import kombu |
|||
from oslo_config import cfg |
|||
|
|||
task_exchange = kombu.Exchange('tasks', type='direct') |
|||
task_queue = kombu.Queue('normal', task_exchange, routing_key='normal') |
|||
|
|||
queues_opts = [ |
|||
cfg.StrOpt('transport-url', |
|||
default='amqp://guest:guest@localhost:5672//', |
|||
help='AMQP connection URL.') |
|||
] |
|||
|
|||
cfg.CONF.register_opts(queues_opts) |
|||
|
|||
|
|||
def list_opts(): |
|||
"""Entry point for oslo-config-generator.""" |
|||
return [(None, copy.deepcopy(queues_opts))] |
@ -0,0 +1,15 @@ |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
import pbr.version |
|||
|
|||
version_info = pbr.version.VersionInfo('faafo') |
@ -0,0 +1,156 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|||
# not use this file except in compliance with the License. You may obtain |
|||
# a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations |
|||
# under the License. |
|||
|
|||
# based on http://code.activestate.com/recipes/577120-julia-fractals/ |
|||
|
|||
import base64 |
|||
import copy |
|||
import hashlib |
|||
import json |
|||
import os |
|||
from PIL import Image |
|||
import random |
|||
import socket |
|||
import tempfile |
|||
import time |
|||
|
|||
from kombu.mixins import ConsumerMixin |
|||
from oslo_config import cfg |
|||
from oslo_log import log |
|||
import requests |
|||
|
|||
from faafo import queues |
|||
|
|||
LOG = log.getLogger('faafo.worker') |
|||
CONF = cfg.CONF |
|||
|
|||
|
|||
worker_opts = { |
|||
cfg.StrOpt('endpoint-url', |
|||
default='http://localhost', |
|||
help='API connection URL') |
|||
} |
|||
|
|||
CONF.register_opts(worker_opts) |
|||
|
|||
|
|||
def list_opts(): |
|||
"""Entry point for oslo-config-generator.""" |
|||
return [(None, copy.deepcopy(worker_opts))] |
|||
|
|||
|
|||
class JuliaSet(object): |
|||
|
|||
def __init__(self, width, height, xa=-2.0, xb=2.0, ya=-1.5, yb=1.5, |
|||
iterations=255): |
|||
self.xa = xa |
|||
self.xb = xb |
|||
self.ya = ya |
|||
self.yb = yb |
|||
self.iterations = iterations |
|||
self.width = width |
|||
self.height = height |
|||
self.draw() |
|||
|
|||
def draw(self): |
|||
self.image = Image.new("RGB", (self.width, self.height)) |
|||
c, z = self._set_point() |
|||
for y in range(self.height): |
|||
zy = y * (self.yb - self.ya) / (self.height - 1) + self.ya |
|||
for x in range(self.width): |
|||
zx = x * (self.xb - self.xa) / (self.width - 1) + self.xa |
|||
z = zx + zy * 1j |
|||
for i in range(self.iterations): |
|||
if abs(z) > 2.0: |
|||
break |
|||
z = z * z + c |
|||
self.image.putpixel((x, y), |
|||
(i % 8 * 32, i % 16 * 16, i % 32 * 8)) |
|||
|
|||
def get_file(self): |
|||
with tempfile.NamedTemporaryFile(delete=False) as fp: |
|||
self.image.save(fp, "PNG") |
|||
return fp.name |
|||
|
|||
def _set_point(self): |
|||
random.seed() |
|||
while True: |
|||
cx = random.random() * (self.xb - self.xa) + self.xa |
|||
cy = random.random() * (self.yb - self.ya) + self.ya |
|||
c = cx + cy * 1j |
|||
z = c |
|||
for i in range(self.iterations): |
|||
if abs(z) > 2.0: |
|||
break |
|||
z = z * z + c |
|||
if i > 10 and i < 100: |
|||
break |
|||
|
|||
return (c, z) |
|||
|
|||
|
|||
class Worker(ConsumerMixin): |
|||
|
|||
def __init__(self, connection): |
|||
self.connection = connection |
|||
|
|||
def get_consumers(self, Consumer, channel): |
|||
return [Consumer(queues=queues.task_queue, |
|||
accept=['json'], |
|||
callbacks=[self.process])] |
|||
|
|||
def process(self, task, message): |
|||
LOG.info("processing task %s" % task['uuid']) |
|||
LOG.debug(task) |
|||
start_time = time.time() |
|||
juliaset = JuliaSet(task['width'], |
|||
task['height'], |
|||
task['xa'], |
|||
task['xb'], |
|||
task['ya'], |
|||
task['yb'], |
|||
task['iterations']) |
|||
elapsed_time = time.time() - start_time |
|||
LOG.info("task %s processed in %f seconds" % |
|||
(task['uuid'], elapsed_time)) |
|||
|
|||
filename = juliaset.get_file() |
|||
LOG.debug("saved result of task %s to temporary file %s" % |
|||
(task['uuid'], filename)) |
|||
with open(filename, "rb") as fp: |
|||
size = os.fstat(fp.fileno()).st_size |
|||
image = base64.b64encode(fp.read()) |
|||
checksum = hashlib.sha256(open(filename, 'rb').read()).hexdigest() |
|||
os.remove(filename) |
|||
LOG.debug("removed temporary file %s" % filename) |
|||
|
|||
result = { |
|||
'uuid': task['uuid'], |
|||
'duration': elapsed_time, |
|||
'image': image, |
|||
'checksum': checksum, |
|||
'size': size, |
|||
'generated_by': socket.gethostname() |
|||
} |
|||
|
|||
# NOTE(berendt): only necessary when using requests < 2.4.2 |
|||
headers = {'Content-type': 'application/json', |
|||
'Accept': 'text/plain'} |
|||
|
|||
requests.put("%s/v1/fractal/%s" % |
|||
(CONF.endpoint_url, str(task['uuid'])), |
|||
json.dumps(result), headers=headers) |
|||
|
|||
message.ack() |
|||
return result |
@ -0,0 +1,17 @@ |
|||
pbr>=1.6 |
|||
pytz |
|||
positional |
|||
iso8601 |
|||
anyjson>=0.3.3 |
|||
eventlet>=0.17.4 |
|||
PyMySQL>=0.6.2,<0.7 # 0.7 design change breaks faafo, MIT License |
|||
Pillow==2.4.0 # MIT |
|||
requests>=2.5.2 |
|||
Flask-Bootstrap |
|||
Flask>=0.10,<1.0 |
|||
flask-restless |
|||
flask-sqlalchemy |
|||
oslo.config>=2.3.0 # Apache-2.0 |
|||
oslo.log>=1.8.0 # Apache-2.0 |
|||
PrettyTable>=0.7,<0.8 |
|||
kombu>=3.0.7 |
@ -0,0 +1,54 @@ |
|||
[metadata] |
|||
name = faafo |
|||
summary = First App Application for OpenStack |
|||
description-file = |
|||
README.rst |
|||
author = OpenStack Documentation |
|||
author-email = openstack-doc@lists.openstack.org |
|||
home-page = http://docs.openstack.org/developer/faafo/ |
|||
classifier = |
|||
Environment :: OpenStack |
|||
Intended Audience :: Information Technology |
|||
Intended Audience :: System Administrators |
|||
License :: OSI Approved :: Apache Software License |
|||
Operating System :: POSIX :: Linux |
|||
Programming Language :: Python |
|||
Programming Language :: Python :: 2 |
|||
Programming Language :: Python :: 2.7 |
|||
|
|||
[files] |
|||
packages = |
|||
faafo |
|||
scripts = |
|||
bin/faafo |
|||
bin/faafo-worker |
|||
extra_files = |
|||
faafo/api/templates/index.html |
|||
data_files = |
|||
/etc/faafo = etc/faafo.conf |
|||
|
|||
[global] |
|||
setup-hooks = |
|||
pbr.hooks.setup_hook |
|||
|
|||
[entry_points] |
|||
console_scripts = |
|||
faafo-api = faafo.api.service:main |
|||
oslo.config.opts = |
|||
faafo.api = faafo.api.service:list_opts |
|||
faafo.worker = faafo.worker.service:list_opts |
|||
faafo.queues= faafo.queues:list_opts |
|||
|
|||
[build_sphinx] |
|||
source-dir = doc/source |
|||
build-dir = doc/build |
|||
all_files = 1 |
|||
|
|||
[upload_sphinx] |
|||
upload-dir = doc/build/html |
|||
|
|||
[wheel] |
|||
universal = 1 |
|||
|
|||
[pbr] |
|||
warnerrors = true |
@ -0,0 +1,29 @@ |
|||
#!/usr/bin/env python |
|||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. |
|||
# |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|||
# you may not use this file except in compliance with the License. |
|||
# You may obtain a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, |
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
# implied. |
|||
# See the License for the specific language governing permissions and |
|||
# limitations under the License. |
|||
|
|||
import setuptools |
|||
|
|||
# In python < 2.7.4, a lazy loading of package `pbr` will break |
|||
# setuptools if some other modules registered functions in `atexit`. |
|||
# solution from: http://bugs.python.org/issue15881#msg170215 |
|||
try: |
|||
import multiprocessing # noqa |
|||
except ImportError: |
|||
pass |
|||
|
|||
setuptools.setup( |
|||
setup_requires=['pbr'], |
|||
pbr=True) |
@ -0,0 +1,2 @@ |
|||
hacking<0.11,>=0.10 |
|||
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 |
@ -0,0 +1,27 @@ |
|||
[tox] |
|||
minversion = 1.6 |
|||
envlist = pep8 |
|||
skipsdist = True |
|||
|
|||
[testenv] |
|||
usedevelop = True |
|||
deps = -r{toxinidir}/test-requirements.txt |
|||
-r{toxinidir}/requirements.txt |
|||
install_command = pip install -U {opts} {packages} |
|||
|
|||
[testenv:venv] |
|||
commands = {posargs} |
|||
|
|||
[testenv:docs] |
|||
commands = python setup.py build_sphinx |
|||
|
|||
[testenv:genconfig] |
|||
commands = |
|||
oslo-config-generator --config-file etc/oslo-config-generator/faafo.conf |
|||
|
|||
[testenv:pep8] |
|||
commands = flake8 {posargs} |
|||
|
|||
[flake8] |
|||
show-source = True |
|||
exclude=.venv,.git,.tox,*egg*,build,*openstack/common* |
Write
Preview
Loading…
Cancel
Save
Reference in new issue