示例#1
0
    def run(self):
        self.pool = ThreadPool(processes=self.SYNC_WORKERS)
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute('''CREATE TABLE IF NOT EXISTS zvol_calls(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          reply_to TEXT NOT NULL,
                          time INT NOT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvols(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT,
                          iscsi_target TEXT UNIQUE,
                          remotehost TEXT,
                          remotepool TEXT,
                          sync BOOLEAN)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvolattrs(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          frequency INT DEFAULT NULL,
                          nextsync INT DEFAULT NULL,
                          downloadspeed INT DEFAULT NULL,
                          uploadspeed INT DEFAULT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS sync_queue(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT NOT NULL,
                          remotehost TEXT,
                          remotepool TEXT,
                          is_sending BOOLEAN,
                          is_delete_remote BOOLEAN,
                          time INT)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS globals(
                          attr TEXT PRIMARY KEY NOT NULL,
                          value TEXT)''')
            cur.execute('DELETE FROM sync_queue')
            con.commit()
            # Record all parameters in the configuration file in globals
            for key in self.nc.DATA.keys():
                self.setAttr(key, self.nc.DATA[key])
            if self.getAttr('frequency') is None:
                self.setAttr('frequency', self.SYNC_PULL_DEFAULT)

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage',
                                                    'direct', "img-storage", "img-storage",
                                                    self.process_message, lambda a:
                                                    self.startup(),
                                                    routing_key=self.nc.NODE_NAME,
                                                    ssl = True,
                                                    ssl_options = self.ssl_options,
                                                    encryption = self.use_encryption,
                                                    frontend = self.nc.FRONTEND_NAME,
                                                    secur_server = self.secur_server
                                                    )
        self.queue_connector.run()
示例#2
0
    def run(self):
        self.pool = ThreadPool(processes=self.SYNC_WORKERS)
        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            cur.execute('''CREATE TABLE IF NOT EXISTS zvol_calls(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          reply_to TEXT NOT NULL,
                          time INT NOT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvols(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT,
                          iscsi_target TEXT UNIQUE,
                          remotehost TEXT)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS sync_queue(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT NOT NULL,
                          remotehost TEXT,
                          is_sending BOOLEAN,
                          is_delete_remote BOOLEAN,
                          time INT)''')
            con.commit()

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage',
                'direct', "img-storage", "img-storage",
                self.process_message, lambda a: \
                self.startup(),
                routing_key=NodeConfig.NODE_NAME)
        self.queue_connector.run()
    def run(self):
        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            cur.execute('CREATE TABLE IF NOT EXISTS zvol_calls(zvol TEXT PRIMARY KEY NOT NULL, reply_to TEXT NOT NULL, time INT NOT NULL)')
            cur.execute('CREATE TABLE IF NOT EXISTS zvols(zvol TEXT PRIMARY KEY NOT NULL, iscsi_target TEXT UNIQUE, hosting TEXT)')
            con.commit()

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage', 'direct', self.process_message)
        self.queue_connector.run()
示例#4
0
    def run(self):
        self.pool = ThreadPool(processes=self.SYNC_WORKERS)
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute('''CREATE TABLE IF NOT EXISTS zvol_calls(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          reply_to TEXT NOT NULL,
                          time INT NOT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvols(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT,
                          iscsi_target TEXT UNIQUE,
                          remotehost TEXT,
                          remotepool TEXT,
                          sync BOOLEAN)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvolattrs(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          frequency INT DEFAULT NULL,
                          nextsync INT DEFAULT NULL,
                          downloadspeed INT DEFAULT NULL,
                          uploadspeed INT DEFAULT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS sync_queue(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT NOT NULL,
                          remotehost TEXT,
                          remotepool TEXT,
                          is_sending BOOLEAN,
                          is_delete_remote BOOLEAN,
                          time INT)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS globals(
                          attr TEXT PRIMARY KEY NOT NULL,
                          value TEXT)''')
            cur.execute('DELETE FROM sync_queue')
            con.commit()
            # Record all parameters in the configuration file in globals
            for key in self.nc.DATA.keys():
                self.setAttr(key, self.nc.DATA[key])
            if self.getAttr('frequency') is None:
                self.setAttr('frequency', self.SYNC_PULL_DEFAULT)

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage',
                                                    'direct', "img-storage", "img-storage",
                                                    self.process_message, lambda a:
                                                    self.startup(),
                                                    routing_key=self.nc.NODE_NAME)
        self.queue_connector.run()
