Пример #1
0
 def post(self):
     parser = reqparse.RequestParser()
     parser.add_argument('username', type=str, required=True,
                         help='Username')
     parser.add_argument('org', type=str, required=True,
                         help='Org for user membership')
     parser.add_argument('email', type=str, required=True,
                         help='Email address for user')
     parser.add_argument('parentuser', type=str, required=False,
                         help='Parent user in form of user@org')
     args = parser.parse_args()
     try:
         session = CassandraCluster.getSession(
             config['cassandra']['auth_keyspace'])
         checkUserExistsQuery = CassandraCluster.getPreparedStatement(
             """
             SELECT username, org FROM users
             WHERE org = ?
             AND username = ?
             """, keyspace=session.keyspace)
         results = session.execute(checkUserExistsQuery,
                                   (args['org'], args['username'])
                                   ).current_rows
         if len(results) == 0:
             checkOrgSetting = CassandraCluster.getPreparedStatement(
                 """
                 SELECT value FROM orgsettings
                 WHERE org = ?
                 AND setting = ?
                 """, keyspace=session.keyspace)
             results = session.execute(checkOrgSetting,
                                       (args['org'], 'registrationOpen')
                                       ).current_rows
             if len(results) == 0 or results[0].value == 0:
                 return {'Message':
                         'Cannot create user "%s@%s". Organization is ' %
                         (args['username'], args['org']) +
                         'closed for registrations or does not exist.'}, 400
             else:
                 createUserQuery = CassandraCluster.getPreparedStatement(
                     """
                     INSERT INTO users ( org, username, email, parentuser,
                     createdate )
                     VALUES ( ?, ?, ?, ?, dateof(now()) )
                     """, keyspace=session.keyspace)
                 createUserQuery.consistency_level = ConsistencyLevel.QUORUM
                 session.execute(createUserQuery,
                                 (args['org'], args['username'],
                                  args['email'], args['parentuser'])
                                 )
         else:
             return {'Message':
                     'Cannot create user "%s@%s", as it already exists.' %
                     (args['username'], args['org'])}, 400
     except Exception as e:
         log.error('Exception in Users.Post: %s' % (e,))
         return {'ServerError': 500, 'Message':
                 'There was an error fulfiling your request'}, 500
     return {'Message':
             'User "%s@%s" created.' % (args['username'], args['org'])}
Пример #2
0
    def createUserSessionKey(org, username, sessionId,
                             consistency=ConsistencyLevel.LOCAL_QUORUM,
                             session=None):
        """
        Create a session key record in the usersessionkeys table for the given
        user session

        :org:
            Name of organization for the user
        :username:
            Name of the user
        :sessionid:
            ID of the session to create a key for
        """
        charList = (list(range(48, 58)) +  # Numbers
                    list(range(65, 91)) +  # Uppercase
                    list(range(97, 123)))  # Lowercase
        sysrand = SystemRandom()
        sessionKey = ''.join(
            chr(sysrand.choice(charList))
            for i in range(64))
        try:
            createUserSessionKeyQuery = CassandraCluster.getPreparedStatement(
                """
                INSERT INTO usersessionkeys ( sessionkey, org, username,
                    sessionid )
                VALUES ( ?, ?, ?, ? )
                """, keyspace=session.keyspace)
            createUserSessionKeyQuery.consistency_level = consistency
            session.execute(createUserSessionKeyQuery,
                            (sessionKey, org, username, sessionId))
            return sessionKey
        except Exception as e:
            log.critical("Exception in AuthDB.createUserSessionKey: %s" % (e,))
Пример #3
0
    def createUserSession(org, username,
                          consistency=ConsistencyLevel.LOCAL_QUORUM,
                          session=None):
        """
        Create a session record in the usersessions table for the given user

        :org:
            Name of organization for the user
        :username:
            Name of the user
        """
        sessionId = uuid.uuid4()
        try:
            createUserSessionQuery = CassandraCluster.getPreparedStatement(
                """
                INSERT INTO usersessions ( org, username, sessionid, startdate,
                    lastupdate )
                VALUES ( ?, ?, ?, dateof(now()), dateof(now()) )
                """, keyspace=session.keyspace)
            createUserSessionQuery.consistency_level = consistency
            session.execute(createUserSessionQuery,
                            (org, username, sessionId))
            return sessionId
        except Exception as e:
            log.critical("Exception in AuthDB.createUserSession: %s" % (e,))
