def del_zvol(self, message, props):
        #  input: del_zvol message
        #  output:  zvol_deleted message --> requestor
        #  state updates: zvols table, Entry deleted

        zvol_name = message['zvol']
        zpool_name = message['zpool']
        self.logger.debug('Deleting zvol %s' % zvol_name)
        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            try:
                self.lock_zvol(zvol_name, props.reply_to)
                cur.execute(
                    'SELECT remotehost, iscsi_target FROM zvols WHERE zvol = ?',
                    [zvol_name])
                row = cur.fetchone()
                if row == None:
                    raise ActionError('ZVol %s not found in database' %
                                      zvol_name)
                if row[0] != None:
                    raise ActionError('Error deleting zvol %s: is mapped' %
                                      zvol_name)

                self.logger.debug('Invoking zfs destroy %s/%s' %
                                  (zpool_name, zvol_name))
                yield runCommandBackground([
                    'zfs', 'destroy',
                    '%s/%s' % (zpool_name, zvol_name), '-r'
                ])
                self.logger.debug('zfs destroy success %s' % zvol_name)

                cur.execute('DELETE FROM zvolattrs WHERE zvol = ?',
                            [zvol_name])
                cur.execute('DELETE FROM zvols WHERE zvol = ?', [zvol_name])
                con.commit()

                self.release_zvol(zvol_name)
                self.queue_connector.publish_message(
                    json.dumps({
                        'action': 'zvol_deleted',
                        'status': 'success'
                    }),
                    exchange='',
                    routing_key=props.reply_to)
            except ActionError, err:
                if not isinstance(err, ZvolBusyActionError):
                    self.release_zvol(zvol_name)
                self.failAction(props.reply_to, 'zvol_deleted', str(err))
示例#2
0
def runCommandBackground(cmdlist, shell=False):
    """
    Wrapper around subprocess call using Tornado's Subprocess class.
    This routine can fork a process in the background without blocking the
    main IOloop, the the forked process can run for a long time without
    problem
    """

    LOG = logging.getLogger('imgstorage.imgstoragenas.NasDaemon')
    LOG.debug('Executing: ' + str(cmdlist))

    # tornado.process.initialize()

    sub_process = tornado.process.Subprocess(cmdlist, stdout=STREAM,
                                             stderr=STREAM, shell=shell)

    # we need to set_exit_callback to fetch the return value
    # the function can even be empty by it must be set or the
    # sub_process.returncode will be always None

    retval = 0
    sub_process.set_exit_callback(lambda value: value)

    (result, error) = \
        (yield [Task(sub_process.stdout.read_until_close),
                Task(sub_process.stderr.read_until_close)])

    if sub_process.returncode:
        raise ActionError('Error executing %s: %s' % (cmdlist, error))

    raise Return((result.splitlines(), error))