示例#5
0
class NasDaemon:

    def __init__(self):
        # FIXME: user should be configurable at startup
        self.imgUser = '******'
        self.stdin_path = '/dev/null'
        self.stdout_path = '/tmp/out.log'
        self.stderr_path = '/tmp/err.log'
        self.pidfile_path = '/var/run/img-storage-nas.pid'
        self.pidfile_timeout = 5
        self.function_dict = {
            'map_zvol': self.map_zvol,
            'unmap_zvol': self.unmap_zvol,
            'zvol_mapped': self.zvol_mapped,
            'zvol_unmapped': self.zvol_unmapped,
            'list_zvols': self.list_zvols,
            'del_zvol': self.del_zvol,
            'get_zvol_attrs': self.get_zvol_attrs,
            'set_zvol_attrs': self.set_zvol_attrs,
            'get_attrs': self.get_attrs,
            'set_attrs': self.set_attrs,
            'del_attrs': self.del_attrs,
            'zvol_synced': self.zvol_synced,
        }

        self.nc = NodeConfig.NodeConfig()
        self.SQLITE_DB = '/opt/rocks/var/img_storage.db'
        self.NODE_NAME = self.nc.NODE_NAME
        self.ib_net = self.nc.SYNC_NETWORK

        self.sync_result = None

        self.results = {}
        if self.nc.IMG_SYNC_WORKERS:
            self.SYNC_WORKERS = int(self.nc.IMG_SYNC_WORKERS)
        else:
            self.SYNC_WORKERS = 5

        self.SYNC_CHECK_TIMEOUT = 10
        # try once/minute to add new jobs to the sync queue
        self.SYNC_PULL_TIMEOUT = 60
        # the default SYNC_PULL is 5 minutes
        self.SYNC_PULL_DEFAULT = 60 * 5

        self.logger = \
            logging.getLogger('imgstorage.imgstoragenas.NasDaemon')

        self.ZVOLATTRS = ['frequency', 'nextsync',
                          'downloadspeed', 'uploadspeed']

    def dbconnect(self):
        """ connect to sqlite3 database, turn on foreign constraints """
        con = sqlite3.connect(self.SQLITE_DB)
        cur = con.cursor()
        cur.execute('PRAGMA foreign_keys=ON')
        return con

    def getZvolAttr(self, zvol, attr=None):
        """ Return a single named attribute for a zvol, or a dictionary of
                attributes if attr=None """
        rval = None
        with self.dbconnect() as con:
            cur = con.cursor()
            if attr is not None:
                cur.execute("SELECT %s FROM zvolattrs WHERE zvol='%s'" %
                            (attr, zvol))
                row = cur.fetchone()
                if row != None:
                    rval = row[0]
            else:
                cur.execute("SELECT * from zvolattrs WHERE zvol='%s'" % zvol)
                row = cur.fetchone()
                if row != None:
                    rval = dict((cur.description[i][0], value)
                                for (i, value) in enumerate(row))

        return rval

    def setZvolAttr(self, zvol, attr, value=None):
        """ Set a single named attribute for a zvol. Set to Null of value is None """
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute(
                'SELECT count(*) FROM zvolattrs WHERE zvol = ?', [zvol])
            if cur.fetchone()[0] == 0:
                cur.execute('INSERT INTO zvolattrs(zvol) VALUES(?)', [zvol])
            if value is None:
                setStmt = "SET %s=NULL" % attr
            elif isinstance(value, int):
                setStmt = "SET %s=%d" % (attr, value)
            else:
                setStmt = "SET %s='%s'" % (attr, value)
            cur.execute(""" UPDATE zvolattrs %s WHERE  zvol='%s'""" %
                        (setStmt, zvol))
            con.commit()

    def getAttr(self, attr=None):
        """ Return a single named global attribute or a dictionary of
                all attributes if attr=None. All values are type string """
        rval = None
        with self.dbconnect() as con:
            cur = con.cursor()
            if attr is not None:
                cur.execute("SELECT value FROM globals WHERE attr='%s'" %
                            (attr))
                row = cur.fetchone()
                if row != None:
                    rval = row[0]
            else:
                cur.execute("SELECT attr,value FROM globals")
                rval = {}
                for row in cur.fetchall():
                    attr, value = row
                    rval[attr] = value
        return rval

    def setAttr(self, attr, value=None):
        """ Set a single named attribute.Set to Null of value is None """
        with self.dbconnect() as con:
            if value is not None:
                value = str(value)
            cur = con.cursor()
            cur.execute('''INSERT OR REPLACE INTO globals(attr,value)
                               VALUES(?,?)''', [attr, value] )
            con.commit()

    def deleteAttr(self, attr):
        """ delete single named attribute """
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute('''DELETE FROM globals WHERE attr="%s"''' % attr )
            con.commit()

    # Attribute get/set messages

    def get_zvol_attrs(self, message, props):
        #  input: get_zvol_attr messages
        #  output:  get_zvol_attr message --> requestor

        zvol = message['zvol']
        try:
            attrs = self.getZvolAttr(zvol)
            attrs['zvol'] = zvol
        except Exception as err:
            self.failAction(props.reply_to, 'get_zvol_attrs', str(err))
            return
        reply = json.dumps({'action': 'get_zvol_attrs', 'status': 'success',
                            'body': attrs})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def set_zvol_attrs(self, message, props):
        #  input: set_zvol_attr message
        #  output:  set_zvol_attr message --> requestor
        #  state updates: zvols attr table

        zvol = message['zvol']
        try:
            for k in message.keys():
                if k in self.ZVOLATTRS:
                    self.setZvolAttr(zvol, k, message[k])
        except Exception as err:
            self.failAction(props.reply_to, 'set_zvol_attrs', str(err))
            return

        reply = json.dumps({'action': 'set_zvol_attrs', 'status': 'success'})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def get_attrs(self, message, props):
        #  input: get_attrs messages
        #  output:  get_attrs message --> requestor

        try:
            attrs = self.getAttr()
        except Exception as err:
            self.failAction(props.reply_to, 'get_attrs', str(err))
            return
        reply = json.dumps({'action': 'get_attrs', 'status': 'success',
                            'attrs': attrs})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def set_attrs(self, message, props):
        #  input: set_attrs message
        #  output:  set_attrs message --> requestor
        #  state updates: attrs table

        try:
            attrs = message['attrs']
            for k in attrs.keys():
                self.setAttr(k, attrs[k])
        except Exception as err:
            self.failAction(props.reply_to, 'set_attrs', str(err))
            return

        reply = json.dumps({'action': 'set_attrs', 'status': 'success'})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def del_attrs(self, message, props):
        #  input: del_attrs message
        #  output:  del_attrs message --> requestor
        #  state updates: attrs table

        try:
            for attr in message['attrs']:
                self.deleteAttr(attr)
        except Exception as err:
            self.failAction(props.reply_to, 'del_attrs', str(err))
            return

        reply = json.dumps({'action': 'del_attrs', 'status': 'success'})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    ############   Main Run Method ###################

    def run(self):
        self.pool = ThreadPool(processes=self.SYNC_WORKERS)
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute('''CREATE TABLE IF NOT EXISTS zvol_calls(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          reply_to TEXT NOT NULL,
                          time INT NOT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvols(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT,
                          iscsi_target TEXT UNIQUE,
                          remotehost TEXT,
                          remotepool TEXT,
                          sync BOOLEAN)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvolattrs(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          frequency INT DEFAULT NULL,
                          nextsync INT DEFAULT NULL,
                          downloadspeed INT DEFAULT NULL,
                          uploadspeed INT DEFAULT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS sync_queue(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT NOT NULL,
                          remotehost TEXT,
                          remotepool TEXT,
                          is_sending BOOLEAN,
                          is_delete_remote BOOLEAN,
                          time INT)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS globals(
                          attr TEXT PRIMARY KEY NOT NULL,
                          value TEXT)''')
            cur.execute('DELETE FROM sync_queue')
            con.commit()
            # Record all parameters in the configuration file in globals
            for key in self.nc.DATA.keys():
                self.setAttr(key, self.nc.DATA[key])
            if self.getAttr('frequency') is None:
                self.setAttr('frequency', self.SYNC_PULL_DEFAULT)

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage',
                                                    'direct', "img-storage", "img-storage",
                                                    self.process_message, lambda a:
                                                    self.startup(),
                                                    routing_key=self.nc.NODE_NAME)
        self.queue_connector.run()

    def failAction(
        self,
        routing_key,
        action,
        error_message,
    ):
        if routing_key != None and action != None:
            self.queue_connector.publish_message(json.dumps({'action': action,
                                                             'status': 'error', 'error': error_message}),
                                                 exchange='', routing_key=routing_key)
        self.logger.error('Failed %s: %s' % (action, error_message))

    def startup(self):
        self.schedule_zvols_pull()
        self.schedule_next_sync()

    @coroutine
    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']

        # 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('%s.%s'
                                                  % (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),
                    ip,
                ]))

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

                # 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': ('%s.%s' % (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))