Пример #4
0
    def createUser(org, username, email, parentuser,
                   consistency=ConsistencyLevel.LOCAL_QUORUM,
                   session=None):
        """
        Create a user in the authdb.users table.

        :org:
            Name of organization
        :username:
            Name of user
        :email:
            Email address for the user
        :parentuser:
            Parent user for this user (in the form of user@org) or None
        :consistency:
            Cassandra ConsistencyLevel (default LOCAL_QUORUM)
        """
        createUserQuery = CassandraCluster.getPreparedStatement(
            """
            INSERT INTO users ( org, username, email, parentuser, createdate )
            VALUES ( ?, ?, ?, ?, dateof(now()) )
            """, keyspace=session.keyspace)
        createUserQuery.consistency_level = consistency
        return session.execute(createUserQuery,
                               (org, username, email, parentuser))
Пример #5
0
    def createPasswordReset(org, username,
                            consistency=ConsistencyLevel.LOCAL_QUORUM,
                            session=None):
        """
        Create a password reset in authdb.userpasswordresets table.

        :org:
            Name of organization
        :username:
            Name of user
        """
        createPasswordResetQuery = CassandraCluster.getPreparedStatement(
            """
            INSERT INTO userpasswordresets ( org, username, requestdate,
                                             resetid )
            VALUES ( ?, ?, dateof(now()), ? )
            """, keyspace=session.keyspace)
        createPasswordResetQuery.consistency_level = consistency

        resetid = uuid.uuid4()

        try:
            session.execute(createPasswordResetQuery,
                            (org, username, resetid))
            return resetid
        except Exception as e:
            log.error("Caught exception in AuthDB.createPasswordReset: %s"
                      % (e,))
            return False
Пример #6
0
    def setPassword(org, username, passwordHash, salt,
                    consistency=ConsistencyLevel.LOCAL_QUORUM,
                    session=None):
        """
        Update/set user's password with given hash and salt

        :org:
            Name of org the user is in
        :username:
            Name of user
        :passwordHash:
            The Argon2 hash of the salted password
        :salt:
            Salt used to generate the hash
        :consistency:
            Cassandra consistency level. Defaults to LOCAL_QUORUM.
        """
        setPasswordQuery = CassandraCluster.getPreparedStatement(
            """
            UPDATE users SET
            hash = ?,
            salt = ?
            WHERE org = ?
            AND username = ?
            """, keyspace=session.keyspace)
        setPasswordQuery.consistency_level = consistency
        session.execute(setPasswordQuery, (passwordHash, salt, org, username))
Пример #7
0
    def setupDB(keyspace, replication_class='SimpleStrategy',
                replication_factor=1):
        try:
            DB.createDB(keyspace, replication_class, replication_factor)
        except cassandra.AlreadyExists:
            log.info('Keyspace "%s" already exists (skipping)' % (keyspace,))

        session = CassandraCluster.getSession(keyspace)

        if not DB.tableExists(session.keyspace, 'schema_migrations'):
            # Create the schema_migrations table. This table stores the history
            #   of schema update scripts that have been run against the
            #   keyspace.
            try:
                log.info('Creating Schema Migrations table')
                session.execute(SimpleStatement(
                    """
                    CREATE TABLE schema_migrations (
                        scriptname text,
                        time timestamp,
                        run boolean,
                        failed boolean,
                        error text,
                        content text,
                        PRIMARY KEY (scriptname, time)
                        )
                    """, consistency_level=ConsistencyLevel.QUORUM))
            except Exception as e:
                log.info('Failed to create Schema Migrations table (Ignoring)')
                log.debug(str(e))

        if not DB.tableExists(session.keyspace, 'schema_migration_requests'):
            # Create schema_migration_requests table. This table is used to
            #   manage and coordinate multiple nodes requesting schema
            #   update/migrations in order to ensure only one attempts to alter
            #   schema at any time.
            try:
                log.info('Creating Schema Migration Requests table')
                session.execute(SimpleStatement(
                    """
                    CREATE TABLE schema_migration_requests (
                        reqid uuid,
                        reqtime timestamp,
                        inprogress boolean,
                        failed boolean,
                        lastupdate timestamp,
                        PRIMARY KEY (reqid)
                        )
                    """, consistency_level=ConsistencyLevel.QUORUM))
            except Exception as e:
                log.info('Failed to create Schema Migration Requests ' +
                         'table (Ignoring)')
                log.debug(str(e))

        # Just to prevent race conditions on creation and read
        time.sleep(1)

        # Request migration tasks
        DB.requestMigration(session)
