Remove old Flask Refstack application

It will be replaced with API v1 written on Pecan

https://storyboard.openstack.org/#!/story/132

Change-Id: I69a27b640544c22790e768be440a8396c9c3b7b8
This commit is contained in:
sslypushenko 2014-12-31 10:38:11 +02:00
parent 5cae323d2b
commit 3f56fde9c1
31 changed files with 0 additions and 1483 deletions

View File

@ -4,7 +4,6 @@ RefStack/TCUP and Driver Test
This project support collection and publication of Community Test results for OpenStack. There are multiple components of this effort:
* `RefStack <doc/refstack.md>`_: Community-facing API for registration of interop-compliance endpoints for on-demand testing.
* `TCUP <doc/tcup.md>`_: Portable, Containerized Tempest for Community running and reporting results to RefStack
* Driver Test
Participate
@ -31,10 +30,3 @@ Collecting the Results > Running RefStack
RefStack is a Web UI and API used to collect and display test results. This information is used by the DefCore committee to help select must-pass capabilities.
`RefStack docs <doc/refstack.md>`_
Test your Cloud and Share! > Running TCUP
-----------------------------------------
TCUP (Tempest in a Container to Upload from Probe) is a portable way for community members to quickly and consistently run Tempest against private and public clouds.
`TCUP docs <doc/tcup.md>`_

View File

@ -1,24 +0,0 @@
"""added_alt_user
Revision ID: 121ee191d348
Revises: 2d1f3e3cd357
Create Date: 2014-04-07 11:43:51.800255
"""
# revision identifiers, used by Alembic.
revision = '121ee191d348'
down_revision = '2d1f3e3cd357'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column(
'cloud',
sa.Column('alt_user', sa.String(length=80), nullable=True))
def downgrade():
op.drop_column('cloud', 'alt_user')

View File

@ -1,28 +0,0 @@
"""added architecture to cloud
Revision ID: 2d1f3e3cd357
Revises: 4a425a7aff50
Create Date: 2014-03-07 12:11:20.403933
"""
# revision identifiers, used by Alembic.
revision = '2d1f3e3cd357'
down_revision = '4a425a7aff50'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column(
'cloud',
sa.Column('architecture', sa.String(length=40), nullable=True))
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('cloud', 'architecture')
### end Alembic commands ###

View File