示例#6
0
class NasDaemon:

    def __init__(self):
        # FIXME: user should be configurable at startup
        self.imgUser = '******'
        self.stdin_path = '/dev/null'
        self.stdout_path = '/tmp/out.log'
        self.stderr_path = '/tmp/err.log'
        self.pidfile_path = '/var/run/img-storage-nas.pid'
        self.pidfile_timeout = 5
        self.function_dict = {
            'map_zvol': self.map_zvol,
            'unmap_zvol': self.unmap_zvol,
            'zvol_mapped': self.zvol_mapped,
            'zvol_unmapped': self.zvol_unmapped,
            'list_zvols': self.list_zvols,
            'del_zvol': self.del_zvol,
            'get_zvol_attrs': self.get_zvol_attrs,
            'set_zvol_attrs': self.set_zvol_attrs,
            'get_attrs': self.get_attrs,
            'set_attrs': self.set_attrs,
            'del_attrs': self.del_attrs,
            'zvol_synced': self.zvol_synced,
        }

        self.nc = NodeConfig.NodeConfig()
        self.SQLITE_DB = '/opt/rocks/var/img_storage.db'
        self.NODE_NAME = self.nc.NODE_NAME
        self.ib_net = self.nc.SYNC_NETWORK

        self.sync_result = None

        self.results = {}
        if self.nc.IMG_SYNC_WORKERS:
            self.SYNC_WORKERS = int(self.nc.IMG_SYNC_WORKERS)
        else:
            self.SYNC_WORKERS = 5

        self.SYNC_CHECK_TIMEOUT = 10
        # try once/minute to add new jobs to the sync queue
        self.SYNC_PULL_TIMEOUT = 60
        # the default SYNC_PULL is 5 minutes
        self.SYNC_PULL_DEFAULT = 60 * 5

        self.ssl_options = self.nc.DATA.get("ssl_options", False)
        if(self.ssl_options):
            self.ssl_options = json.loads(self.ssl_options)
        self.use_encryption = self.nc.DATA.get("use_encryption", False)
        self.secur_server = self.nc.DATA.get("secur_server", False)

        self.logger = \
            logging.getLogger('imgstorage.imgstoragenas.NasDaemon')

        self.ZVOLATTRS = ['frequency', 'nextsync',
                          'downloadspeed', 'uploadspeed']

    def dbconnect(self):
        """ connect to sqlite3 database, turn on foreign constraints """
        con = sqlite3.connect(self.SQLITE_DB)
        cur = con.cursor()
        cur.execute('PRAGMA foreign_keys=ON')
        return con

    def getZvolAttr(self, zvol, attr=None):
        """ Return a single named attribute for a zvol, or a dictionary of
                attributes if attr=None """
        rval = None
        with self.dbconnect() as con:
            cur = con.cursor()
            if attr is not None:
                cur.execute("SELECT %s FROM zvolattrs WHERE zvol='%s'" %
                            (attr, zvol))
                row = cur.fetchone()
                if row != None:
                    rval = row[0]
            else:
                cur.execute("SELECT * from zvolattrs WHERE zvol='%s'" % zvol)
                row = cur.fetchone()
                if row != None:
                    rval = dict((cur.description[i][0], value)
                                for (i, value) in enumerate(row))

        return rval

    def setZvolAttr(self, zvol, attr, value=None):
        """ Set a single named attribute for a zvol. Set to Null of value is None """
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute(
                'SELECT count(*) FROM zvolattrs WHERE zvol = ?', [zvol])
            if cur.fetchone()[0] == 0:
                cur.execute('INSERT INTO zvolattrs(zvol) VALUES(?)', [zvol])
            if value is None:
                setStmt = "SET %s=NULL" % attr
            elif isinstance(value, int):
                setStmt = "SET %s=%d" % (attr, value)
            else:
                setStmt = "SET %s='%s'" % (attr, value)
            cur.execute(""" UPDATE zvolattrs %s WHERE  zvol='%s'""" %
                        (setStmt, zvol))
            con.commit()

    def getAttr(self, attr=None):
        """ Return a single named global attribute or a dictionary of
                all attributes if attr=None. All values are type string """
        rval = None
        with self.dbconnect() as con:
            cur = con.cursor()
            if attr is not None:
                cur.execute("SELECT value FROM globals WHERE attr='%s'" %
                            (attr))
                row = cur.fetchone()
                if row != None:
                    rval = row[0]
            else:
                cur.execute("SELECT attr,value FROM globals")
                rval = {}
                for row in cur.fetchall():
                    attr, value = row
                    rval[attr] = value
        return rval

    def setAttr(self, attr, value=None):
        """ Set a single named attribute.Set to Null of value is None """
        with self.dbconnect() as con:
            if value is not None:
                value = str(value)
            cur = con.cursor()
            cur.execute('''INSERT OR REPLACE INTO globals(attr,value)
                               VALUES(?,?)''', [attr, value] )
            con.commit()

    def deleteAttr(self, attr):
        """ delete single named attribute """
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute('''DELETE FROM globals WHERE attr="%s"''' % attr )
            con.commit()

    # Attribute get/set messages

    def get_zvol_attrs(self, message, props):
        #  input: get_zvol_attr messages
        #  output:  get_zvol_attr message --> requestor

        zvol = message['zvol']
        try:
            attrs = self.getZvolAttr(zvol)
            attrs['zvol'] = zvol
        except Exception as err:
            self.failAction(props.reply_to, 'get_zvol_attrs', str(err))
            return
        reply = json.dumps({'action': 'get_zvol_attrs', 'status': 'success',
                            'body': attrs})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def set_zvol_attrs(self, message, props):
        #  input: set_zvol_attr message
        #  output:  set_zvol_attr message --> requestor
        #  state updates: zvols attr table

        zvol = message['zvol']
        try:
            for k in message.keys():
                if k in self.ZVOLATTRS:
                    self.setZvolAttr(zvol, k, message[k])
        except Exception as err:
            self.failAction(props.reply_to, 'set_zvol_attrs', str(err))
            return

        reply = json.dumps({'action': 'set_zvol_attrs', 'status': 'success'})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def get_attrs(self, message, props):
        #  input: get_attrs messages
        #  output:  get_attrs message --> requestor

        try:
            attrs = self.getAttr()
        except Exception as err:
            self.failAction(props.reply_to, 'get_attrs', str(err))
            return
        reply = json.dumps({'action': 'get_attrs', 'status': 'success',
                            'attrs': attrs})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def set_attrs(self, message, props):
        #  input: set_attrs message
        #  output:  set_attrs message --> requestor
        #  state updates: attrs table

        try:
            attrs = message['attrs']
            for k in attrs.keys():
                self.setAttr(k, attrs[k])
        except Exception as err:
            self.failAction(props.reply_to, 'set_attrs', str(err))
            return

        reply = json.dumps({'action': 'set_attrs', 'status': 'success'})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    def del_attrs(self, message, props):
        #  input: del_attrs message
        #  output:  del_attrs message --> requestor
        #  state updates: attrs table

        try:
            for attr in message['attrs']:
                self.deleteAttr(attr)
        except Exception as err:
            self.failAction(props.reply_to, 'del_attrs', str(err))
            return

        reply = json.dumps({'action': 'del_attrs', 'status': 'success'})
        self.queue_connector.publish_message(reply, exchange='',
                                             routing_key=props.reply_to)

    ############   Main Run Method ###################

    def run(self):
        self.pool = ThreadPool(processes=self.SYNC_WORKERS)
        with self.dbconnect() as con:
            cur = con.cursor()
            cur.execute('''CREATE TABLE IF NOT EXISTS zvol_calls(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          reply_to TEXT NOT NULL,
                          time INT NOT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvols(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT,
                          iscsi_target TEXT UNIQUE,
                          remotehost TEXT,
                          remotepool TEXT,
                          sync BOOLEAN)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvolattrs(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          frequency INT DEFAULT NULL,
                          nextsync INT DEFAULT NULL,
                          downloadspeed INT DEFAULT NULL,
                          uploadspeed INT DEFAULT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS sync_queue(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT NOT NULL,
                          remotehost TEXT,
                          remotepool TEXT,
                          is_sending BOOLEAN,
                          is_delete_remote BOOLEAN,
                          time INT)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS globals(
                          attr TEXT PRIMARY KEY NOT NULL,
                          value TEXT)''')
            cur.execute('DELETE FROM sync_queue')
            con.commit()
            # Record all parameters in the configuration file in globals
            for key in self.nc.DATA.keys():
                self.setAttr(key, self.nc.DATA[key])
            if self.getAttr('frequency') is None:
                self.setAttr('frequency', self.SYNC_PULL_DEFAULT)

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage',
                                                    'direct', "img-storage", "img-storage",
                                                    self.process_message, lambda a:
                                                    self.startup(),
                                                    routing_key=self.nc.NODE_NAME,
                                                    ssl = True,
                                                    ssl_options = self.ssl_options,
                                                    encryption = self.use_encryption,
                                                    frontend = self.nc.FRONTEND_NAME,
                                                    secur_server = self.secur_server
                                                    )
        self.queue_connector.run()

    def failAction(
        self,
        routing_key,
        action,
        error_message,
    ):
        if routing_key != None and action != None:
            self.queue_connector.publish_message(json.dumps({'action': action,
                                                             'status': 'error', 'error': error_message}),
                                                 exchange='', routing_key=routing_key)
        self.logger.error('Failed %s: %s' % (action, error_message))

    def startup(self):
        self.schedule_zvols_pull()
        self.schedule_next_sync()

    @coroutine
    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))
