pep8 cleanup

This commit is contained in:
David Lenwell 2014-02-21 16:08:12 -08:00
parent 14b0810030
commit 0bd1399735
15 changed files with 221 additions and 194 deletions

4
alembic/versions/1d6540fc6279_added_vendor_contact.py Normal file → Executable file
View File

@ -16,7 +16,9 @@ import sqlalchemy as sa
def upgrade(): def upgrade():
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.add_column('vendor', sa.Column('contact_name', sa.String(length=120), nullable=True)) op.add_column(
'vendor',
sa.Column('contact_name', sa.String(length=120), nullable=True))
### end Alembic commands ### ### end Alembic commands ###

110
alembic/versions/3790aed42558_from_scratch.py Normal file → Executable file
View File

@ -16,63 +16,69 @@ import sqlalchemy as sa
def upgrade(): def upgrade():
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.create_table('cloud', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), 'cloud',
sa.Column('label', sa.String(length=60), nullable=True), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('endpoint', sa.String(length=120), nullable=True), sa.Column('label', sa.String(length=60), nullable=True),
sa.Column('test_user', sa.String(length=80), nullable=True), sa.Column('endpoint', sa.String(length=120), nullable=True),
sa.Column('test_key', sa.String(length=80), nullable=True), sa.Column('test_user', sa.String(length=80), nullable=True),
sa.Column('admin_endpoint', sa.String(length=120), nullable=True), sa.Column('test_key', sa.String(length=80), nullable=True),
sa.Column('admin_user', sa.String(length=80), nullable=True), sa.Column('admin_endpoint', sa.String(length=120), nullable=True),
sa.Column('admin_key', sa.String(length=80), nullable=True), sa.Column('admin_user', sa.String(length=80), nullable=True),
sa.PrimaryKeyConstraint('id'), sa.Column('admin_key', sa.String(length=80), nullable=True),
sa.UniqueConstraint('endpoint') sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('endpoint')
) )
op.create_table('vendor', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), 'vendor',
sa.Column('vendor_name', sa.String(length=80), nullable=True), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('contact_email', sa.String(length=120), nullable=True), sa.Column('vendor_name', sa.String(length=80), nullable=True),
sa.PrimaryKeyConstraint('id'), sa.Column('contact_email', sa.String(length=120), nullable=True),
sa.UniqueConstraint('contact_email'), sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('vendor_name') sa.UniqueConstraint('contact_email'),
sa.UniqueConstraint('vendor_name')
) )
op.create_table('user', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), 'user',
sa.Column('vendor_id', sa.Integer(), nullable=True), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=60), nullable=True), sa.Column('vendor_id', sa.Integer(), nullable=True),
sa.Column('email', sa.String(length=200), nullable=True), sa.Column('name', sa.String(length=60), nullable=True),
sa.Column('email_verified', sa.Boolean(), nullable=True), sa.Column('email', sa.String(length=200), nullable=True),
sa.Column('openid', sa.String(length=200), nullable=True), sa.Column('email_verified', sa.Boolean(), nullable=True),
sa.Column('authorized', sa.Boolean(), nullable=True), sa.Column('openid', sa.String(length=200), nullable=True),
sa.Column('su', sa.Boolean(), nullable=True), sa.Column('authorized', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['vendor_id'], ['vendor.id'], ), sa.Column('su', sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint('id'), sa.ForeignKeyConstraint(['vendor_id'], ['vendor.id'], ),
sa.UniqueConstraint('email'), sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('openid') sa.UniqueConstraint('email'),
sa.UniqueConstraint('openid')
) )
op.create_table('test', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), 'test',
sa.Column('cloud_id', sa.Integer(), nullable=True), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('config', sa.String(length=4096), nullable=True), sa.Column('cloud_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['cloud_id'], ['cloud.id'], ), sa.Column('config', sa.String(length=4096), nullable=True),
sa.PrimaryKeyConstraint('id') sa.ForeignKeyConstraint(['cloud_id'], ['cloud.id'], ),
sa.PrimaryKeyConstraint('id')
) )
op.create_table('test_results', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), 'test_results',
sa.Column('test_id', sa.Integer(), nullable=True), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=True), sa.Column('test_id', sa.Integer(), nullable=True),
sa.Column('blob', sa.Binary(), nullable=True), sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['test_id'], ['test.id'], ), sa.Column('blob', sa.Binary(), nullable=True),
sa.PrimaryKeyConstraint('id') sa.ForeignKeyConstraint(['test_id'], ['test.id'], ),
sa.PrimaryKeyConstraint('id')
) )
op.create_table('test_status', op.create_table(
sa.Column('id', sa.Integer(), nullable=False), 'test_status',
sa.Column('test_id', sa.Integer(), nullable=True), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('message', sa.String(length=1024), nullable=True), sa.Column('test_id', sa.Integer(), nullable=True),
sa.Column('finished', sa.Boolean(), nullable=True), sa.Column('message', sa.String(length=1024), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True), sa.Column('finished', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['test_id'], ['test.id'], ), sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id') sa.ForeignKeyConstraint(['test_id'], ['test.id'], ),
sa.PrimaryKeyConstraint('id')
) )
### end Alembic commands ### ### end Alembic commands ###