@ -1,100 +0,0 @@
"""major restructure
Revision ID: 4a425a7aff50
Revises: None
Create Date: 2014-03-07 11:20:58.899889
"""
# revision identifiers, used by Alembic.
revision = '4a425a7aff50'
down_revision = None
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table(
'vendor',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('vendor_name', sa.String(length=80), nullable=True),
sa.Column('contact_email', sa.String(length=120), nullable=True),
sa.Column('contact_name', sa.String(length=120), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('contact_email'),
sa.UniqueConstraint('vendor_name')
)
op.create_table(
'user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('vendor_id', sa.Integer(), nullable=True),
sa.Column('name', sa.String(length=60), nullable=True),
sa.Column('email', sa.String(length=200), nullable=True),
sa.Column('email_verified', sa.Boolean(), nullable=True),
sa.Column('openid', sa.String(length=200), nullable=True),
sa.Column('authorized', sa.Boolean(), nullable=True),
sa.Column('su', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['vendor_id'], ['vendor.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('openid')
)
op.create_table(
'apikey',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=60), nullable=True),
sa.Column('key', sa.String(length=200), nullable=True),
sa.Column('openid', sa.String(length=200), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table(
'cloud',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('label', sa.String(length=60), nullable=True),
sa.Column('endpoint', sa.String(length=512), nullable=True),
sa.Column('endpoint_v3', sa.String(length=512), nullable=True),
sa.Column('admin_endpoint', sa.String(length=512), nullable=True),
sa.Column('test_user', sa.String(length=80), nullable=True),
sa.Column('admin_user', sa.String(length=80), nullable=True),
sa.Column('version', sa.String(length=80), nullable=True),
sa.Column('tempest_sha', sa.String(length=128), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table(
'test',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('cloud_id', sa.Integer(), nullable=True),
sa.Column('finished', sa.Boolean(), nullable=True),
sa.Column('subunit', sa.String(length=4096), nullable=True),
sa.Column('parsed', sa.String(length=4096), nullable=True),
sa.ForeignKeyConstraint(['cloud_id'], ['cloud.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table(
'test_status',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('test_id', sa.Integer(), nullable=True),
sa.Column('message', sa.String(length=1024), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['test_id'], ['test.id'], ),
sa.PrimaryKeyConstraint('id')
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('test_status')
op.drop_table('test')
op.drop_table('cloud')
op.drop_table('apikey')
op.drop_table('user')
op.drop_table('vendor')
### end Alembic commands ###

View File

@ -1,32 +0,0 @@
Below Instructions guide you to run tempest manually from tcup container :
Pre-requisites : set up docker container by following instructions in the tcup.md document
Once you are in TCUP container, make sure you can able to ping $OS_AUTH_URL
Instructions :
1)cd tempest/etc
2)copy tempest.conf.sample tempest.conf
3)update the tempest.conf file with the target cloud values(AUTH_URL,User name,Password,Tenent name,Tenent Id,IMAGE_ID,IMAGE_ID_ALT)
4)update below attributes in tempest.conf with the OS_VALUES then save the tempest.conf file
**Tempest.conf** ** Replace with ****
uri = OS_AUTH_URL
username = OS_USERNAME
password = OS_PASSWORD
tenant name = OS_TENANT_NAME
image_ref = {$IMAGE_ID}(run glance image-list command on target cloud,
copy image id then replace here)
image_ref_alt = {$IMAGE_ID_ALT}(run glance image-list command on target
cloud,copy image id then replace here)
5)cd tempest
6)nosetests -v tempest ( to run full tempest suits)
If you want to stop the tempest execution at the first failure,use below command
7)nosetests -vx tempest

View File

@ -1,70 +0,0 @@
TCUP Configuration
===========================
The following instructions are designs run Refstack/Tempest in a container with minimal setup on your system.
> These steps are do not install Refstack for contributions or development, they are intended for a user who wants to just run and report test results.
1. Make sure you have python and wget installed for your operating system.
1. Install Docker using [[https://www.docker.io/gettingstarted/#h_installation]]
1. Note: if you are in an environment with a proxy, make sure you configure `/etc/default/docker` to leverage the proxy too!
1. You may want to prep-the environment using `sudo docker pull ubuntu:13.10`
1. Setup Docker to run without sudo
1. permanently (recommended):
1. `sudo usermod -a -G docker <your-user>`
1. you will need to reboot after this change
1. short term: `sudo chmod 666 /var/run/docker.sock`
1. Get the code: `wget https://raw.githubusercontent.com/stackforge/refstack/master/scripts/tcup.py`
1. note: you can also get the code by cloning the Refstack and running the code in `/scripts/tcup.py`
1. Set your environment variables to access the test target cloud
1. generally, you will `source openrc.sh` to load the cloud credentials and URLs
Note : once you have loaded openrc.sh, type this command : env | grep OS_ then make sure values are assigned to each of the below:
*OS_TENANT_ID
*OS_PASSWORD
*OS_AUTH_URL
*OS_USERNAME
*OS_TENANT_NAME
If values are missing for any of the above, you could manually export for missing env variable (ex:export OS_PASSWORD=<YourPassword>)
1. Run TCUP: `python tcup.py`
1. if you want to work on the code from Refstack, use `scripts/tcup.py'
## Troubleshooting TCUP
Before troubleshooting TCUP, make sure that you have network connectivity to our target cloud from the host system (the one you run TCUP on).
1. ping the IP or FQDN from `echo $OS_AUTH_URL`
1. get a "HTTP/1.1 200 OK" response from `curl -I $OS_AUTH_URL`
There are several ways to trouble shoot, TCUP.
1. Run TCUP using the debug flag: `tcup.py --debug`
1. Attach to the container as instructed at the end of the TCUP script
1. Inside the container:
1. check your environment variables include the OS_* values using `export | grep OS_`
1. ping the IP or FQDN from `echo $OS_AUTH_URL`
1. get "200 OK" from Keystone using `curl -I $OS_AUTH_URL`
1. confirm your credentials are working using `keystone catalog`
## Docker Tips
1. You can inspect which containers are running!
1. `docker ps` shows the running containers
1. `docker attach` allows you to connect to a container (may have to press enter)
1. exit from inside the container with `Ctrl-p` + `Ctrl-q`
1. Orphaned Containers: Over time, you may end up with [orphaned contaniers](http://jimhoskins.com/2013/07/27/remove-untagged-docker-images.html), use the following to clean them up
1. `docker rm $(docker ps -a -q)`
1. `docker rmi $(docker images | grep "^<none>" | awk "{print $3}")`
## For Developers
If you run TCUP in debug mode (`export DEBUG=true` or using `--debug` parameter) then TCUP will automatically mount your PWD as /dev.
If you run TCUP from your Refstack clone, then you can work directly in Refstack code from inside
a TCUP container from the /dev directory.

View File

@ -1,28 +0,0 @@
import flask
from flask.ext.admin.contrib import sqla
import models
# Global admin object
from refstack.extensions import admin
from refstack.extensions import db
class SecureView(sqla.ModelView):
def is_accessible(self):
# let us look at the admin if we're in debug mode
if flask.current_app.debug:
return True
return flask.g.user.su is not False
def init_app(app):
admin.init_app(app)
def configure_admin(app):
admin.add_view(SecureView(models.ApiKey, db.session))
admin.add_view(SecureView(models.Cloud, db.session))
admin.add_view(SecureView(models.User, db.session))
admin.add_view(SecureView(models.Vendor, db.session))

View File

@ -1,81 +0,0 @@
#
# Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
#
# 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.
#
"""Basic API code.
This is using Flask-Restless at the moment because it is super simple,
but probably should be moved to a more versatile framework like
Flask-Restful later on.
"""
import flask
from flask.ext import restless
import models
from refstack.extensions import api
def init_app(app, *args, **kw):
api.init_app(app, *args, **kw)
def access_control(**kw):
if not flask.g.user:
raise _not_authorized()
if not flask.g.user.su:
return _not_authorized()
# That's it, we're defaulting to superuser only access
# until we flesh this out further
ALL_METHODS = {'GET_SINGLE': [access_control],
'GET_MANY': [access_control],
'PUT_SINGLE': [access_control],
'PUT_MANY': [access_control],
'POST': [access_control],
'DELETE': [access_control]}
def configure_api(app):
cloud_api = api.create_api_blueprint(models.Cloud,
preprocessors=ALL_METHODS)
cloud_api.before_request(authenticate)
app.register_blueprint(cloud_api)
def _not_authorized():
return restless.ProcessingException(message='Not Authorized',
status_code=401)
def authenticate():
# If we're already authenticated, we can ignore this
if flask.g.user:
return
# Otherwise check headers
openid = flask.request.headers.get('X-AUTH-OPENID')
if openid:
# In debug mode accept anything
if flask.current_app.debug and False:
flask.g.user = models.User.query.filter_by(openid=openid).first()
return
apikey = flask.request.headers.get('X-AUTH-APIKEY')
apikey_ref = models.ApiKey.query.filter_by(key=apikey)
if apikey_ref['openid'] == openid:
flask.g.user = apikey_ref.user

View File

@ -1,207 +0,0 @@
#
# Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
#
# 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
from flask import Flask, render_template
from refstack.config import DefaultConfig
import admin
import api
from refstack.extensions import db
from refstack.extensions import oid
from refstack import utils
INSTANCE_FOLDER_PATH = utils.INSTANCE_FOLDER_PATH
# For import *
__all__ = ['create_app']
DEFAULT_BLUEPRINTS = tuple()
# frontend,
# user,
# settings,
# api,
# admin,
#)
def create_app(config=None, app_name=None, blueprints=None):
"""Create a Flask app."""
if app_name is None:
app_name = DefaultConfig.PROJECT
if blueprints is None:
blueprints = DEFAULT_BLUEPRINTS
# NOTE(termie): Flask has this new instance_path stuff that allows
# you to keep config and such in different places, but I don't really
# see how that is going to be very helpful, so we're going to stick
# to using config relative to the root unless we explicitly set such
# a path in the INSTANCE_FOLDER_PATH environment variable.
app = Flask(app_name,
instance_path=INSTANCE_FOLDER_PATH,
instance_relative_config=True)
configure_app(app, config)
configure_hook(app)
configure_blueprints(app, blueprints)
configure_extensions(app)
configure_logging(app)
configure_template_filters(app)
configure_error_handlers(app)
if app.debug:
print(utils.dump_config(app))
return app
def configure_app(app, config=None):
"""Different ways of configurations."""
# http://flask.pocoo.org/docs/api/#configuration
app.config.from_object(DefaultConfig)
# If we've set the INSTANCE_FOLDER_PATH environment var, this may be
# loaded from an instance folder, otherwise relative to flask.root_path.
# http://flask.pocoo.org/docs/config/#instance-folders
app.config.from_pyfile('refstack.cfg', silent=True)
if config:
app.config.from_object(config)
def configure_extensions(app):
# flask-sqlalchemy
db.init_app(app)
# flask-admin
admin.init_app(app)
admin.configure_admin(app)
# flask-restless
api.init_app(app, flask_sqlalchemy_db=db)
api.configure_api(app)
## flask-mail
#mail.init_app(app)
## flask-cache
#cache.init_app(app)
## flask-babel
#babel = Babel(app)
#@babel.localeselector
#def get_locale():
# accept_languages = app.config.get('ACCEPT_LANGUAGES')
# return request.accept_languages.best_match(accept_languages)
## flask-login
#login_manager.login_view = 'frontend.login'
#login_manager.refresh_view = 'frontend.reauth'
#@login_manager.user_loader
#def load_user(id):
# return User.query.get(id)
#login_manager.setup_app(app)
# flask-openid
oid.init_app(app)
def configure_blueprints(app, blueprints):
"""Configure blueprints in views."""
for blueprint in blueprints:
app.register_blueprint(blueprint)
def configure_template_filters(app):
@app.template_filter()
def pretty_date(value):
return pretty_date(value)
@app.template_filter()
def format_date(value, format='%Y-%m-%d'):
return value.strftime(format)
def configure_logging(app):
"""Configure file(info) and email(error) logging."""
if app.debug or app.testing:
# Skip debug and test mode. Just check standard output.
return
import logging
from logging.handlers import SMTPHandler
# Set info level on logger, which might be overwritten by handlers.
# Suppress DEBUG messages.
app.logger.setLevel(logging.INFO)
info_log = os.path.join(app.config['LOG_FOLDER'], 'info.log')
info_file_handler = logging.handlers.RotatingFileHandler(
info_log, maxBytes=100000, backupCount=10)
info_file_handler.setLevel(logging.INFO)
info_file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]')
)
app.logger.addHandler(info_file_handler)
# Testing
#app.logger.info("testing info.")
#app.logger.warn("testing warn.")
#app.logger.error("testing error.")
mail_handler = SMTPHandler(app.config['MAIL_SERVER'],
app.config['MAIL_USERNAME'],
app.config['ADMINS'],
'O_ops... %s failed!' % app.config['PROJECT'],
(app.config['MAIL_USERNAME'],
app.config['MAIL_PASSWORD']))
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]')
)
app.logger.addHandler(mail_handler)
def configure_hook(app):
@app.before_request
def before_request():
pass
def configure_error_handlers(app):
@app.errorhandler(403)
def forbidden_page(error):
return render_template("errors/forbidden_page.html"), 403
@app.errorhandler(404)
def page_not_found(error):
return render_template("errors/page_not_found.html"), 404
@app.errorhandler(500)
def server_error_page(error):
return render_template("errors/server_error.html"), 500

View File

@ -1,10 +0,0 @@
lockfile
subunit
python-daemon
extras
statsd>=1.0.0,<3.0
sqlalchemy>=0.8.2,<0.9.0
pyzmq>=13.1.0,<14.0.0
git+https://github.com/openstack/tempest.git
SQLAlchemy==0.8.1
requests==1.2.3

View File

@ -1,2 +0,0 @@
python-subunit
docutils==0.9.1

View File

@ -1,19 +0,0 @@
# content of: tox.ini , put in same dir as setup.py
[tox]
envlist = py27
[testenv]
# Set STATSD env variables so that statsd code paths are tested.
setenv = STATSD_HOST=localhost
STATSD_PORT=8125
VIRTUAL_ENV=/tmp/1/env
changedir=/tmp/1
commands=
client.py {posargs:--config}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt

View File

@ -1,90 +0,0 @@
#
# Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
#
# 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
from utils import make_dir, INSTANCE_FOLDER_PATH, PROJECT_ROOT
class BaseConfig(object):
"""base config object"""
PROJECT = "refstack"
# The app root path, also can use flask.root_path.
PROJECT_ROOT = PROJECT_ROOT
DEBUG = False
TESTING = False
ADMINS = ['youremail@yourdomain.com']
# http://flask.pocoo.org/docs/quickstart/#sessions
SECRET_KEY = 'secret key'
LOG_FOLDER = os.path.join(INSTANCE_FOLDER_PATH, 'logs')
make_dir(LOG_FOLDER)
# Fild upload, should override in production.
# Limited the maximum allowed payload to 16 megabytes.
# http://flask.pocoo.org/docs/patterns/fileuploads/#improving-uploads
MAX_CONTENT_LENGTH = 16 * 1024 * 1024
UPLOAD_FOLDER = os.path.join(INSTANCE_FOLDER_PATH, 'uploads')
make_dir(UPLOAD_FOLDER)
class DefaultConfig(BaseConfig):
"""default config thing"""
DEBUG = True
# Flask-Sqlalchemy: http://packages.python.org/Flask-SQLAlchemy/config.html
SQLALCHEMY_ECHO = True
# SQLITE for prototyping.
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + \
INSTANCE_FOLDER_PATH + '/db.sqlite'
# MYSQL for production.
#SQLALCHEMY_DATABASE_URI = \
# 'mysql://username:password@server/db?charset=utf8'
# Flask-babel: http://pythonhosted.org/Flask-Babel/
ACCEPT_LANGUAGES = ['zh']
BABEL_DEFAULT_LOCALE = 'en'
# Flask-cache: http://pythonhosted.org/Flask-Cache/
CACHE_TYPE = 'simple'
CACHE_DEFAULT_TIMEOUT = 60
# Flask-mail: http://pythonhosted.org/flask-mail/
MAIL_DEBUG = DEBUG
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USE_SSL = False
# Should put MAIL_USERNAME and MAIL_PASSWORD
# in production under instance folder.
MAIL_USERNAME = 'yourmail@gmail.com'
MAIL_PASSWORD = 'yourpass'
MAIL_DEFAULT_SENDER = MAIL_USERNAME
# Flask-openid: http://pythonhosted.org/Flask-OpenID/
OPENID_FS_STORE_PATH = os.path.join(INSTANCE_FOLDER_PATH, 'openid')
make_dir(OPENID_FS_STORE_PATH)
class TestConfig(BaseConfig):
TESTING = True
WTF_CSRF_ENABLED = False
SQLALCHEMY_ECHO = False
SQLALCHEMY_DATABASE_URI = 'sqlite://'

View File

@ -1,17 +0,0 @@
#-*- coding: utf-8 -*-
# This file based on MIT licensed code at: https://github.com/imwilsonxu/fbone
from functools import wraps
from flask import abort
from flask.ext.login import current_user
def admin_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_admin():
abort(403)
return f(*args, **kwargs)
return decorated_function

View File

@ -1,37 +0,0 @@
#
# Copyright (c) 2013 Piston Cloud Computing, Inc.
# All Rights Reserved.
#
# 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.
from flask.ext.admin import Admin
admin = Admin()
from flask.ext.restless import APIManager
api = APIManager()
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
from flask.ext.mail import Mail
mail = Mail()
# TODO(termie): not used yet
#from flask.ext.cache import Cache
#cache = Cache()
from flask.ext.login import LoginManager
login_manager = LoginManager()
from flask.ext.openid import OpenID
oid = OpenID()

View File

@ -1,11 +0,0 @@
{% extends "layout.html" %}
{% import 'admin/layout.html' as layout with context -%}
{% block head_css %}
<link href="{{ url_for('admin.static', filename='bootstrap/css/bootstrap.css') }}" rel="stylesheet">
<link href="{{ url_for('admin.static', filename='admin/css/admin.css') }}" rel="stylesheet">
{% endblock %}
{% block body %}{% endblock %}
{% block tail_js %}
<script src="{{ url_for('admin.static', filename='select2/select2.min.js') }}" type="text/javascript"></script>
{% endblock %}

View File

@ -1,22 +0,0 @@
{% extends "layout.html" %}
{% block title %}Create Profile{% endblock %}
{% block body %}
<h2>Create Profile</h2>
<p>
Hey! This is the first time you signed in on this website. In
order to proceed we need some extra information from you:
<form action="" method=post>
<dl>
<dt>Name:
<dd><input type=text name=name size=30 value="{{ request.values.name }}">
<dt>E-Mail:
<dd><input type=text name=email size=30 value="{{ request.values.email }}">
</dl>
<p>
<input type=submit value="Create profile">
<input type=hidden name=next value="{{ next }}">
</form>
<p>
If you don't want to proceed, you can <a href="{{ url_for('logout')
}}">sign out</a> again.
{% endblock %}

View File

@ -1,16 +0,0 @@
{% extends "layout.html" %}
{% block title %}Edit Profile{% endblock %}
{% block body %}
<h2>Edit Profile</h2>
<form action="" method=post>
<dl>
<dt>Name:
<dd><input type=text name=name size=30 value="{{ form.name }}">
<dt>E-Mail
<dd><input type=text name=email size=30 value="{{ form.email }}">
</dl>
<p>
<input type=submit value="Update profile">
<input type=submit name=delete value="Delete">
</form>
{% endblock %}

View File

@ -1 +0,0 @@
page not found

View File

@ -1,131 +0,0 @@
{% extends "layout.html" %}
{% block title %}Welcome{% endblock %}
{% block body %}
<div class="panel panel-default unit one-of-two">
<div class="panel-heading">
<span class="glyphicon glyphicon-cloud"></span> Clouds
<button type="button"
class="btn btn-default btn-xs"
onclick="location.href='/create-cloud'"
style="float: right;">
<span class="glyphicon glyphicon-plus-sign"></span> add cloud
</button>
</div>
<table class="table">
{% for cloud in clouds %}
<tr>
<td>
{{ cloud.label }}
</td>
<td align="right">
<div class="btn-group">
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Test Cloud"
data-placement="top"
onclick="window.location='/test-cloud/{{ cloud.id }}'">
<span class="glyphicon glyphicon-play"></span>
</button>
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Edit Cloud"
data-placement="top"
onclick="window.location='/edit-cloud/{{ cloud.id }}'">
<span class="glyphicon glyphicon-pencil"></span>
</button>
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Delete Cloud"
data-placement="top"
onclick="confirm_delete_cloud('{{ cloud.id }}','{{ cloud.label}}')">
<span class="glyphicon glyphicon-trash"></span>
</button>
</div>
</td>
</tr>
{% endfor %}
</table>
</div>
<div class="panel panel-default unit one-of-two">
<div class="panel-heading">
<span class="glyphicon glyphicon-list"></span> Tests
</div>
<table class="table">
{% for cloud in clouds %}
{% for test in cloud.tests %}
<tr>
<td>
{{ cloud.label }} (Test ID: {{ test.id }} )
</td>
<td align="right">
<div class="btn-group">
{% if test.finished %}
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Show Report"
data-placement="top"
onclick="window.location='/show-report/{{ test.id }}'">
<span class="glyphicon glyphicon-file"></span>
</button>
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Download Result"
data-placement="top"
onclick="window.location='/download-result/{{ test.id }}'">
<span class="glyphicon glyphicon-cloud-download"></span>
</button>
{% else %}
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Test In Progress..."
data-placement="top"
onclick="window.location='/show-status/{{ test.id }}'">
<span class="glyphicon glyphicon-refresh"></span>
</button>
{% endif %}
<button type="button"
class="btn btn-default btn-xs"
rel="tooltip"
title="Delete Test"
data-placement="top"
onclick="confirm_delete_test('{{ test.id }}','{{ cloud.label}}')">
<span class="glyphicon glyphicon-trash"></span>
</button>
</div>
</td>
</tr>
{% endfor %}
{% endfor %}
</table>
</div>
<script type="text/javascript">
$(function () {
$("[rel='tooltip']").tooltip({container:'body'});
}
);
function confirm_delete_cloud(cloud_id,label)
{
var r=confirm("Are you sure you want to delete " + label + "?");
if (r==true)
{
window.location='/delete-cloud/' + cloud_id;
}
}
function confirm_delete_test(test_id,label)
{
var r=confirm("Are you sure you want to delete test " + test_id + " of " + label + "?");
if (r==true)
{
window.location='/delete-test/' + test_id;
}
}
</script>
{% endblock %}

View File

@ -1,41 +0,0 @@
{% extends "layout.html" %}
{% block title %}Welcome{% endblock %}
{% block body %}
<div class="unit span-grid">
<h2>What is Refstack?</h2>
</div>
<div class="unit one-of-three index_left option">
<span class="glyphicon glyphicon-check"></span>
<p>An existence proof of the certified openstack APIs.</p>
</div>
<div class="unit one-of-three index_center option">
<span class="glyphicon glyphicon-check"></span>
<p>A reference OpenStack environment for tools developers.</p>
</div>
<div class="unit one-of-three index_right option">
<span class="glyphicon glyphicon-check"></span>
<p>A certification process for OpenStack service and product vendors.</p>
</div>
<div class="unit span-grid">
<br/>
Vendors that are registered with RefStack:<br/><br/>
<ul class="vendors">
{% for vendor in vendors %}
<li>{{ vendor.vendor_name }}</li>
{% endfor %}
</ul>
</div>
<div class="unit one-of-two">
<h3>Get involved!</h3>
We welcome collaboration. <Br>
To join the conversation jump into <a href="irc://irc.freenode.net/channel?refstack">#refstack</a> on freenode.
<br/><br/>
For a living blueprint see our <a href="https://etherpad.openstack.org/RefStackBlueprint">etherpad</a>!
</div>
<div class="unit one-of-two">
<h3>Refstack CLI</h3>
The refstack command line interface allows you to pre-test your cloud. <br>
<br/>
Documentation coming soon!
</div>
{% endblock %}

View File

@ -1,99 +0,0 @@
<!doctype html>
<html>
<head>
<title>{% block title %}Welcome{% endblock %} | RefStack</title>
<script src="{{ url_for('static', filename='jquery-1.10.1.min.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"></script>
<!-- refstack specific js-->
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" />
<link rel="stylesheet" type="text/css" href="/static/toast.css">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='refstack.css') }}">
<!-- compiled and minified bootstrap JavaScript -->
{% block head_css %}{% endblock %}
</head>
<body>
<div class="container">
<div class="header">
<a href="/" class="logo">
<span class="glyphicon glyphicon-check"></span>
Refstack
</a>
<ul class="ref_nav">
{% if g.user %}
{% if g.user.su %}
<li>
<span class="glyphicon glyphicon-th-large"></span>
<a href="{{ url_for('index') }}">Dashboard</a>
</li>
<li class="dropdown">
<span class="glyphicon glyphicon-cog"></span>
<a data-toggle="dropdown" href="#">
Admin
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li>
<a href="/admin/vendorview/">Vendors</a>
</li>
<li >
<a href="/admin/cloudview/">Clouds</a>
</li>
<li >
<a href="/admin/userview/">Users</a>
</li>
</ul>
</li>
<li>
<span class="glyphicon glyphicon-user"></span>
<a href="{{ url_for('view_profile') }}">{{ g.user.name }}</a>
</li>
<li>
<span class="glyphicon glyphicon-log-out"></span>
<a href="{{ url_for('logout') }}">sign out</a>
</li>
{% else %}
<li>
<span class="glyphicon glyphicon-th-large"></span>
<a href="{{ url_for('index') }}">Dashboard</a>
</li>
<li>
<span class="glyphicon glyphicon-user"></span>
<a href="{{ url_for('view_profile') }}">{{ g.user.name }}</a>
</li>
<li>
<span class="glyphicon glyphicon-log-out"></span>
<a href="{{ url_for('logout') }}">sign out</a>
</li>
{% endif %}
{% else %}
<li>
<span class="glyphicon glyphicon-home"></span>
<a href="{{ url_for('index') }}">Overview</a>
</li>
<li>
<span class="glyphicon glyphicon-log-in"></span>
<a href="{{ url_for('login') }}">sign in</a>
</li>
{% endif %}
</ul>
</div>
<div class="inner_container">
{% for message in get_flashed_messages() %}
<p class=message>{{ message }}</p>
{% endfor %}
<div class="">
{% block body %}{% endblock %}
</div>
</div>
</div>
{% block tail_js %}{% endblock %}
<script src="{{ url_for('static', filename='refstack.js') }}"></script>
</body>
</html>

View File

@ -1,13 +0,0 @@
{% extends "layout.html" %}
{% block title %}Sign in{% endblock %}
{% block body %}
<h2>Sign in</h2>
<form action="" method=post>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}</p>{% endif %}
<p>
OpenID:
<input type=text name=openid size=30>
<input type=submit value="Sign in">
<input type=hidden name=next value="{{ next }}">
</form>
{% endblock %}

View File

@ -1,142 +0,0 @@
<!doctype html>
<html>
<head>
<title>{% block title %}Welcome{% endblock %} | RefStack</title>
<script src="{{ url_for('static', filename='jquery-1.10.1.min.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"></script>
<!-- refstack specific js-->
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" />
<link rel="stylesheet" type="text/css" href="/static/toast.css">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='refstack.css') }}">
<!-- compiled and minified bootstrap JavaScript -->
{% block head_css %}{% endblock %}
</head>
<body>
<div class="container">
<div class="header">
<a href="/" class="logo">
<span class="glyphicon glyphicon-check"></span>
Refstack
</a>
</div>
<div class="inner_container">
<div class="panel panel-default unit one-of-two">
<div class="panel-heading">
<span class="glyphicon glyphicon-cloud"></span> Forced Results
</div>
<ul class="list-group">
<li class="heading list-group-item">compute</li>
<li class="list-group-item">aggregates<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">availability zone<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">fixed ips<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">flavors access<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">flavors extra specs<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">flavors<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">hosts<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">hypervisor<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">quotas<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">servers<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">services<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">simple tenant usage<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">flavors</li>
<li class="list-group-item">floating ips<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">floating ips actions<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">list floating ips<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="heading list-group-item">images</li>
<li class="list-group-item">image metadata<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">images oneserver<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">images<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">list image filters<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">list images<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">keypairs</li>
<li class="list-group-item">keypairs<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">limits</li>
<li class="list-group-item">absolute limits<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="heading list-group-item">security groups</li>
<li class="list-group-item">security group rules<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">security groups<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">servers</li>
<li class="list-group-item">attach interfaces<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">create server<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">disk config<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">instance actions<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">list server filters<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">list servers negative<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">multiple create<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">server actions<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">server addresses<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">server metadata<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">server personality<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">server rescue<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">servers negative<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">servers<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">virtual interfaces<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">authorization<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">auth token<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">extensions<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">live block migration<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">quotas<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">volumes</li>
<li class="list-group-item">attach volume<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes get<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes list<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes negative<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="heading list-group-item">identity</li>
<li class="list-group-item">admin<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">roles<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">services<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">tenants<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">users<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">image</li>
<li class="sub-heading list-group-item">v1</li>
<li class="list-group-item">image members<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">images<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="sub-heading list-group-item">v2</li>
<li class="list-group-item">images<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">network</li>
<li class="list-group-item">floating ips<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">load balancer<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">networks<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">quotas<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">routers<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">security groups<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">object storage</li>
<li class="list-group-item">account quotas<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">account services<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">container acl<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">container services<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">container staticweb<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">container sync<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">object expiry<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">object services<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">object version<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">orchestration</li>
<li class="list-group-item">neutron resources<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">non empty stack<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">server cfn init<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">stacks<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="list-group-item">templates<span class="glyphicon glyphicon-ok-circle"></span></li>
<li class="heading list-group-item">volume</li>
<li class="list-group-item">multi backend<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volume types extra specs negative<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volume types extra specs<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volume types negative<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volume types<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes actions<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes get<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes list<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes negative<span class="glyphicon glyphicon-remove-circle"></span></li>
<li class="list-group-item">volumes snapshots<span class="glyphicon glyphicon-remove-circle"></span></li>
</ul>
{% block body %}{% endblock %}
</div>
</div>
</div>
</body>
</html>

View File

@ -1,45 +0,0 @@
{% extends "layout.html" %}
{% block title %}View Profile{% endblock %}
{% block body %}
<div class="panel panel-default unit one-of-two">
<div class="panel-heading">
<span class="glyphicon glyphicon-user"></span> {{ user.name }}'s profile
<a href="/profile/edit" style="float: right;">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</div>
<table class="table">
<tr>
<td>
E-Mail:
</td>
<td >
{{ user.email }}
{% if user.email_verified %}
<span class="glyphicon glyphicon-ok-circle green" ></span>
{% else %}
<span class="glyphicon glyphicon-remove-circle red"></span>
{% endif %}
</td>
</tr>
<tr>
<td>
Vendor:
</td>
<td >
{{ user.vendor.vendor_name }}
{% if user.authorized %}
<span class="glyphicon glyphicon-ok-circle green" ></span>
{% else %}
<span class="glyphicon glyphicon-remove-circle red"></span>
{% endif %}
</td>
</tr>
</table>
</div>
{% endblock %}

View File

@ -13,15 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
#
from refstack import app as base_app
import unittest
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.app = base_app.create_app().test_client()
def test_nothing(self):
# make sure the shuffled sequence does not lose any elements
pass

View File

@ -19,7 +19,6 @@
from datetime import datetime
import os
import pprint
import random
import string
@ -120,8 +119,3 @@ def make_dir(dir_path):
### Begin Non-Fbone stuff
def dump_config(app):
"""Useful to dump app config for debug purposes."""
return pprint.pformat(dict(app.config.items()))

View File

@ -1,184 +0,0 @@
#
# Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
#
# 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 flask
from flask import abort, flash, request, redirect, url_for, \
render_template, g, session
from flask_mail import Mail
from refstack import app as base_app
from refstack.extensions import db
from refstack.extensions import oid
from refstack.models import Cloud
from refstack.models import Test
from refstack.models import User
from refstack.models import Vendor
app = base_app.create_app()
mail = Mail(app)
public_routes = ['/post-result', '/get-miniconf']
@app.before_request
def before_request():
"""Runs before the request itself."""
if request.path not in public_routes:
g.user = None
if 'openid' in session:
flask.g.user = User.query.\
filter_by(openid=session['openid']).first()
@app.route('/', methods=['POST', 'GET'])
def index():
"""Index view."""
if g.user is not None:
# something else
clouds = Cloud.query.filter_by(user_id=g.user.id).all()
return render_template('home.html', clouds=clouds)
else:
vendors = Vendor.query.all()
return render_template('index.html', vendors=vendors)
@app.route('/login', methods=['GET', 'POST'])
@oid.loginhandler
def login():
"""Does the login via OpenID.
Has to call into `oid.try_login` to start the OpenID machinery.
"""
# if we are already logged in, go back to were we came from
if g.user is not None:
return redirect(oid.get_next_url())
return oid.try_login(
"https://login.launchpad.net/",
ask_for=['email', 'nickname'])
@oid.after_login
def create_or_login(resp):
"""This is called when login with OpenID succeeded and it's not
necessary to figure out if this is the users's first login or not.
This function has to redirect otherwise the user will be presented
with a terrible URL which we certainly don't want.
"""
session['openid'] = resp.identity_url
user = User.query.filter_by(openid=resp.identity_url).first()
if user is not None:
flash(u'Successfully signed in')
g.user = user
return redirect(oid.get_next_url())
return redirect(url_for('create_profile', next=oid.get_next_url(),
name=resp.fullname or resp.nickname,
email=resp.email))
@app.route('/create-profile', methods=['GET', 'POST'])
def create_profile():
"""If this is the user's first login, the create_or_login function
will redirect here so that the user can set up his profile.
"""
if g.user is not None or 'openid' not in session:
return redirect(url_for('index'))
if request.method == 'POST':
name = request.form['name']
email = request.form['email']
if not name:
flash(u'Error: you have to provide a name')
elif '@' not in email:
flash(u'Error: you have to enter a valid email address')
else:
flash(u'Profile successfully created')
db.session.add(User(name, email, session['openid']))
db.session.commit()
return redirect(oid.get_next_url())
return render_template(
'create_profile.html', next_url=oid.get_next_url())
@app.route('/profile/edit', methods=['GET', 'POST'])
def edit_profile():
"""Updates a profile."""
if g.user is None:
abort(401)
form = dict(name=g.user.name, email=g.user.email)
if request.method == 'POST':
if 'delete' in request.form:
db.session.delete(g.user)
db.session.commit()
session['openid'] = None
flash(u'Profile deleted')
return redirect(url_for('index'))
form['name'] = request.form['name']
form['email'] = request.form['email']
if not form['name']:
flash(u'Error: you have to provide a name')
elif '@' not in form['email']:
flash(u'Error: you have to enter a valid email address')
else:
flash(u'Profile successfully created')
g.user.name = form['name']
g.user.email = form['email']
db.session.commit()
return redirect(url_for('edit_profile'))
return render_template('edit_profile.html', form=form)
@app.route('/profile', methods=['GET', 'POST'])
def view_profile():
"""Updates a profile."""
if g.user is None:
abort(401)
return render_template('view_profile.html', user=g.user)
@app.route('/logout')
def logout():
"""Log out."""
session.pop('openid', None)
flash(u'You have been signed out')
return redirect(oid.get_next_url())
@app.route('/post-result', methods=['POST'])
def post_result():
"""Receive tempest test result from a remote test runner."""
# todo: come up with some form of authentication
# Im sure we can come with something more elegant than this
# but it should work for now.
#print request.files
f = request.files['file']
if not f:
return 'only valid with file post', 400
else:
if request.args.get('test_id', ''):
# this data is for a specific test triggered by the gui and we
# want to relate it
new_test = Test.query.\
filter_by(id=request.args.get('test_id', '')).first()
new_test.subunit = f.read()
new_test.finished = True
else:
# anonymous data .. we still want to capture it
new_test = Test()
new_test.subunit = f.read()
new_test.finished = True
db.session.add(new_test)
db.session.commit()
return 'thank you', 201

View File

@ -1,21 +1,6 @@
Flask==0.10.1
Flask-Admin==1.0.7
Flask-Login==0.2.11
Flask-Mail==0.9.0
Flask-OpenID==1.2.1
Flask-Principal==0.3.5
Flask-SQLAlchemy==1.0
Flask-Security==1.6.3
Flask-WTF==0.8.3
Flask-Restless==0.13.1
SQLAlchemy==0.8.3
WTForms==1.0.4
Werkzeug==0.9.6
alembic==0.5.0
gunicorn==0.17.4
pyOpenSSL==0.13
pycrypto==2.6
python-openid==2.2.5
requests==1.2.3
python-keystoneclient
Jinja2==2.7.1