From 407c8872442d5d11d0f99ebcf6a0f25558a302e7 Mon Sep 17 00:00:00 2001 From: David Lenwell Date: Fri, 4 Oct 2013 01:24:29 -0700 Subject: [PATCH] added additional schema and migration also moved the database into /tmp for now.. --- alembic/env.py | 8 +- .../501f41fac63a_additions_to_schema.py | 22 +++ .../versions/53fcc008b313_auto_generating.py | 56 +++++++ refstack/app.py | 6 +- refstack/cli/refstack | 19 +-- refstack/models.py | 145 +++++++++++++----- 6 files changed, 201 insertions(+), 55 deletions(-) create mode 100644 alembic/versions/501f41fac63a_additions_to_schema.py create mode 100755 alembic/versions/53fcc008b313_auto_generating.py diff --git a/alembic/env.py b/alembic/env.py index a42a57c3..afed5bdd 100755 --- a/alembic/env.py +++ b/alembic/env.py @@ -24,13 +24,13 @@ sys.path.append("./") from alembic import context from sqlalchemy import engine_from_config, pool from logging.config import fileConfig -from refstack.web import app +from refstack import app # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config cur_db_uri = config.get_section_option('alembic', 'sqlalchemy.url') -my_db_uri = app.config.get('SQLALCHEMY_DATABASE_URI', cur_db_uri) +my_db_uri = 'sqlite:////tmp/refstack.db' # app.config.get('SQLALCHEMY_DATABASE_URI', cur_db_uri) config.set_section_option('alembic', 'sqlalchemy.url', my_db_uri) # Interpret the config file for Python logging. @@ -42,8 +42,8 @@ fileConfig(config.config_file_name) # from myapp import mymodel # target_metadata = mymodel.Base.metadata # target_metadata = None -from refstack.models import db -target_metadata = db.metadata +from refstack.models import * +target_metadata = Base.metadata def run_migrations_offline(): diff --git a/alembic/versions/501f41fac63a_additions_to_schema.py b/alembic/versions/501f41fac63a_additions_to_schema.py new file mode 100644 index 00000000..8aaddaa3 --- /dev/null +++ b/alembic/versions/501f41fac63a_additions_to_schema.py @@ -0,0 +1,22 @@ +"""additions to schema + +Revision ID: 501f41fac63a +Revises: 2bf99fa45ded +Create Date: 2013-09-20 10:18:32.223074 + +""" + +# revision identifiers, used by Alembic. +revision = '501f41fac63a' +down_revision = '2bf99fa45ded' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/alembic/versions/53fcc008b313_auto_generating.py b/alembic/versions/53fcc008b313_auto_generating.py new file mode 100755 index 00000000..bc6afaa4 --- /dev/null +++ b/alembic/versions/53fcc008b313_auto_generating.py @@ -0,0 +1,56 @@ +"""auto generating + +Revision ID: 53fcc008b313 +Revises: 501f41fac63a +Create Date: 2013-09-20 10:30:23.782772 + +""" + +# revision identifiers, used by Alembic. +revision = '53fcc008b313' +down_revision = '501f41fac63a' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.create_table('test', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('cloud_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['cloud_id'], ['cloud.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('test_results', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('test_id', sa.Integer(), nullable=True), + sa.Column('timestamp', sa.DateTime(), nullable=True), + sa.Column('blob', sa.Binary(), nullable=True), + sa.ForeignKeyConstraint(['test_id'], ['test.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('finished', sa.Boolean(), nullable=True), + sa.Column('timestamp', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['test_id'], ['test.id'], ), + sa.PrimaryKeyConstraint('id') + ) + + op.add_column(u'user', sa.Column('vendor_id', sa.Integer(), nullable=True)) + op.add_column(u'user', sa.Column('authorized', sa.Boolean(), nullable=True)) + ### end Alembic commands ### + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.drop_column(u'user', 'authorized') + op.drop_column(u'user', 'vendor_id') + op.add_column(u'cloud', sa.Column(u'vendor_id', sa.INTEGER(), nullable=True)) + op.drop_table('test_status') + op.drop_table('test_results') + op.drop_table('test') + ### end Alembic commands ### diff --git a/refstack/app.py b/refstack/app.py index 27bf6d01..a7a56bb2 100755 --- a/refstack/app.py +++ b/refstack/app.py @@ -19,8 +19,10 @@ from flask import Flask, session -db_path = os.path.abspath( - os.path.join(os.path.basename(__file__), "../")) +#db_path = os.path.abspath( +# os.path.join(os.path.basename(__file__), "../")) + +db_path = 'tmp' app = Flask(__name__) diff --git a/refstack/cli/refstack b/refstack/cli/refstack index 8780f003..182d7e3f 100755 --- a/refstack/cli/refstack +++ b/refstack/cli/refstack @@ -19,11 +19,11 @@ import argparse from textwrap import dedent from sqlalchemy.exc import IntegrityError -from refstack.localmodels import * +from refstack.models import * def add(args): - """add cloud + """adds a cloud refstack add --endpoint='http://127.0.0.1:5000/v3/' --test-user='demo' --test-key='pass' --admin-endpoint='http://127.0.0.1:5000/v3/' @@ -50,7 +50,7 @@ def add(args): def remove(args): - """remove cloud + """removes a cloud refstack remove {cloud_id} confirms that cloud-id 123 has been removed from the database as well as @@ -67,7 +67,7 @@ def remove(args): def clouds(args): - """returns either a list of cached tests""" + """returns a list of your clouds""" print 'Your clouds:\n' print 'id | endpoint | test-user | admin-user ' print '---------------------------------------' @@ -79,7 +79,7 @@ def clouds(args): def run(args): """run test command - refstack run --cloud_id {123} --sha {sha} + refstack run {cloud_id} --sha {sha} triggers local run of tempest with specified cloud_id returns a test_id so that the user can check status or cancel the test""" @@ -118,15 +118,6 @@ def tests(args): print 'tests triggered' -def clouds(args): - """returns either a list of cached tests""" - print 'Your clouds:\n' - print 'id | endpoint | test-user | admin-user ' - print '---------------------------------------' - for row in db.query(Cloud).all(): - print "%s | %s | %s | %s " % (row.id, row.endpoint, row.test_user, row.admin_user) - print '' - def subcommands(subparsers): """argparse subparsers with """ add_cloud_parser = subparsers.add_parser('add', help='Add a new Cloud') diff --git a/refstack/models.py b/refstack/models.py index ff4b73c8..6604e131 100755 --- a/refstack/models.py +++ b/refstack/models.py @@ -13,49 +13,124 @@ # 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.sqlalchemy import SQLAlchemy -from sqlalchemy.exc import IntegrityError -from app import app +"""striaght up sqlalchemy declarative_base model structure. -db = SQLAlchemy(app) + *I created this because i was having a problem getting + the cli to use the models that were generated for the flask + webapp. The plan is to use this for both. But I have not + started my serious efforts on the web interface. dl 10.2013 + + *For now in dev I have this database in /tmp we can talk + about someplace else for it by default. +""" +from datetime import datetime +from sqlalchemy import create_engine +from sqlalchemy.orm import scoped_session, sessionmaker,relationship, backref +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import Column, Integer, String, ForeignKey, DateTime, Binary, Boolean + +engine = create_engine('sqlite:////tmp/refstack.db', convert_unicode=True) +db = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=engine)) + +Base = declarative_base() +Base.query = db.query_property() -class Vendor(db.Model): - id = db.Column(db.Integer, primary_key=True) - vendor_name = db.Column(db.String(80), unique=True) - contact_email = db.Column(db.String(120), unique=True) - def __str__(self): - return self.vendor_name - - -class Cloud(db.Model): - id = db.Column(db.Integer, primary_key=True) - vendor_id = db.Column(db.Integer, db.ForeignKey('vendor.id')) - vendor = db.relationship('Vendor', - backref=db.backref('clouds', +class User(Base): + __tablename__ = 'user' + id = Column(Integer, primary_key=True) + vendor_id = Column(Integer, ForeignKey('vendor.id')) + vendor = relationship('Vendor', + backref=backref('clouds', lazy='dynamic')) - endpoint = db.Column(db.String(120), unique=False) - test_user = db.Column(db.String(80), unique=False) - test_key = db.Column(db.String(80), unique=False) - admin_endpoint = db.Column(db.String(120), unique=False) - admin_user = db.Column(db.String(80), unique=False) - admin_key = db.Column(db.String(80), unique=False) - - def __str__(self): - return self.endpoint - - -class User(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(60)) - email = db.Column(db.String(200), unique=True) - openid = db.Column(db.String(200), unique=True) - + name = Column(String(60)) + email = Column(String(200), unique=True) + openid = Column(String(200), unique=True) + authorized = Column(Boolean, default=False) + def __init__(self, name, email, openid): self.name = name self.email = email self.openid = openid def __str__(self): - return self.name \ No newline at end of file + return self.name + + + +class Vendor(Base): + __tablename__ = 'vendor' + id = Column(Integer, primary_key=True) + vendor_name = Column(String(80), unique=True) + contact_email = Column(String(120), unique=True) + + def __str__(self): + return self.vendor_name + + + +class Cloud(Base): + """*need to take the time to descibe this stuff in detail. + """ + __tablename__ = 'cloud' + id = Column(Integer, primary_key=True) + + endpoint = Column(String(120), unique=True) + test_user = Column(String(80), unique=False) + test_key = Column(String(80), unique=False) + admin_endpoint = Column(String(120), unique=False) + admin_user = Column(String(80), unique=False) + admin_key = Column(String(80), unique=False) + + def __init__(self, + endpoint, + test_user, + test_key, + admin_endpoint, + admin_user, + admin_key): + """init method to allow ordered input """ + self.endpoint = endpoint + self.test_user = test_user + self.test_key = test_key + self.admin_endpoint = admin_endpoint + self.admin_user = admin_user + self.admin_key = admin_key + + + +class Test(Base): + __tablename__ = 'test' + id = Column(Integer, primary_key=True) + cloud_id = Column(Integer, ForeignKey('cloud.id')) + cloud = relationship('Cloud', + backref=backref('tests',lazy='dynamic')) + status = relationship("TestStatus", + order_by="desc(test_status.timestamp)", + primaryjoin="TestStatus.test_id==Test.id") + + + +class TestStatus(Base): + __tablename__ = 'test_status' + id = Column(Integer, primary_key=True) + test_id = Column(Integer, ForeignKey('test.id')) + message = Column(String(1024)) + finished = Column(Boolean, default=False) + timestamp = Column(DateTime, default=datetime.now) + + + +class TestResults(Base): + __tablename__ = 'test_results' + id = Column(Integer, primary_key=True) + test_id = Column(Integer, ForeignKey('test.id')) + test = relationship('Test', + backref=backref('results',lazy='dynamic')) + timestamp = Column(DateTime, default=datetime.now) + blob = Column(Binary) + +