3
alembic/versions/4288db006e5_added_user_cloud_rel.py Normal file → Executable file
View File

@ -10,9 +10,6 @@ Create Date: 2013-10-31 21:00:27.473833
revision = '4288db006e5' revision = '4288db006e5'
down_revision = '1d6540fc6279' down_revision = '1d6540fc6279'
from alembic import op
import sqlalchemy as sa
def upgrade(): def upgrade():
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

5
alembic/versions/449461dbc725_add_apikey.py Normal file → Executable file
View File

@ -15,7 +15,8 @@ import sqlalchemy as sa
def upgrade(): def upgrade():
op.create_table('apikey', op.create_table(
'apikey',
sa.Column('id', sa.Integer(), nullable=False), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=60), nullable=True), sa.Column('name', sa.String(length=60), nullable=True),
sa.Column('key', sa.String(length=200), nullable=True), sa.Column('key', sa.String(length=200), nullable=True),
@ -24,7 +25,7 @@ def upgrade():
sa.Column('timestamp', sa.DateTime(), nullable=True), sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'), sa.PrimaryKeyConstraint('id'),
) )
def downgrade(): def downgrade():

5
alembic/versions/59e15d864941_added_subunit_output.py Normal file → Executable file
View File

@ -16,7 +16,10 @@ import sqlalchemy as sa
def upgrade(): def upgrade():
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.add_column('test_results', sa.Column('subunit', sa.String(length=8192), nullable=True)) op.add_column(
'test_results',
sa.Column('subunit', sa.String(length=8192), nullable=True)
)
### end Alembic commands ### ### end Alembic commands ###

17
refstack/api.py Normal file → Executable file
View File

@ -1,3 +1,18 @@
#
# 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. """Basic API code.
This is using Flask-Restless at the moment because it is super simple, This is using Flask-Restless at the moment because it is super simple,
@ -47,8 +62,6 @@ def _not_authorized():
status_code=401) status_code=401)
def authenticate(): def authenticate():
# If we're already authenticated, we can ignore this # If we're already authenticated, we can ignore this
if flask.g.user: if flask.g.user:

38
refstack/app.py Normal file → Executable file
View File