class NasDaemon():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/tmp/out.log'
        self.stderr_path = '/tmp/err.log'
        self.pidfile_path =  '/var/run/img-storage-nas.pid'
        self.pidfile_timeout = 5
        self.function_dict = {'set_zvol':self.set_zvol, 'tear_down':self.tear_down, 'zvol_attached':self.zvol_attached, 'zvol_detached': self.zvol_detached, 'list_zvols': self.list_zvols, 'del_zvol': self.del_zvol }

        self.ZPOOL = 'tank'
        self.SQLITE_DB = '/opt/rocks/var/img_storage.db'
        self.NODE_NAME = RabbitMQLocator().NODE_NAME

        self.logger = logging.getLogger(__name__)

        db = rocks.db.helper.DatabaseHelper()
        db.connect()
        self.ib_net = db.getHostAttr(db.getHostname(), 'IB_net')
        db.close()

    def run(self):
        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            cur.execute('CREATE TABLE IF NOT EXISTS zvol_calls(zvol TEXT PRIMARY KEY NOT NULL, reply_to TEXT NOT NULL, time INT NOT NULL)')
            cur.execute('CREATE TABLE IF NOT EXISTS zvols(zvol TEXT PRIMARY KEY NOT NULL, iscsi_target TEXT UNIQUE, hosting TEXT)')
            con.commit()

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage', 'direct', self.process_message)
        self.queue_connector.run()

    def failAction(self, routing_key, action, error_message):
        if(routing_key != None and action != None):
            self.queue_connector.publish_message({'action': action, 'status': 'error', 'error':error_message}, exchange='', routing_key=routing_key)
        self.logger.error("Failed %s: %s"%(action, error_message))

    """
    Received set_zvol command from frontend, passing to compute node
    """
    def set_zvol(self, message, props):
        hosting = message['hosting']
        zvol_name = message['zvol']
        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])
                if(cur.fetchone()[0] == 0):
                    runCommand(['zfs', 'create', '-o', 'primarycache=metadata', '-o', 'volblocksize=128K', '-V', '%sgb'%message['size'], '%s/%s'%(self.ZPOOL, zvol_name)])
                    cur.execute('INSERT OR REPLACE INTO zvols VALUES (?,?,?) ',(zvol_name, None, None))
                    con.commit()
                    self.logger.debug('Created new zvol %s'%zvol_name)

                cur.execute('SELECT iscsi_target 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('%s.%s'%(hosting, self.ib_net))
                        use_ib = True
                    except:
                        pass

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

                iscsi_target = ''

                out = runCommand(['tgt-setup-lun', '-n', zvol_name, '-d', '/dev/%s/%s'%(self.ZPOOL, zvol_name), ip])
                for line in out:
                    if "Creating new target" in line:
                        iscsi_target = line['Creating new target (name='.__len__():line.index(',')]
                self.logger.debug('Mapped %s to iscsi target %s'%(zvol_name, iscsi_target))

                cur.execute('INSERT OR REPLACE INTO zvols VALUES (?,?,?) ',(zvol_name, iscsi_target,hosting))
                con.commit()

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

                self.queue_connector.publish_message(
                        {'action': 'set_zvol', 'target':iscsi_target, 'nas': ('%s.%s'%(self.NODE_NAME, self.ib_net)) if use_ib else self.NODE_NAME},
                        hosting,
                        self.NODE_NAME,
                        on_fail=lambda: failDeliver(iscsi_target, zvol_name, props.reply_to, hosting))
                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_attached', str(err))
示例#8
0
class NasDaemon:

    def __init__(self):
        ## FIXME: user should be configurable at startup 
        self.imgUser = '******'    
	self.prefix = "IMG-STORAGE-"

        self.stdin_path = '/dev/null'
        self.stdout_path = '/tmp/out.log'
        self.stderr_path = '/tmp/err.log'
        self.pidfile_path = '/var/run/img-storage-nas.pid'
        self.pidfile_timeout = 5
        self.function_dict = {
            'map_zvol': self.map_zvol,
            'unmap_zvol': self.unmap_zvol,
            'zvol_mapped': self.zvol_mapped,
            'zvol_unmapped': self.zvol_unmapped,
            'list_zvols': self.list_zvols,
            'del_zvol': self.del_zvol,
            'zvol_synced': self.zvol_synced,
            }

        self.SQLITE_DB = '/opt/rocks/var/img_storage.db'
        self.NODE_NAME = NodeConfig.NODE_NAME
        self.ib_net = NodeConfig.IB_NET

        self.sync_result = None

        self.results = {}
        if NodeConfig.IMG_SYNC_WORKERS:
            self.SYNC_WORKERS = int(NodeConfig.IMG_SYNC_WORKERS)
        else:
            self.SYNC_WORKERS = 5

        self.SYNC_CHECK_TIMEOUT = 10
        self.SYNC_PULL_TIMEOUT = 60 * 5

        rocks.db.helper.DatabaseHelper().closeSession()  # to reopen after daemonization

        self.logger = \
            logging.getLogger('imgstorage.imgstoragenas.NasDaemon')

    def run(self):
        self.pool = ThreadPool(processes=self.SYNC_WORKERS)
        with sqlite3.connect(self.SQLITE_DB) as con:
            cur = con.cursor()
            cur.execute('''CREATE TABLE IF NOT EXISTS zvol_calls(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          reply_to TEXT NOT NULL,
                          time INT NOT NULL)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS zvols(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT,
                          iscsi_target TEXT UNIQUE,
                          remotehost TEXT)''')
            cur.execute('''CREATE TABLE IF NOT EXISTS sync_queue(
                          zvol TEXT PRIMARY KEY NOT NULL,
                          zpool TEXT NOT NULL,
                          remotehost TEXT,
                          is_sending BOOLEAN,
                          is_delete_remote BOOLEAN,
                          time INT)''')
            con.commit()

        self.queue_connector = RabbitMQCommonClient('rocks.vm-manage',
                'direct', "img-storage", "img-storage",
                self.process_message, lambda a: \
                self.startup(),
                routing_key=NodeConfig.NODE_NAME)
        self.queue_connector.run()

    def failAction(
        self,
        routing_key,
        action,
        error_message,
        ):
        if routing_key != None and action != None:
            self.queue_connector.publish_message(json.dumps({'action': action,
                    'status': 'error', 'error': error_message}),
                    exchange='', routing_key=routing_key)
        self.logger.error('Failed %s: %s' % (action, error_message))

    def startup(self):
        self.schedule_zvols_pull()
        self.schedule_next_sync()

    @coroutine
    def map_zvol(self, message, props):
        remotehost = message['remotehost']
        zpool_name = message['zpool']
        zvol_name = message['zvol']
        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
                    self.logger.debug('checking if  zvol %s exists' % volume)
                    rcode = subprocess.call(["zfs","list",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)

                    cur.execute('INSERT OR REPLACE INTO zvols VALUES (?,?,?,?) '
                                , (zvol_name, None, None, None))
                    con.commit()
                    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('%s.%s'
                                % (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),
                    ip,
                    ]))

                for line in out:
                    if 'Creating new target' in line:
                        start = 'Creating new target (name='.__len__()
                        iscsi_target = line[start:line.index(',')]
                self.logger.debug('Mapped %s to iscsi target %s'
                                  % (zvol_name, iscsi_target))

                cur.execute('INSERT OR REPLACE INTO zvols VALUES (?,?,?,?) '
                            , (zvol_name, zpool_name, iscsi_target,
                            remotehost))
                con.commit()

                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)

                self.queue_connector.publish_message(json.dumps({
                    'action': 'map_zvol',
                    'target': iscsi_target,
                    'nas': ('%s.%s' % (self.NODE_NAME,
                            self.ib_net) if use_ib else self.NODE_NAME),
                    'size': message['size'],
                    'zvol': zvol_name,
                    }), 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))