Пример #8
0
 def getUserSessions(org, username, session=None):
     getUserSessionQuery = CassandraCluster.getPreparedStatement(
         """
         SELECT * FROM usersessions
         WHERE org = ?
         AND username = ?
         """, keyspace=session.keyspace)
     return session.execute(getUserSessionQuery,
                            (org, username)).current_rows
Пример #9
0
 def createDB(keyspace, replication_class, replication_factor,
              consistency=ConsistencyLevel.QUORUM):
     session = CassandraCluster.getSession()
     log.info('Creating Keyspace "%s"' % (keyspace,))
     session.execute(SimpleStatement(
         """
         CREATE KEYSPACE %s WITH replication
             = {'class': '%s', 'replication_factor': %s};
         """ % (keyspace, replication_class, replication_factor),
         consistency_level=consistency))
Пример #10
0
    def getOrg(org, session=None):
        """
        Retrieve an org from the authdb.orgs table

        :org:
            Name of organization the user belongs to
        """
        getOrgQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT * FROM orgs
            WHERE org = ?
            """, keyspace=session.keyspace)
        return session.execute(getOrgQuery, (org,))
Пример #11
0
    def tableExists(keyspace, table):
        """
        Determine if the given table exists in the keyspace

        :keyspace:
            The keyspace to check for the table
        :table:
            Table to check for
        """
        if keyspace is None or table is None:
            return False

        session = CassandraCluster.getSession('system')

        lookuptable = CassandraCluster.getPreparedStatement("""
            SELECT columnfamily_name FROM schema_columnfamilies
                WHERE keyspace_name=? and columnfamily_name=?
        """, keyspace=session.keyspace)
        table_count = len(session.execute(lookuptable,
                                          (keyspace, table))
                          .current_rows)

        return table_count == 1
Пример #12
0
    def getGlobalSetting(setting, session=None):
        """
        Get a setting/property for system from the authdb.globalsettings
        table.

        :setting:
            Setting/property name
        """
        getGlobalSettingQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT value FROM globalsettings
            WHERE setting = ?
            """, keyspace=session.keyspace)

        return session.execute(getGlobalSettingQuery, (setting,))
Пример #13
0
 def get(self, username, org):
     """
     Retrieve basic user record information.
     """
     try:
         session = CassandraCluster.getSession(
             config['cassandra']['auth_keyspace'])
         getUserQuery = CassandraCluster.getPreparedStatement(
             """
             SELECT username, org, parentuser, createdate FROM users
             WHERE org = ?
             AND username = ?
             """, keyspace=session.keyspace)
         results = session.execute(getUserQuery,
                                   (org, username)).current_rows
     except Exception as e:
         log.error('Exception on User/get: %s' % str(e))
         return {'ServerError': 500, 'Message':
                 'There was an error fulfiling your request'}, 500
     if len(results) == 0:
         return {'Message':
                 'No user matched "%s"@"%s"' % (username, org)}, 404
     elif len(results) == 1:
         # dict(zip(n._fields, list(n)))
         user = {
                 'username': results[0].username,
                 'org': results[0].org,
                 'parentuser': str(results[0].parentuser),
                 'createdate': str(results[0].createdate)
                 }
         if results[0].parentuser is not None:
             user['parentuser'] = results[0].parentuser
         return user
     else:
         return {'RequestError': 400, 'Message':
                 'Request returned too many results'}, 400