@ -1,19 +1,21 @@
# -*- coding: utf-8 -*- #
# Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
# This file based on MIT licensed code at: https://github.com/imwilsonxu/fbone #
# 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 os
from flask import Flask, render_template
from flask import Flask, request, render_template
#from flask.ext.babel import Babel
from .config import DefaultConfig from .config import DefaultConfig
#from .user import User, user
#from .settings import settings
#from .frontend import frontend
#from .api import api
#from .admin import admin
#from .extensions import db, mail, cache, login_manager, oid
from refstack import admin from refstack import admin
from refstack import api from refstack import api
from .extensions import db from .extensions import db
@ -55,8 +57,6 @@ def create_app(config=None, app_name=None, blueprints=None):
instance_path=INSTANCE_FOLDER_PATH, instance_path=INSTANCE_FOLDER_PATH,
instance_relative_config=True) instance_relative_config=True)
configure_app(app, config) configure_app(app, config)
configure_hook(app) configure_hook(app)
configure_blueprints(app, blueprints) configure_blueprints(app, blueprints)
@ -85,9 +85,6 @@ def configure_app(app, config=None):
if config: if config:
app.config.from_object(config) app.config.from_object(config)
# Use instance folder instead of env variables to make deployment easier.
#app.config.from_envvar('%s_APP_CONFIG' % DefaultConfig.PROJECT.upper(), silent=True)
def configure_extensions(app): def configure_extensions(app):
# flask-sqlalchemy # flask-sqlalchemy
@ -161,7 +158,8 @@ def configure_logging(app):
app.logger.setLevel(logging.INFO) app.logger.setLevel(logging.INFO)
info_log = os.path.join(app.config['LOG_FOLDER'], 'info.log') 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 = logging.handlers.RotatingFileHandler(
info_log, maxBytes=100000, backupCount=10)
info_file_handler.setLevel(logging.INFO) info_file_handler.setLevel(logging.INFO)
info_file_handler.setFormatter(logging.Formatter( info_file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s ' '%(asctime)s %(levelname)s: %(message)s '

View File

@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from keystoneclient.v2_0 import client from keystoneclient.v2_0 import client
from refstack.models import * from refstack.models import db, Cloud
class TempestConfig(object): class TempestConfig(object):
@ -24,14 +24,13 @@ class TempestConfig(object):
def output(self): def output(self):
"""outputs config in propper format""" """outputs config in propper format"""
output = '' output = ''
for k,v in self.config.items(): for k, v in self.config.items():
output += '[%s] \n' % k output += '[%s] \n' % k
for sk,sv in v.items(): for sk, sv in v.items():
output += '%s = %s \n' % (sk,sv) output += '%s = %s \n' % (sk, sv)
output += '\n' output += '\n'
return output return output
def build_config_from_keystone(self): def build_config_from_keystone(self):
"""uses the keystoneclient libs to query a clouds endpoint and """uses the keystoneclient libs to query a clouds endpoint and
retrive a service catelog. that it then uses to populate the retrive a service catelog. that it then uses to populate the
@ -65,9 +64,11 @@ class TempestConfig(object):
self._keystone.management_url = self._cloud.admin_endpoint self._keystone.management_url = self._cloud.admin_endpoint
# make sure this keystone server can list services using has_service_catalog # make sure this keystone server can list services
# using has_service_catalog
if not self._keystone.has_service_catalog(): if not self._keystone.has_service_catalog():
# we have no service catelog all tests are fail because we can't build a config # we have no service catelog all tests are fail
# because we can't build a config
print "fail " print "fail "
#else: #else:
# print "has service catalog" # print "has service catalog"
@ -76,13 +77,12 @@ class TempestConfig(object):
# make a local dict of the service catalog # make a local dict of the service catalog
for item in self._keystone.service_catalog.catalog['serviceCatalog']: for item in self._keystone.service_catalog.catalog['serviceCatalog']:
self.service_catalog[item['name']]=item['endpoints'][0]['publicURL'] self.service_catalog[item['name']] = \
item['endpoints'][0]['publicURL']
#print "%s : %s" % (item['name'],item['endpoints'][0]['publicURL'])
# setup output service_available # setup output service_available
for service in self.config['service_available'].keys(): for service in self.config['service_available'].keys():
if self.service_catalog.has_key(service): if service in self.service_catalog:
self.config['service_available'][service] = True self.config['service_available'][service] = True
# boto settings # boto settings
@ -92,17 +92,16 @@ class TempestConfig(object):
# return the actual config # return the actual config
return self.output() return self.output()
def __init__(self, cloud_id): def __init__(self, cloud_id):
""" sets up the default configs""" """ sets up the default configs"""
self.cloud_id = cloud_id self.cloud_id = cloud_id
self.config['DEFAULT'] = { self.config['DEFAULT'] = {
'debug':True, 'debug': True,
'use_stderr':False, 'use_stderr': False,
'log_file':'output', 'log_file': 'output',
'lock_path': '/tmp/'+str(cloud_id)+'/', 'lock_path': '/tmp/'+str(cloud_id)+'/',
'default_log_levels':"""tempest.stress=INFO,amqplib=WARN, 'default_log_levels': """tempest.stress=INFO,amqplib=WARN,
sqlalchemy=WARN,boto=WARN,suds=INFO,keystone=INFO, sqlalchemy=WARN,boto=WARN,suds=INFO,keystone=INFO,
eventlet.wsgi.server=WARN"""} eventlet.wsgi.server=WARN"""}
@ -123,7 +122,6 @@ class TempestConfig(object):
'admin_tenant_name': '', 'admin_tenant_name': '',
'admin_role': ''} 'admin_role': ''}
self.config['compute'] = { self.config['compute'] = {
'catalog_type': 'compute', 'catalog_type': 'compute',
'allow_tenant_isolation': True, 'allow_tenant_isolation': True,
@ -166,7 +164,7 @@ class TempestConfig(object):
self.config['image'] = { self.config['image'] = {
'catalog_type': 'image', 'catalog_type': 'image',
'api_version': 1, 'api_version': 1,
'http_image': 'ttp://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz'} 'http_image': ''}
self.config['network'] = { self.config['network'] = {
'catalog_type': 'network', 'catalog_type': 'network',
@ -175,7 +173,7 @@ class TempestConfig(object):
'tenant_network_mask_bits': 28, 'tenant_network_mask_bits': 28,
'tenant_networks_reachable': False, 'tenant_networks_reachable': False,
'public_network_id': '', 'public_network_id': '',
'public_router_id': '' } 'public_router_id': ''}
self.config['volume'] = { self.config['volume'] = {
'catalog_type': 'volume', 'catalog_type': 'volume',
@ -186,14 +184,14 @@ class TempestConfig(object):
'backend1_name': 'BACKEND_1', 'backend1_name': 'BACKEND_1',
'backend2_name': 'BACKEND_2', 'backend2_name': 'BACKEND_2',
'storage_protocol': 'iSCSI', 'storage_protocol': 'iSCSI',
'vendor_name': 'Open Source' } 'vendor_name': 'Open Source'}
self.config['object-storage'] = { self.config['object-storage'] = {
'catalog_type': 'object-store', 'catalog_type': 'object-store',
'container_sync_timeout': 120, 'container_sync_timeout': 120,
'container_sync_interval': 5, 'container_sync_interval': 5,
'accounts_quotas_available': True, 'accounts_quotas_available': True,
'operator_role': 'Member' } 'operator_role': 'Member'}
self.config['boto'] = { self.config['boto'] = {
'ssh_user': 'cirros', 'ssh_user': 'cirros',
@ -209,7 +207,7 @@ class TempestConfig(object):
'http_socket_timeout': 30, 'http_socket_timeout': 30,
'num_retries': 1, 'num_retries': 1,
'build_timeout': 400, 'build_timeout': 400,
'build_interval': 1 } 'build_interval': 1}
self.config['orchestration'] = { self.config['orchestration'] = {
'catalog_type': 'orchestration', 'catalog_type': 'orchestration',
@ -229,12 +227,12 @@ class TempestConfig(object):
'ari_img_file': 'cirros-0.3.1-x86_64-initrd', 'ari_img_file': 'cirros-0.3.1-x86_64-initrd',
'aki_img_file': 'cirros-0.3.1-x86_64-vmlinuz', 'aki_img_file': 'cirros-0.3.1-x86_64-vmlinuz',
'ssh_user': 'cirros', 'ssh_user': 'cirros',
'large_ops_number': 0 } 'large_ops_number': 0}
self.config['cli'] = { self.config['cli'] = {
'enabled': True, 'enabled': True,
'cli_dir': '/usr/local/bin', 'cli_dir': '/usr/local/bin',
'timeout': 15 } 'timeout': 15}
self.config['service_available'] = { self.config['service_available'] = {
'cinder': False, 'cinder': False,
@ -243,9 +241,9 @@ class TempestConfig(object):
'swift': False, 'swift': False,
'nova': False, 'nova': False,
'heat': False, 'heat': False,
'horizon': False } 'horizon': False}
self.config['stress'] = { self.config['stress'] = {
'max_instances': 32, 'max_instances': 32,
'log_check_interval': 60, 'log_check_interval': 60,
'default_thread_number_per_action': 4 } 'default_thread_number_per_action': 4}

View File

@ -18,7 +18,7 @@ from subprocess import call
from refstack.common.tempest_config import TempestConfig from refstack.common.tempest_config import TempestConfig
import testrepository.repository.file import testrepository.repository.file
from testrepository import ui from testrepository import ui
#from testrepository.commands import run from testrepository.commands import run
from testrepository.commands import init from testrepository.commands import init
import gear import gear
@ -26,17 +26,15 @@ import gear
class TesterWorker(object): class TesterWorker(object):
"""gearman worker code""" """gearman worker code"""
def __init__(self,app): def __init__(self, app):
self.worker = gear.Worker('run_remote_test') self.worker = gear.Worker('run_remote_test')
self.worker.addServer(app.gearman_server) self.worker.addServer(app.gearman_server)
self.worker.registerFunction('run_remote_test') self.worker.registerFunction('run_remote_test')
def run_remote_test(self): def run_remote_test(self):
pass pass
def run_job(self): def run_job(self):
while True: while True:
job = self.worker.getJob() job = self.worker.getJob()
@ -68,13 +66,11 @@ class TestRepositorySource(object):
self.init_repo() self.init_repo()
def get_subunit_stream(self): def get_subunit_stream(self):
try: try:
return self.testrepository_last_stream() return self.testrepository_last_stream()
except KeyError: except KeyError:
raise NoStreamPresent() pass # raise NoStreamPresent()
def init_repo(self): def init_repo(self):
"""inits a new testrepository repo in the supplied path""" """inits a new testrepository repo in the supplied path"""
@ -86,16 +82,14 @@ class TestRepositorySource(object):
# if this happens its fine .. just means the repo is already there # if this happens its fine .. just means the repo is already there
pass pass
def run(self): def run(self):
here = os.getcwd() os.getcwd()
os.chdir(self.testr_directory) os.chdir(self.testr_directory)
self._ui.c = self.testr_directory+'tempest.conf' self._ui.c = self.testr_directory+'tempest.conf'
cmd = run.run(self._ui) cmd = run.run(self._ui)
res = cmd.execute() return cmd.execute()
def testrepository_last_stream(self): def testrepository_last_stream(self):
factory = testrepository.repository.file.RepositoryFactory() factory = testrepository.repository.file.RepositoryFactory()
@ -113,8 +107,7 @@ class Tester(object):
cloud_id = None cloud_id = None
_status = None _status = None
def __init__(self, cloud_id=None, test_id=None, sha=None):
def __init__(self,cloud_id=None,test_id=None,sha=None):
""" init method loads specified id or fails""" """ init method loads specified id or fails"""
if not test_id: if not test_id:
#create a new test id #create a new test id
@ -128,7 +121,6 @@ class Tester(object):
self.sha = sha self.sha = sha
self.test_path = "/tmp/%s/" % cloud_id self.test_path = "/tmp/%s/" % cloud_id
def run_remote(self): def run_remote(self):
"""triggers remote run""" """triggers remote run"""
# install tempest in virt env # install tempest in virt env
@ -144,27 +136,28 @@ class Tester(object):
# write the tempest config to that folder # write the tempest config to that folder
self.write_config(self.test_path) self.write_config(self.test_path)
# setup the repo wrapper.. this creates the repo if its not already there # setup the repo wrapper..
# this creates the repo if its not already there
tr = TestRepositorySource(self.test_path) tr = TestRepositorySource(self.test_path)
"""TODO: So this is supposed to use the testr wrapper to trigger a run.. however.. """TODO: So this is supposed to use the testr wrapper to trigger a run.
I am completly blocked on how to make it work the right way.. so I am moving on however.. I am completly blocked on how to make it work the right way..
for now once the congigs are setup and repo initiated it will call a subprocess so I am moving on for now once the congigs are setup and repo initiated
run the command .. THEN query the repo for the last set of results and store the it will call a subprocess run the command .. THEN query the repo for
subunit stream. the last set of results and store the subunit stream.
# run tests # run tests
#tr.run() #tr.run()
""" """
print "starting test" print "starting test"
call([self.test_path+'runtests.sh']) # replace this call([self.test_path+'runtests.sh']) # replace this
print "finished with tests" print "finished with tests"
# get back the results # get back the results
result = tr.testrepository_last_stream() result = tr.testrepository_last_stream()
# write results to database maybe .. or return them .. not sure which .. # write results to database maybe ..
return result.read() return result.read()
#return None #return None
@ -185,7 +178,8 @@ exit $?""" % path
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
${PYTHON:-python} -m subunit.run discover tempest.api $LISTOPT $IDOPTION ${PYTHON:-python} -m subunit.run discover \
tempest.api $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE test_id_option=--load-list $IDFILE
test_list_option=--list test_list_option=--list
group_regex=([^\.]*\.)*""" group_regex=([^\.]*\.)*"""
@ -209,14 +203,16 @@ group_regex=([^\.]*\.)*"""
"""The status property.""" """The status property."""
def fget(self): def fget(self):
return self._status return self._status
def fset(self, value): def fset(self, value):
self._status = value self._status = value
def fdel(self): def fdel(self):
del self._status del self._status
return locals() return locals()
@property @property
def config(self): def config(self):
"""The config property. outputs a tempest config based on settings""" """The config property. outputs a tempest config based on settings"""
return self.tempest_config.output() return self.tempest_config.output()

33
refstack/config.py Normal file → Executable file
View File

@ -1,14 +1,25 @@
# -*- coding: utf-8 -*- #
# Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
# This file based on MIT licensed code at: https://github.com/imwilsonxu/fbone #
# 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 os
from utils import make_dir, INSTANCE_FOLDER_PATH, PROJECT_ROOT from utils import make_dir, INSTANCE_FOLDER_PATH, PROJECT_ROOT
class BaseConfig(object): class BaseConfig(object):
"""base config object"""
PROJECT = "refstack" PROJECT = "refstack"
# The app root path, also can use flask.root_path. # The app root path, also can use flask.root_path.
@ -34,15 +45,17 @@ class BaseConfig(object):
class DefaultConfig(BaseConfig): class DefaultConfig(BaseConfig):
"""default config thing"""
DEBUG = True DEBUG = True
# Flask-Sqlalchemy: http://packages.python.org/Flask-SQLAlchemy/config.html # Flask-Sqlalchemy: http://packages.python.org/Flask-SQLAlchemy/config.html
SQLALCHEMY_ECHO = True SQLALCHEMY_ECHO = True
# SQLITE for prototyping. # SQLITE for prototyping.
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + INSTANCE_FOLDER_PATH + '/db.sqlite' SQLALCHEMY_DATABASE_URI = 'sqlite:///' + \
INSTANCE_FOLDER_PATH + '/db.sqlite'
# MYSQL for production. # MYSQL for production.
#SQLALCHEMY_DATABASE_URI = 'mysql://username:password@server/db?charset=utf8' #SQLALCHEMY_DATABASE_URI = \
# 'mysql://username:password@server/db?charset=utf8'
# Flask-babel: http://pythonhosted.org/Flask-Babel/ # Flask-babel: http://pythonhosted.org/Flask-Babel/
ACCEPT_LANGUAGES = ['zh'] ACCEPT_LANGUAGES = ['zh']
@ -53,13 +66,13 @@ class DefaultConfig(BaseConfig):
CACHE_DEFAULT_TIMEOUT = 60 CACHE_DEFAULT_TIMEOUT = 60
# Flask-mail: http://pythonhosted.org/flask-mail/ # Flask-mail: http://pythonhosted.org/flask-mail/
# https://bitbucket.org/danjac/flask-mail/issue/3/problem-with-gmails-smtp-server
MAIL_DEBUG = DEBUG MAIL_DEBUG = DEBUG
MAIL_SERVER = 'smtp.gmail.com' MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 587 MAIL_PORT = 587
MAIL_USE_TLS = True MAIL_USE_TLS = True
MAIL_USE_SSL = False MAIL_USE_SSL = False
# Should put MAIL_USERNAME and MAIL_PASSWORD in production under instance folder. # Should put MAIL_USERNAME and MAIL_PASSWORD
# in production under instance folder.
MAIL_USERNAME = 'yourmail@gmail.com' MAIL_USERNAME = 'yourmail@gmail.com'
MAIL_PASSWORD = 'yourpass' MAIL_PASSWORD = 'yourpass'
MAIL_DEFAULT_SENDER = MAIL_USERNAME MAIL_DEFAULT_SENDER = MAIL_USERNAME

2
refstack/default_settings.py Normal file → Executable file
View File

@ -32,7 +32,7 @@ class Default(object):
MAILGUN_DOMAIN = 'refstack.org' MAILGUN_DOMAIN = 'refstack.org'
SECRET_KEY = '#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@' SECRET_KEY = '#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@'
SQLALCHEMY_DATABASE_URI = os.environ.get( SQLALCHEMY_DATABASE_URI = os.environ.get(
'DATABASE_URL', 'sqlite:///%s/refstack.db' % (db_path)) 'DATABASE_URL', 'sqlite:///%s/refstack.db' % (db_path))
DEBUG = True DEBUG = True
SECURITY_PASSWORD_HASH = 'sha512_crypt' SECURITY_PASSWORD_HASH = 'sha512_crypt'
SECURITY_PASSWORD_SALT = SECRET_KEY SECURITY_PASSWORD_SALT = SECRET_KEY

View File

@ -55,12 +55,11 @@ class ApiKey(db.Model):
backref=db.backref('apikeys', lazy='dynamic')) backref=db.backref('apikeys', lazy='dynamic'))
"""
Note: The vendor list will be pre-populated from the sponsoring company database.
TODO: better define the vendor object and its relationship with user
it needs the ability to facilitate a login.
"""
class Vendor(db.Model): class Vendor(db.Model):
"""Note: The vendor list will be pre-populated from the
sponsoring company database.
TODO: better define the vendor object and its relationship with user
it needs the ability to facilitate a login."""
__tablename__ = 'vendor' __tablename__ = 'vendor'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
vendor_name = db.Column(db.String(80), unique=True) vendor_name = db.Column(db.String(80), unique=True)
@ -87,7 +86,7 @@ class Cloud(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship('User', user = db.relationship('User',
backref=db.backref('clouds',lazy='dynamic')) backref=db.backref('clouds', lazy='dynamic'))
class Test(db.Model): class Test(db.Model):
@ -95,10 +94,9 @@ class Test(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
cloud_id = db.Column(db.Integer, db.ForeignKey('cloud.id')) cloud_id = db.Column(db.Integer, db.ForeignKey('cloud.id'))
cloud = db.relationship('Cloud', cloud = db.relationship('Cloud',
backref=db.backref('tests',lazy='dynamic')) backref=db.backref('tests', lazy='dynamic'))
config = db.Column(db.String(4096)) config = db.Column(db.String(4096))
def __init__(self, cloud_id): def __init__(self, cloud_id):
self.cloud_id = cloud_id self.cloud_id = cloud_id
@ -108,13 +106,12 @@ class TestStatus(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
test_id = db.Column(db.Integer, db.ForeignKey('test.id')) test_id = db.Column(db.Integer, db.ForeignKey('test.id'))
test = db.relationship('Test', test = db.relationship('Test',
backref=db.backref('status',lazy='dynamic')) backref=db.backref('status', lazy='dynamic'))
message = db.Column(db.String(1024)) message = db.Column(db.String(1024))
finished = db.Column(db.Boolean, default=False) finished = db.Column(db.Boolean, default=False)
timestamp = db.Column(db.DateTime, default=datetime.now) timestamp = db.Column(db.DateTime, default=datetime.now)
def __init__(self, test_id, message, finished=False):
def __init__(self,test_id, message, finished=False):
self.test_id = test_id self.test_id = test_id
self.message = message self.message = message
self.finished = finished self.finished = finished
@ -125,9 +122,7 @@ class TestResults(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
test_id = db.Column(db.Integer, db.ForeignKey('test.id')) test_id = db.Column(db.Integer, db.ForeignKey('test.id'))
test = db.relationship('Test', test = db.relationship('Test',
backref=db.backref('results',lazy='dynamic')) backref=db.backref('results', lazy='dynamic'))
timestamp = db.Column(db.DateTime, default=datetime.now) timestamp = db.Column(db.DateTime, default=datetime.now)
subunit = db.Column(db.String(8192)) subunit = db.Column(db.String(8192))
blob = db.Column(db.Binary) blob = db.Column(db.Binary)

23
refstack/utils.py Normal file → Executable file
View File

@ -1,13 +1,23 @@
# -*- coding: utf-8 -*- #
# Copyright (c) 2013 Piston Cloud Computing, Inc.
# This file based on MIT licensed code at: https://github.com/imwilsonxu/fbone # 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.
""" """
Utils has nothing to do with models and views. Utils has nothing to do with models and views.
""" """
from datetime import datetime from datetime import datetime
import logging
import os import os
import pprint import pprint
import random import random
@ -92,7 +102,8 @@ def pretty_date(dt, default=None):
def allowed_file(filename): def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_AVATAR_EXTENSIONS return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_AVATAR_EXTENSIONS
def id_generator(size=10, chars=string.ascii_letters + string.digits): def id_generator(size=10, chars=string.ascii_letters + string.digits):

View File

@ -1,6 +1,5 @@
# #
# Copyright (c) 2013 Piston Cloud Computing, Inc. # Copyright (c) 2013 Piston Cloud Computing, Inc. All Rights Reserved.
# All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
@ -13,30 +12,26 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import os #
import logging from flask import abort, flash, request, redirect, url_for, \
render_template, g, session, flask
import flask #from flask_openid import OpenID
from flask import Flask, abort, flash, request, redirect, url_for, \ #from flask.ext.admin import Admin, BaseView, expose, AdminIndexView
render_template, g, session #from flask.ext.admin.contrib.sqlamodel import ModelView
from flask_openid import OpenID #from flask.ext.security import Security, \
from flask.ext.admin import Admin, BaseView, expose, AdminIndexView # UserMixin, login_required
from flask.ext.admin.contrib.sqlamodel import ModelView #from wtforms import TextField
from flask.ext.security import Security, SQLAlchemyUserDatastore, \
UserMixin, RoleMixin, login_required
from wtforms import Form, BooleanField, TextField, \
PasswordField, validators
from flask_mail import Mail from flask_mail import Mail
from refstack import app as base_app from refstack import app as base_app
from refstack.extensions import db from refstack.extensions import db
from refstack.extensions import oid from refstack.extensions import oid
from refstack import api #from refstack import api
from refstack.models import ApiKey #from refstack.models import ApiKey
from refstack.models import Cloud #from refstack.models import Cloud
from refstack.models import Test #from refstack.models import Test
from refstack.models import TestResults #from refstack.models import TestResults
from refstack.models import TestStatus #from refstack.models import TestStatus
from refstack.models import User from refstack.models import User
from refstack.models import Vendor from refstack.models import Vendor
@ -62,7 +57,7 @@ def index():
"""Index view.""" """Index view."""
if g.user is not None: if g.user is not None:
# something else # something else
clouds = Cloud.query.filter_by(user_id=g.user.id).all() clouds = db.Cloud.query.filter_by(user_id=g.user.id).all()
return render_template('home.html', clouds=clouds) return render_template('home.html', clouds=clouds)
else: else:
vendors = Vendor.query.all() vendors = Vendor.query.all()
@ -79,8 +74,9 @@ def login():
# if we are already logged in, go back to were we came from # if we are already logged in, go back to were we came from
if g.user is not None: if g.user is not None:
return redirect(oid.get_next_url()) return redirect(oid.get_next_url())
return oid.try_login("https://login.launchpad.net/", return oid.try_login(
ask_for=['email', 'nickname']) "https://login.launchpad.net/",
ask_for=['email', 'nickname'])
@oid.after_login @oid.after_login
@ -127,7 +123,7 @@ def create_profile():
@app.route('/delete-cloud/<int:cloud_id>', methods=['GET', 'POST']) @app.route('/delete-cloud/<int:cloud_id>', methods=['GET', 'POST'])
def delete_cloud(cloud_id): def delete_cloud(cloud_id):
"""Delete function for clouds.""" """Delete function for clouds."""
c = Cloud.query.filter_by(id=cloud_id).first() c = db.Cloud.query.filter_by(id=cloud_id).first()
if not c: if not c:
flash(u'Not a valid Cloud ID!') flash(u'Not a valid Cloud ID!')
@ -142,7 +138,7 @@ def delete_cloud(cloud_id):
@app.route('/edit-cloud/<int:cloud_id>', methods=['GET', 'POST']) @app.route('/edit-cloud/<int:cloud_id>', methods=['GET', 'POST'])
def edit_cloud(cloud_id): def edit_cloud(cloud_id):
c = Cloud.query.filter_by(id=cloud_id).first() c = db.Cloud.query.filter_by(id=cloud_id).first()
if not c: if not c:
flash(u'Not a valid Cloud ID!') flash(u'Not a valid Cloud ID!')
@ -188,9 +184,7 @@ def edit_cloud(cloud_id):
admin_user=c.admin_user, admin_user=c.admin_user,
admin_key=c.admin_key) admin_key=c.admin_key)
return render_template('edit_cloud.html', form=form)
return render_template('edit_cloud.html',form=form)
@app.route('/create-cloud', methods=['GET', 'POST']) @app.route('/create-cloud', methods=['GET', 'POST'])
@ -215,7 +209,7 @@ def create_cloud():
elif not request.form['admin_key']: elif not request.form['admin_key']:
flash(u'Error: All fields are required') flash(u'Error: All fields are required')
else: else:
c = Cloud() c = db.Cloud()
c.user_id = g.user.id c.user_id = g.user.id
c.label = request.form['label'] c.label = request.form['label']
c.endpoint = request.form['endpoint'] c.endpoint = request.form['endpoint']

View File

@ -14,9 +14,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
"""Simple script for importing the vendor """Simple script for importing the vendor
list csv file provided by the foundation""" list csv file provided by the foundation"""
from refstack.models import * from refstack.models import db, Vendor
import csv import csv
_file = 'members_sponsors_20131030.csv' _file = 'members_sponsors_20131030.csv'