示例#3
0
 def my_side_effect(*args, **kwargs):
     if args[0][:3] == ['iscsiadm', '-m', 'session']:
         return StringIO(iscsiadm_session_response % (target, bdev))
     elif args[0][:3] == ['iscsiadm', '-m', 'discovery']:
         return StringIO(iscsiadm_discovery_response %
                         target)  # find remote targets
     elif args[0][:3] == ['iscsiadm', '-m', 'node']:
         raise ActionError('Some error happened')
    def schedule_next_sync(self):
        try:
            with self.dbconnect() as con:
                cur = con.cursor()

                for (zvol, job_result) in self.results.items():
                    if job_result.ready():
                        del self.results[zvol]
                        cur.execute(
                            '''SELECT 
                                       remotehost, is_sending, zvol, zpool, is_delete_remote,remotepool
                                       FROM sync_queue 
                                       WHERE zvol = ?''', [zvol])
                        row = cur.fetchone()

                        try:
                            if not row:
                                raise ActionError(
                                    'Not found record for %s in sync_queue table'
                                    % zvol)

                            (remotehost, is_sending, zvol, zpool,
                             is_delete_remote, remotepool) = row

                            #self.logger.debug('Sync %s is ready' % zvol)

                            job_result.get(
                            )  # will raise exception is there was one during job execution
                            if is_sending:
                                cur.execute(
                                    'SELECT iscsi_target FROM zvols WHERE zvol = ?',
                                    [zvol])
                                target = cur.fetchone()[0]
                                self.queue_connector.publish_message(
                                    json.dumps({
                                        'action': 'sync_zvol',
                                        'zvol': zvol,
                                        'target': target
                                    }),
                                    remotehost,
                                    self.NODE_NAME,
                                    on_fail=lambda: self.logger.error(
                                        'Compute node %s is unavailable to sync zvol %s'
                                        % (remotehost, zvol
                                           )))  # reply back to compute node
                            elif is_delete_remote:
                                cur.execute(
                                    'UPDATE zvols SET remotehost = NULL, remotepool = NULL where zvol = ?',
                                    [zvol])
                                con.commit()
                                self.release_zvol(zvol)
                        except ActionError, msg:

                            self.logger.exception(
                                'Error performing sync for %s: %s' %
                                (zvol, str(msg)))
                        finally:
    def zvol_unmapped(self, message, props):
        target = message['target']
        zvol = message['zvol']
        self.logger.debug('Got zvol %s unmapped message' % target)

        reply_to = None

        try:
            with sqlite3.connect(self.SQLITE_DB) as con:
                cur = con.cursor()

                # get request destination

                cur.execute(
                    '''SELECT reply_to, zpool, remotepool, sync 
                                FROM zvol_calls 
                                JOIN zvols 
                                ON zvol_calls.zvol = zvols.zvol 
                                WHERE zvols.zvol = ?''', [zvol])
                [reply_to, zpool, remotepool, sync] = cur.fetchone()

                if message['status'] == 'error':
                    raise ActionError(
                        'Error detaching iSCSI target from compute node: %s' %
                        message.get('error'))

                if not sync:
                    self.detach_target(target, True)
                    self.release_zvol(zvol)
                else:
                    self.detach_target(target, False)
                    cur.execute(
                        'UPDATE sync_queue SET is_delete_remote = 1 WHERE zvol = ?',
                        [zvol])
                    if cur.rowcount == 0:
                        cur.execute(
                            '''INSERT INTO 
                            sync_queue(zvol,zpool,remotehost,remotepool,is_sending,
                            is_delete_remote, time)  
                            VALUES(?,?,?,?,0,1,?)''', [
                                zvol, zpool, props.reply_to, remotepool,
                                time.time()
                            ])
                    con.commit()

                self.queue_connector.publish_message(json.dumps({
                    'action':
                    'zvol_unmapped',
                    'status':
                    'success'
                }),
                                                     exchange='',
                                                     routing_key=reply_to)
        except ActionError, err:

            self.release_zvol(zvol)
            self.failAction(reply_to, 'zvol_unmapped', str(err))
    def unmap_zvol(self, message, props):
        #  input: unmap_zvol message
        #  output:  unmap_zvol message --> remotehost
        #  state updates: None

        zvol_name = message['zvol']
        self.logger.debug('Tearing down zvol %s' % zvol_name)

        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            try:
                cur.execute(
                    'SELECT remotehost, iscsi_target FROM zvols WHERE zvol = ?',
                    [zvol_name])
                row = cur.fetchone()
                if row == None:
                    raise ActionError('ZVol %s not found in database' %
                                      zvol_name)
                (remotehost, target) = row
                if remotehost == None:
                    raise ActionError('ZVol %s is not mapped' % zvol_name)

                self.lock_zvol(zvol_name, props.reply_to)
                self.queue_connector.publish_message(
                    json.dumps({
                        'action': 'unmap_zvol',
                        'target': target,
                        'zvol': zvol_name
                    }),
                    remotehost,
                    self.NODE_NAME,
                    on_fail=lambda: self.failAction(
                        props.reply_to, 'zvol_unmapped',
                        'Compute node %s is unavailable' % remotehost))
                self.logger.debug('Tearing down zvol %s sent' % zvol_name)
            except ActionError, err:

                if not isinstance(err, ZvolBusyActionError):
                    self.release_zvol(zvol_name)
                    self.failAction(props.reply_to, 'zvol_unmapped', str(err))
                else:
                    return False
    def zvol_mapped(self, message, props):
        target = message['target']

        zvol = None
        reply_to = None

        self.logger.debug('Got zvol mapped message %s' % target)
        with sqlite3.connect(self.SQLITE_DB) as con:
            try:
                cur = con.cursor()
                cur.execute(
                    '''SELECT zvol_calls.reply_to,
                        zvol_calls.zvol, zvols.zpool, zvols.sync FROM zvol_calls
                        JOIN zvols ON zvol_calls.zvol = zvols.zvol
                        WHERE zvols.iscsi_target = ?''', [target])
                (reply_to, zvol, zpool, sync) = cur.fetchone()

                if message['status'] != 'success':
                    raise ActionError(
                        'Error attaching iSCSI target to compute node: %s' %
                        message.get('error'))

                if not sync:
                    self.release_zvol(zvol)
                else:
                    cur.execute('DELETE FROM sync_queue WHERE zvol = ?',
                                [zvol])
                    cur.execute(
                        '''INSERT INTO 
                                   sync_queue(zvol,zpool,remotehost,is_sending,is_delete_remote,time,remotepool)
                                    SELECT zvol,?,?,1,1,?,remotepool 
                                    FROM zvols 
                                    WHERE iscsi_target = ? ''',
                        [zpool, props.reply_to,
                         time.time(), target])
                    con.commit()

                self.queue_connector.publish_message(json.dumps({
                    'action':
                    'zvol_mapped',
                    'bdev':
                    message['bdev'],
                    'status':
                    'success'
                }),
                                                     exchange='',
                                                     routing_key=reply_to)
            except ActionError, err:
                self.release_zvol(zvol)
                self.failAction(reply_to, 'zvol_mapped', str(err))