Пример #14
0
    def getUser(org, username, session=None):
        """
        Retrieve a user from the authdb.users table

        :org:
            Name of organization the user belongs to
        :username:
            Name of the user
        """
        getUserQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT username, org, parentuser, createdate FROM users
            WHERE org = ?
            AND username = ?
            """, keyspace=session.keyspace)
        return session.execute(getUserQuery, (org, username))
Пример #15
0
    def getPasswordReset(org, username, session=None):
        """
        Retrieve a password reset request from the authdb.userpasswordresets
        table

        :org:
            Name of organization the user belongs to
        :username:
            Name of the user
        """
        getPasswordResetQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT username, org, requestdate, resetid FROM userpasswordresets
            WHERE org = ?
            AND username = ?
            """, keyspace=session.keyspace)
        return session.execute(getPasswordResetQuery, (org, username))
Пример #16
0
    def getOrgSetting(org, setting, session=None):
        """
        Get a setting/property for an organization from the authdb.orgsettings
        table.

        :org:
            Name of organization
        :setting:
            Setting/property name
        """
        checkOrgSetting = CassandraCluster.getPreparedStatement(
            """
            SELECT value FROM orgsettings
            WHERE org = ?
            AND setting = ?
            """, keyspace=session.keyspace)
        return session.execute(checkOrgSetting, (org, setting))
Пример #17
0
    def waitForMigrationCompletion(session):
        """
        Wait for a migration task running on another node to complete

        :keyspace:
            Keyspace to wait to complete migrating
        """

        migrationsRunning = True
        migrationsFailedOrStalled = False

        migrationRequestsQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT * FROM schema_migration_requests
            """, keyspace=session.keyspace)

        log.info('Waiting for migrations to complete on "%s"' %
                 (session.keyspace,))

        while migrationsRunning:
            time.sleep(0.5)
            migrationRequests = session.execute(migrationRequestsQuery)\
                .current_rows

            if len(migrationRequests) == 0:
                # No Migrations running/requested, we're finished waiting
                migrationsRunning = False
                break

            # Check for stale or failed migrations
            staleTime = datetime.datetime.now() - datetime.timedelta(minutes=1)
            for req in migrationRequests:
                if (req.failed or
                        (req.inprogress and req.lastupdate < staleTime)):
                    # We found a failed or stale request (that had started),
                    #   we should re-request a migration
                    migrationsRunning = False
                    migrationsFailedOrStalled = True

        log.info('Finished waiting for migration of "%s"' % (session.keyspace,))
        if migrationsFailedOrStalled:
            log.warning('Detected failed migration of "%s", ' %
                        (session.keyspace,) +
                        'will re-request migration')
            DB.requestMigration(session)
Пример #18
0
    def updateReq(session, reqid):
        """
        Update the lastupdate time on a reqid

        :keyspace:
            Keyspace the reqid applies to
        :reqid:
            ID of the request to be updated
        """

        t = datetime.datetime.now()
        reqUpdateQuery = CassandraCluster.getPreparedStatement(
            """
            UPDATE schema_migration_requests
            SET lastupdate = ?
            WHERE reqid = ?
            """, keyspace=session.keyspace)
        session.execute(reqUpdateQuery, (t, reqid))
Пример #19
0
    def setGlobalSetting(setting, value,
                         consistency=ConsistencyLevel.LOCAL_QUORUM,
                         session=None):
        """
        Set a global setting/property in the authdb.globalsettings table

        :setting:
            Setting/property name
        :value:
            Value of the setting
        :consistency:
            Cassandra consistency level. Defaults to LOCAL_QUORUM.
        """
        setGlobalSettingQuery = CassandraCluster.getPreparedStatement(
            """
            INSERT INTO globalsettings (setting, value)
            VALUES (?, ?)
            """, keyspace=session.keyspace)
        setGlobalSettingQuery.consistency_level = consistency
        session.execute(setGlobalSettingQuery, (setting, value))
Пример #20
0
    def getUserSalt(org, username, session=None):
        """
        Retrieve a user's salt from the authdb.users table

        :org:
            Name of organization the user belongs to
        :username:
            Name of the user
        """
        getUserSaltQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT salt FROM users
            WHERE org = ?
            AND username = ?
            """, keyspace=session.keyspace)
        res = session.execute(getUserSaltQuery, (org, username)).current_rows
        if len(res) > 0:
            return res[0].salt
        else:
            return None
