Commit 4f8f9931 authored by Gero Müller's avatar Gero Müller
Browse files

refactor alembic, remove db plugin, add workspaceconnection to reconnect to running workspace

parent 18a20703
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}
"""Initial setup
Revision ID: 1e0a9d82ddc3
Revises: None
Create Date: 2012-09-25 17:43:11.860161
"""
# revision identifiers, used by Alembic.
revision = '1e0a9d82ddc3'
down_revision = None
from alembic import op
import sqlalchemy as sa
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table(u'shared_folder')
op.drop_table(u'access_statistics')
op.drop_table(u'user')
op.drop_table(u'workspace')
op.drop_table(u'shared_folder_allocation')
### end Alembic commands ###
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table(u'shared_folder_allocation',
sa.Column(u'folder_id', sa.INTEGER(), nullable=False),
sa.Column(u'user_id', sa.INTEGER(), nullable=False),
sa.ForeignKeyConstraint(['folder_id'], [u'shared_folder.id'], ),
sa.ForeignKeyConstraint(['user_id'], [u'user.id'], ),
sa.PrimaryKeyConstraint(u'folder_id', u'user_id')
)
op.create_table(u'workspace',
sa.Column(u'id', sa.INTEGER(), nullable=False, primary_key=True),
sa.Column(u'name', sa.UnicodeText(), nullable=False),
sa.Column(u'host', sa.UnicodeText(), nullable=False),
sa.Column(u'login', sa.UnicodeText(), nullable=True),
sa.Column(u'key', sa.UnicodeText(), nullable=False),
sa.Column(u'password', sa.UnicodeText(), nullable=False),
sa.Column(u'command', sa.UnicodeText(), nullable=False),
sa.Column(u'basedir', sa.UnicodeText(), nullable=False),
)
op.create_table(u'user',
sa.Column(u'id', sa.INTEGER(), nullable=False, primary_key=True),
sa.Column(u'name', sa.UnicodeText(), nullable=False),
sa.Column(u'password', sa.UnicodeText(), nullable=False),
sa.Column(u'email', sa.UnicodeText(), nullable=False),
sa.Column(u'created', sa.DATETIME(), nullable=False),
sa.Column(u'last_request', sa.UnicodeText(), nullable=False),
sa.Column(u'status', sa.INTEGER(), nullable=False),
)
op.create_table(u'access_statistics',
sa.Column(u'id', sa.INTEGER(), nullable=False, primary_key=True),
sa.Column(u'user_ip', sa.UnicodeText(), nullable=False),
sa.Column(u'user_agent', sa.UnicodeText(), nullable=False),
sa.Column(u'date', sa.DATE(), nullable=False),
sa.Column(u'pis', sa.INTEGER(), nullable=False),
)
op.create_table(u'shared_folder',
sa.Column(u'id', sa.INTEGER(), nullable=False, primary_key=True),
sa.Column(u'name', sa.UnicodeText(), nullable=False),
sa.Column(u'created', sa.DATETIME(), nullable=False),
)
### end Alembic commands ###
"""Add hash to user
Revision ID: 7d1e9dd251c
Revises: 1e0a9d82ddc3
Create Date: 2012-09-25 17:44:10.314270
"""
# revision identifiers, used by Alembic.
revision = '7d1e9dd251c'
down_revision = '1e0a9d82ddc3'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('user', sa.Column('hash', sa.UnicodeText()))
def downgrade():
op.drop_column('user', 'hash')
\ No newline at end of file
......@@ -2,8 +2,8 @@
#sqlalchemy_url = sqlite:///var/db/vispa.db
[alembic]
# path to migration scripts
script_location = alembic
script_location = "vispa:models.alembic"
auto_migrate = True
[web]
dev_mode = True
......
......@@ -12,6 +12,7 @@ from vispa.models.preference import VispaPreference, ExtensionPreference
from vispa.models.shortcuts import VispaShortcuts, ExtensionShortcuts
from vispa import browser
import vispa
import vispa.workspace
import logging
import json
import os
......
......@@ -25,4 +25,6 @@ def insertion_safe(*args, **kwargs):
for char in FORBIDDEN_CHARS:
if elem.lower().find(char) >= 0:
return False, elem
return True, None
\ No newline at end of file
return True, None
from . import preference, shortcuts, stats, user, workspace
\ No newline at end of file
from os.path import abspath, dirname
import logging
from alembic.script import ScriptDirectory
from alembic.config import Config
from alembic.environment import EnvironmentContext
import vispa.models
logger = logging.getLogger(__name__)
def migrate(db, revision="head"):
script_location = dirname(abspath(__file__))
logger.debug("script_location: %s" % script_location)
config = Config()
config.set_main_option("script_location", script_location)
script = ScriptDirectory.from_config(config)
def upgrade(rev, context):
return script._upgrade_revs(revision, rev)
with EnvironmentContext(
config,
script,
fn=upgrade,
destination_rev=revision
) as context:
context.configure(
connection=db,
target_metadata=vispa.models.Base.metadata
)
try:
with context.begin_transaction():
context.run_migrations()
except:
vispa.log_exception()
from __future__ import with_statement
import os
import sys
from alembic import context
from sqlalchemy import create_engine, pool
from logging.config import fileConfig
import ConfigParser
import sys, os
# New instance with 'bar' and 'baz' defaulting to 'Life' and 'hard' each
config = ConfigParser.SafeConfigParser()
config.read(context.config.config_file_name)
url = config.get('database', 'sqlalchemy.url')
# Interpret the config file for Python logging.
# This line sets up loggers basically.
#fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),"..")))
from vispa import models
url = context.config.get_section_option('database', 'sqlalchemy_url')
try:
from vispa import models
except:
vispa_path = os.path.abspath(__file__)
vispa_path = os.path.dirname(vispa_path)
vispa_path = os.path.dirname(vispa_path)
vispa_path = os.path.dirname(vispa_path)
vispa_path = os.path.dirname(vispa_path)
sys.path.insert(0, vispa_path)
from vispa import models
target_metadata = models.Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
context.configure(url=url)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
def run_migrations_online():
engine = create_engine(url, poolclass=pool.NullPool)
connection = engine.connect()
context.configure(
connection=connection,
target_metadata=target_metadata
)
context.configure(connection=connection, target_metadata=target_metadata)
try:
with context.begin_transaction():
......@@ -70,4 +42,3 @@ if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
"""inital
Revision ID: 323ad4f65ffd
Revises: None
Create Date: 2013-11-26 21:49:02.833905
"""
# revision identifiers, used by Alembic.
revision = '323ad4f65ffd'
down_revision = None
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.Unicode(length=30), nullable=False),
sa.Column('password', sa.Unicode(length=64), nullable=False),
sa.Column('email', sa.Unicode(length=100), nullable=False),
sa.Column('created', sa.DateTime(), nullable=False),
sa.Column('last_request', sa.DateTime(), nullable=False),
sa.Column('status', sa.Integer(), nullable=False),
sa.Column('hash', sa.Unicode(length=100), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('name')
)
op.create_table('page_statistics',
sa.Column('page', sa.Unicode(length=50), nullable=False),
sa.Column('date', sa.Date(), nullable=False),
sa.Column('pis', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('page', 'date')
)
op.create_table('access_statistics',
sa.Column('user_ip', sa.Unicode(length=60), nullable=False),
sa.Column('user_agent', sa.Unicode(length=200), nullable=False),
sa.Column('date', sa.Date(), nullable=False),
sa.Column('pis', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('user_ip', 'user_agent', 'date')
)
op.create_table('workspace',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('name', sa.Unicode(length=100), nullable=False),
sa.Column('host', sa.Unicode(length=100), nullable=True),
sa.Column('login', sa.Unicode(length=100), nullable=True),
sa.Column('key', sa.Text(), nullable=True),
sa.Column('password', sa.Unicode(length=100), nullable=True),
sa.Column('command', sa.Text(), nullable=True),
sa.Column('basedir', sa.Text(), nullable=True),
sa.Column('created', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('host','login')
)
op.create_table('vispa_preference',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('section', sa.Unicode(length=64), nullable=False),
sa.Column('value', sa.Unicode(length=300), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.Column('created', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('user_id', 'section')
)
op.create_table('extension_preference',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('key', sa.Unicode(length=64), nullable=False),
sa.Column('value', sa.Unicode(length=300), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.Column('created', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('user_id', 'key')
)
op.create_table('extension_shortcuts',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('key', sa.Unicode(length=64), nullable=False),
sa.Column('value', sa.Unicode(length=300), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.Column('created', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('user_id', 'key')
)
op.create_table('vispa_shortcuts',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('key', sa.Unicode(length=64), nullable=False),
sa.Column('value', sa.Unicode(length=300), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.Column('created', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('user_id', 'key')
)
op.create_table('workspace_state',
sa.Column('workspace_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('state', sa.Text(), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.ForeignKeyConstraint(['workspace_id'], ['workspace.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('workspace_id', 'user_id')
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('workspace_state')
op.drop_table('vispa_shortcuts')
op.drop_table('extension_shortcuts')
op.drop_table('extension_preference')
op.drop_table('vispa_preference')
op.drop_table('workspace')
op.drop_table('access_statistics')
op.drop_table('page_statistics')
op.drop_table('user')
### end Alembic commands ###
"""add_workspace_connection
Revision ID: 53f0259321a8
Revises: 323ad4f65ffd
Create Date: 2013-11-26 21:50:37.787190
"""
# revision identifiers, used by Alembic.
revision = '53f0259321a8'
down_revision = '323ad4f65ffd'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('workspace_connection',
sa.Column('workspace_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('host', sa.Text(), nullable=False),
sa.Column('tempdir', sa.Text(), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.ForeignKeyConstraint(['workspace_id'], ['workspace.id'], onupdate='CASCADE', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('workspace_id', 'user_id')
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('workspace_connection')
### end Alembic commands ###
......@@ -10,19 +10,19 @@ import json
class Workspace(Base):
__tablename__ = 'workspace'
id = Column(Integer, nullable=False, primary_key=True)
user_id = Column(Integer, schema.ForeignKey('user.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=True)
name = Column(Unicode(100), nullable=False)
host = Column(Unicode(100), nullable=True, default=None)
login = Column(Unicode(100), nullable=True, default=None)
key = Column(Text, nullable=True, default=None)
password = Column(Unicode(100), nullable=True, default=None)
command = Column(Text, nullable=True, default=None)
basedir = Column(Text, nullable=True, default=None)
#locations = Column(Unicode(100), nullable=False)# e.g. public/user/shared
created = Column(DateTime, nullable=True, default=datetime.now)
__table_args__ = (schema.UniqueConstraint(host, login),)
__tablename__ = 'workspace'
id = Column(Integer, nullable=False, primary_key=True)
user_id = Column(Integer, schema.ForeignKey('user.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=True)
name = Column(Unicode(100), nullable=False)
host = Column(Unicode(100), nullable=True, default=None)
login = Column(Unicode(100), nullable=True, default=None)
key = Column(Text, nullable=True, default=None)
password = Column(Unicode(100), nullable=True, default=None)
command = Column(Text, nullable=True, default=None)
basedir = Column(Text, nullable=True, default=None)
# locations = Column(Unicode(100), nullable=False)# e.g. public/user/shared
created = Column(DateTime, nullable=True, default=datetime.now)
__table_args__ = (schema.UniqueConstraint(host, login),)
KEYS = ['id', 'user_id', 'name', 'host', 'login', 'key', 'password', 'command', 'basedir', 'created']
......@@ -116,12 +116,12 @@ class Workspace(Base):
class WorkspaceState(Base):
__tablename__ = 'workspace_state'
workspace_id = Column(Integer, schema.ForeignKey('workspace.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
user_id = Column(Integer, schema.ForeignKey('user.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
state = Column(Text, nullable=True, default=u'{}')
timestamp = Column(DateTime, nullable=True, default=datetime.now)
__table_args__ = (schema.PrimaryKeyConstraint(workspace_id, user_id),)
__tablename__ = 'workspace_state'
workspace_id = Column(Integer, schema.ForeignKey('workspace.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
user_id = Column(Integer, schema.ForeignKey('user.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
state = Column(Text, nullable=True, default=u'{}')
timestamp = Column(DateTime, nullable=True, default=datetime.now)
__table_args__ = (schema.PrimaryKeyConstraint(workspace_id, user_id),)
@staticmethod
def get_by_ids(session, workspace_id, user_id):
......@@ -159,3 +159,18 @@ class WorkspaceState(Base):
workspace_state.timestamp = datetime.now()
session.commit()
return True
class WorkspaceConnection(Base):
__tablename__ = 'workspace_connection'
workspace_id = Column(Integer, schema.ForeignKey('workspace.id',
ondelete='CASCADE',
onupdate='CASCADE'),
nullable=False, primary_key=True)
user_id = Column(Integer, schema.ForeignKey('user.id', ondelete='CASCADE',
onupdate='CASCADE'),
nullable=False, primary_key=True)
host = Column(Text, nullable=False)
tempdir = Column(Text, nullable=True, default=u'{}')
timestamp = Column(DateTime, nullable=True, default=datetime.now)
# -*- coding: utf-8 -*-
from cherrypy.process.plugins import SimplePlugin
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from vispa.models import Base
class SAPlugin(SimplePlugin):
def __init__(self, bus, identifier):
"""
The plugin is registered to the CherryPy engine and therefore
is part of the bus (the engine *is* a bus) registery.
We use this plugin to create the SA engine. At the same time,
when the plugin starts we create the tables into the database
using the mapped class of the global metadata.
"""
SimplePlugin.__init__(self, bus)
self.bus.listeners.setdefault('bind_session', set())
self.bus.listeners.setdefault('commit_session', set())
self._engine = None
self._session = scoped_session(sessionmaker(autoflush=True, autocommit=False))
self._identifier = identifier
def start(self):
self._engine = create_engine(self._identifier, echo=False)
self._create_all()
def stop(self):
if self._engine:
self._engine.dispose()
self._engine = None
def bind_session(self):
"""
Whenever this plugin receives the 'bind-_session' command, it applies
this method and to bind the current _session to the engine.
It then returns the _session to the caller.
"""
self._session.configure(bind=self._engine)
return self._session
def commit_session(self):
"""
Commits the current transaction or rollbacks if an error occurs.
In all cases, the current _session is unbound and therefore
not usable any longer.
"""
try:
self._session.commit()
except:
self._session.rollback()
finally:
self._session.remove()
def _create_all(self):
self.bus.log('Creating database')
from vispa.models.user import User # @UnusedImport
from vispa.models.workspace import Workspace # @UnusedImport
from vispa.models.stats import AccessStats, PageStats # @UnusedImport
from vispa.models.preference import VispaPreference # @UnusedImport
Base.metadata.create_all(self._engine) # @UndefinedVariable
def _destroy_all(self):
self.bus.log('Destroying database')
Base.metadata.drop_all(self._engine) # @UndefinedVariable
......@@ -8,11 +8,12 @@ import inspect
from logging import config as loggingcfg
from pkgutil import iter_modules
import importlib
import sqlalchemy
import vispa.url
import vispa.plugins.db
import vispa.plugins.template
import vispa.extensions
import vispa.models.alembic
logger = logging.getLogger(__name__)
......@@ -144,6 +145,7 @@ class Server(object):
def __init__(self, args):
self.__init_paths(args)
self.__init_logging(args)
self.__init_database(args)
self.__init_tools(args)
self.__init_platform(args)