示例#8
0
    def map_zvol(self, message, props):
        #  input: map_zvol message
        #  output:  map_zvol message --> remotehost
        #  state updates: zvol table

        remotehost = message['remotehost']
        remotepool = message['remotepool']
        zpool_name = message['zpool']
        zvol_name = message['zvol']
        sync = message['sync']
        try:
             initiator = message['initiator']
        except:
             raise ActionError(
                'Error when setting up: no initiator specified')


        # print "XXX map_zvol (message): ", message
        self.logger.debug('Setting zvol %s' % zvol_name)

        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            try:
                self.lock_zvol(zvol_name, props.reply_to)
                cur.execute(
                    'SELECT count(*) FROM zvols WHERE zvol = ?', [zvol_name])

                volume = "%s/%s" % (zpool_name, zvol_name)
                if cur.fetchone()[0] == 0:

                    """ Create a zvol, if it doesn't already exist """
                    # check if volume already exists
                    # print 'XXX checking if  zvol %s exists' % volume
                    self.logger.debug('checking if  zvol %s exists' % volume)
                    rcode = subprocess.call(["zfs", "list", volume])
                    # print 'XXX check complete (%s)' % volume
                    self.logger.debug('check complete (%s)' % volume)
                    if rcode != 0:
                        # create the zfs FS
                        yield runCommandBackground(zfs_create
                                                   + ['-V', '%sgb' % message['size'], volume])
                        self.logger.debug('Created new zvol %s' % volume)
                    else:
                        self.logger.debug('Vol %s exists' % volume)

                    # Record the creation of the volume
                    cur.execute('''INSERT OR REPLACE INTO
                                zvols(zvol,zpool,iscsi_target,remotehost,remotepool,sync)
                                VALUES (?,?,?,?,?,?) '''
                                , (zvol_name, zpool_name, None, None, None, False))
                    con.commit()
                    # print 'XXX Created new zvol %s' % volume
                    self.logger.debug('Created new zvol %s' % volume)

                cur.execute(
                    'SELECT remotehost FROM zvols WHERE zvol = ?', [zvol_name])
                row = cur.fetchone()
                if row != None and row[0] != None:  # zvol is mapped
                    raise ActionError(
                        'Error when mapping zvol: already mapped')

                ip = None
                use_ib = False

                if self.ib_net:
                    try:
                        ip = socket.gethostbyname(nodenameToDomain(remotehost, self.ib_net))
                        use_ib = True
                    except:
                        pass

                if not use_ib:
                    try:
                        ip = socket.gethostbyname(remotehost)
                    except:
                        raise ActionError('Host %s is unknown'
                                          % remotehost)

                iscsi_target = ''
                (out, err) = (yield runCommandBackground([
                    '/opt/rocks/bin/tgt-setup-lun-lock',
                    '-n',
                    zvol_name,
                    '-d',
                    '/dev/%s/%s' % (zpool_name, zvol_name),
                    initiator,
                ]))

                for line in out:
                    if 'Creating new target' in line:
                        start = 'Creating new target (name='.__len__()
                        iscsi_target = line[start:line.index(',')]
                for line in out:
                    if 'Created Target' in line:
                        iscsi_target = line.split()[-1]

                # print 'XXX Mapped %s to iscsi target %s' % (zvol_name,
                # iscsi_target)
                self.logger.debug('Mapped %s to iscsi target %s'
                                  % (zvol_name, iscsi_target))

                # Update the Zvols table with the target, remote and sync
                # attributes
                cur.execute('''INSERT OR REPLACE INTO
                            zvols(zvol,zpool,iscsi_target,remotehost, remotepool,sync)
                            VALUES (?,?,?,?,?,?) '''
                            , (zvol_name, zpool_name, iscsi_target, remotehost, remotepool, sync))
                con.commit()

                # print 'XXX iscsi target %s inserted into DB' % iscsi_target

                def failDeliver(
                    target,
                    zvol,
                    reply_to,
                    remotehost,
                ):
                    self.detach_target(target, True)
                    self.failAction(props.reply_to, 'zvol_mapped',
                                    'Compute node %s is unavailable'
                                    % remotehost)
                    self.release_zvol(zvol_name)

                # Send a map_zvol message to remotehost
                # XXX: Should rename this message
                self.queue_connector.publish_message(json.dumps({
                    'action': 'map_zvol',
                    'target': iscsi_target,
                    'nas': (nodenameToDomain(self.NODE_NAME,
                                       self.ib_net) if use_ib else self.NODE_NAME),
                    'size': message['size'],
                    'zvol': zvol_name,
                    'sync': sync,
                    'remotehost': remotehost,
                    'remotepool': remotepool,
                }), remotehost, self.NODE_NAME, on_fail=lambda:
                    failDeliver(iscsi_target, zvol_name,
                                props.reply_to, remotehost))
                self.logger.debug('Setting iscsi %s sent'
                                  % iscsi_target)
            except ActionError, err:
                if not isinstance(err, ZvolBusyActionError):
                    self.release_zvol(zvol_name)
                self.failAction(props.reply_to, 'zvol_mapped', str(err))