Пример #21
0
    def createOrg(org, parentorg,
                  consistency=ConsistencyLevel.LOCAL_QUORUM,
                  session=None):
        """
        Create an organization in the authdb.orgs table.

        :org:
            Name of the organization
        :parentorg:
            Parent organization for this organization
        :consistency:
            Cassandra consistency level. Defaults to LOCAL_QUORUM.
        """
        createOrgQuery = CassandraCluster.getPreparedStatement(
            """
            INSERT INTO orgs (org, parentorg)
            VALUES (?, ?)
            """, keyspace=session.keyspace)
        createOrgQuery.consistency_level = consistency
        session.execute(createOrgQuery, (org,))
Пример #22
0
    def getUserSessionByKey(sessionKey, session=None):
        """
        Get session record using a session key

        :sessionKey:
            64-character session key for the session
        """
        getUserSessionByKeyQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT sessionid, username, org FROM usersessionkeys
            WHERE sessionkey = ?
            """, keyspace=session.keyspace)
        res = session.execute(getUserSessionByKeyQuery, (sessionKey,))\
            .current_rows
        numRows = len(res)
        if numRows == 1:
            return AuthDB.getUserSession(res[0].org, res[0].username,
                                         res[0].sessionid)
        elif numRows == 0:
            return None
        elif numRows > 1:
            raise ValueError('Multiple sessions returned by key')
Пример #23
0
    def deletePasswordReset(org, username,
                            consistency=ConsistencyLevel.LOCAL_QUORUM,
                            session=None):
        """
        Delete/remove a password reset request for a user

        :org:
            Name of organization the user belongs to
        :username:
            Name of user
        :consistency:
            Cassandra ConsistencyLevel (default LOCAL_QUORUM)
        """
        deletePasswordResetQuery = CassandraCluster.getPreparedStatement(
            """
            DELETE FROM userpasswordresets
            WHERE org = ?
            AND username = ?
            """, keyspace=session.keyspace)
        deletePasswordResetQuery.consistency_level = consistency
        session.execute(deletePasswordResetQuery,
                        (org, username))
Пример #24
0
    def deleteUserSession(org, username, sessionId,
                          consistency=ConsistencyLevel.LOCAL_QUORUM,
                          session=None):
        """
        Delete/remove a session record from AuthDB.usersessions.

        :org:
            Organization the user belongs to
        :username:
            Name of the user
        :sessionId:
            UUID of the session
        :consistency:

        """
        deleteUserSessionQuery = CassandraCluster.getPreparedStatement(
            """
            DELETE FROM usersessions
            WHERE org = ?
            AND username = ?
            AND sessionid = ?
            """, keyspace=session.keyspace)
        deleteUserSessionQuery.consistency_level = consistency
        session.execute(deleteUserSessionQuery, (org, username, sessionId))
Пример #25
0
    def getUserSession(org, username, sessionId, session=None):
        """
        Get Session record

        :org:
            Name of user's organization
        :username:
            Name of user
        :sessionId:
            ID of session to lookup
        """
        getUserSessionQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT * FROM usersessions
            WHERE org = ?
            AND username = ?
            AND sessionid = ?
            """, keyspace=session.keyspace)
        res = session.execute(getUserSessionQuery, (org, username, sessionId))\
            .current_rows
        if len(res) > 0:
            return res[0]
        else:
            return None
Пример #26
0
    def deleteUserSessionByKey(sessionKey,
                               consistency=ConsistencyLevel.LOCAL_QUORUM,
                               session=None):
        """
        Delete/remove a session key from AuthDB.usersessionkeys and remove the
        associated session record from AuthDB.usersessions.

        :sessionKey:
            Key of the session to delete
        :consistency:
            Cassandra ConsistencyLevel (default LOCAL_QUORUM)
        """
        userSession = AuthDB.getUserSessionByKey(sessionKey)
        deleteUserSessionByKeyQuery = CassandraCluster.getPreparedStatement(
            """
            DELETE FROM usersessionkeys
            WHERE sessionkey = ?
            """, keyspace=session.keyspace)
        deleteUserSessionByKeyQuery.consistency_level = consistency
        if userSession is not None:
            AuthDB.deleteUserSession(userSession.org, userSession.username,
                                     userSession.sessionid,
                                     consistency=consistency)
        session.execute(deleteUserSessionByKeyQuery, (sessionKey,))
Пример #27
0
    def migrateSchema(path, session):
        """
        Execute a CQL schema migration script within a keyspace. File will not
        be run if it is marked as successfully run in the schema_migrations
        table within the keyspace.
        """

        # Get the filename part
        filestart = path.rfind('/')+1
        filename = path[filestart:]

        log.info('Checking migration script "%s"' % (filename,))

        # Get the migration history for the script
        migrationScriptHistoryQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT * FROM schema_migrations
            WHERE scriptname = ?;
            """, keyspace=session.keyspace)
        migrationScriptHistory = session.execute(migrationScriptHistoryQuery,
                                                 (filename,)).current_rows

        # Run if there is no history or the last execution failed
        if (len(migrationScriptHistory) == 0 or
                migrationScriptHistory[-1].failed or
                not migrationScriptHistory[-1].run):

            log.info('Running "%s" as it has not been run sucessfully' %
                     (filename,))

            content = open(path).read()
            exectime = datetime.datetime.now()

            # Insert a record of this script into the schema_migrations table
            #   and mark as not run and not failed.
            migrationScriptRunInsert = CassandraCluster.getPreparedStatement(
                """
                INSERT INTO schema_migrations (scriptname, time, run, failed,
                    error, content)
                    VALUES (?, ?, false, false, '', ?)
                """, keyspace=session.keyspace)
            migrationScriptRunInsert.consistency_level = ConsistencyLevel.QUORUM
            session.execute(migrationScriptRunInsert,
                            (filename, exectime, content))

            try:
                # Run the migration script
                session.execute(SimpleStatement(content,
                                consistency_level=ConsistencyLevel.QUORUM))

                log.info('Successfully ran "%s"' % (filename,))

                # Update the script's run record as completed with success
                migrationScriptUpdateSuccess = \
                    CassandraCluster.getPreparedStatement("""
                        UPDATE schema_migrations
                        SET run = true, failed = false
                        WHERE scriptname = ? AND time = ?
                    """, keyspace=session.keyspace)
                migrationScriptUpdateSuccess.consistency_level = \
                    ConsistencyLevel.QUORUM
                session.execute(migrationScriptUpdateSuccess,
                                (filename, exectime))
            except Exception as e:
                log.info('Failed to run "%s"' % (filename,))

                # Log failure
                migrationScriptUpdateFailure = \
                    CassandraCluster.getPreparedStatement(
                        """
                        UPDATE schema_migrations
                        SET run = false, failed = true, error = ?
                        WHERE scriptname = ? AND time = ?
                        """, keyspace=session.keyspace)
                migrationScriptUpdateFailure.consistency_level = \
                    ConsistencyLevel.QUORUM
                session.execute(migrationScriptUpdateFailure,
                                (str(e), filename, exectime))

                # Pass failure upwards
                raise e
        else:
            log.info('Script "%s" has already been run on %s' %
                     (filename, migrationScriptHistory[-1].time))
Пример #28
0
from database.cassandra import CassandraCluster
from flask import Flask
from flask_restful import Resource, Api, reqparse
import sys, uuid

CassandraCluster.setupKeyspace('authdb')

app = Flask(__name__)
api = Api(app)

class Root(Resource):
    def get(self):
        return { 'SUCCESS': 'true', 'From': 'authservicesapi:Root:get' }

api.add_resource(Root, '/')

if __name__ == "__main__":
    app.run(debug=True)
Пример #29
0
 def func_wrapper(*args, **kwargs):
     return func(*args,
                 session=CassandraCluster.getSession(keyspace),
                 **kwargs)
Пример #30
0
    def requestMigration(session=None):
        """
        Request migration tasks on a keyspace, run if selected or wait if not

        :session:
            Session name to request migration task on
        """

        # Pre-fetch these prepared statements as they are used more than once
        deleteReqQuery = CassandraCluster.getPreparedStatement(
            """
            DELETE FROM schema_migration_requests
            WHERE reqid = ?
            """, keyspace=session.keyspace)

        log.info('Checking schema migration requests table')

        migrationRequestsQuery = CassandraCluster.getPreparedStatement(
            """
            SELECT * FROM schema_migration_requests
            """, keyspace=session.keyspace)
        rawMigrationRequests = session.execute(migrationRequestsQuery)\
            .current_rows

        migrationRequests = []
        staleTime = datetime.datetime.now() - datetime.timedelta(minutes=1)

        for req in rawMigrationRequests:
            # A request is "stale" if it is not in progress and it's request
            #   time is older than 1 minute (something happend while waiting),
            #   or if it is in progress but hasn't been updated in more than 1
            #   minute (something happened while it was updating), or if it is
            #   marked as "failed" (something else happened and the update was
            #   aborted).

            if (req.failed or
                    (not req.inprogress and req.reqtime < staleTime) or
                    (req.inprogress and req.lastupdate < staleTime)):

                # Delete the "stale" request (cleanup task)
                try:
                    log.info('Found stale request %s, deleting' % (req.reqid,))
                    session.execute(deleteReqQuery, (req.reqid,))
                except:
                    pass
            else:
                # Keep the record and continue
                migrationRequests.append(req)

        if len(migrationRequests) == 0:
            # No other pending/active migration requests

            reqid = uuid.uuid4()
            t = datetime.datetime.now()

            log.info('No outstanding migration requests, ' +
                     'requesting migration with ID %s' % (reqid,))

            # Nominate ourselves to run migration tasks
            requestMigrationQuery = CassandraCluster.getPreparedStatement(
                """
                INSERT INTO schema_migration_requests (reqid, reqtime,
                    inprogress, failed, lastupdate)
                VALUES (?, ?, false, false, ?)
                """, keyspace=session.keyspace)
            requestMigrationQuery.consistency_level = ConsistencyLevel.QUORUM
            session.execute(requestMigrationQuery, (reqid, t, t))

            time.sleep(2)

            log.info('Checking migration requests table to see if we ' +
                     'are selected for migration')

            # Check and see if we were selected
            migrationRequestsQuery = CassandraCluster.getPreparedStatement(
                """
                SELECT * FROM schema_migration_requests
                """, keyspace=session.keyspace)
            migrationRequests = session.execute(migrationRequestsQuery)\
                .current_rows

            # Sort by request time
            migrationRequests = sorted(migrationRequests,
                                       key=lambda x: x.reqtime)

            if (migrationRequests[0].reqid == reqid):
                # We were selected (only request or first request)

                t = datetime.datetime.now()

                # Mark ourselves as "In Progress"
                markRequestInProgressQuery = CassandraCluster\
                    .getPreparedStatement(
                        """
                        UPDATE schema_migration_requests
                        SET inprogress = true,
                        lastupdate = ?
                        WHERE reqid = ?
                        """, keyspace=session.keyspace)
                session.execute(markRequestInProgressQuery, (t, reqid))

                try:
                    # Run migration
                    DB.doMigration(session, reqid)

                    # Delete our req if successfully completed
                    session.execute(deleteReqQuery, (reqid,))

                    log.info('Migration completed successfully')
                except Exception as e:
                    log.info('Migration failed')

                    # Something went wrong, mark our req as failed
                    t = datetime.datetime.now()
                    reqFailedQuery = CassandraCluster.getPreparedStatement(
                        """
                        UPDATE schema_migration_requests
                        SET lastupdate = ?,
                        failed = true,
                        inprogress = false
                        WHERE reqid = ?
                        """, keyspace=session.keyspace)
                    session.execute(reqFailedQuery, (t, reqid))

                    raise e
            else:
                log.info('Not selected for migration (lost election)')

                # Not selected, delete our request
                session.execute(deleteReqQuery, (reqid,))

                # Wait for selected node to complete the migration
                DB.waitForMigrationCompletion(session)
        else:
            log.info('Not selected for migration (in progress)')

            # Wait for migration to complete
            DB.waitForMigrationCompletion(session)