def create_share_from_snapshot(self, context, share, snapshot, share_server=None): old_gmgr = self._share_manager(snapshot['share_instance']) # Snapshot clone feature in GlusterFS server essential to support this # API is available in GlusterFS server versions 3.7 and higher. So do # a version check. vers = self.glusterfs_versions[old_gmgr.host_access] minvers = (3, 7) if common.numreduct(vers) < minvers: minvers_str = '.'.join(six.text_type(c) for c in minvers) vers_str = '.'.join(vers) msg = (_("GlusterFS version %(version)s on server %(server)s does " "not support creation of shares from snapshot. " "minimum requirement: %(minversion)s") % { 'version': vers_str, 'server': old_gmgr.host, 'minversion': minvers_str }) LOG.error(msg) raise exception.GlusterfsException(msg) # Clone the snapshot. The snapshot clone, a new GlusterFS volume # would serve as a share. backend_snapshot_name = self._find_actual_backend_snapshot_name( old_gmgr, snapshot) volume = ''.join(['manila-', share['id']]) args_tuple = (('snapshot', 'activate', backend_snapshot_name, 'force', '--mode=script'), ('snapshot', 'clone', volume, backend_snapshot_name)) for args in args_tuple: out, err = old_gmgr.gluster_call( *args, log=("Creating share from snapshot")) # Get a manager for the new volume/share. comp_vol = old_gmgr.components.copy() comp_vol.update({'volume': volume}) gmgr = self._glustermanager(comp_vol) export = self.driver._setup_via_manager( { 'share': share, 'manager': gmgr }, { 'share': snapshot['share_instance'], 'manager': old_gmgr }) argseq = (('set', [USER_CLONED_FROM, snapshot['share_id']]), ('set', [USER_MANILA_SHARE, share['id']]), ('start', [])) for op, opargs in argseq: args = ['volume', op, gmgr.volume] + opargs gmgr.gluster_call(*args, log=("Creating share from snapshot")) self.gluster_used_vols.add(gmgr.qualified) self.private_storage.update(share['id'], {'volume': gmgr.qualified}) return export
def do_setup(self, context): """Setup the GlusterFS volumes.""" glusterfs_versions, exceptions = {}, {} for srvaddr in self.configuration.glusterfs_servers: try: glusterfs_versions[srvaddr] = self._glustermanager( srvaddr, False).get_gluster_version() except exception.GlusterfsException as exc: exceptions[srvaddr] = six.text_type(exc) if exceptions: for srvaddr, excmsg in exceptions.items(): LOG.error( "'gluster version' failed on server " "%(server)s with: %(message)s", { 'server': srvaddr, 'message': excmsg }) raise exception.GlusterfsException( _("'gluster version' failed on servers %s") % (','.join(exceptions.keys()))) notsupp_servers = [] for srvaddr, vers in glusterfs_versions.items(): if common.numreduct(vers) < self.driver.GLUSTERFS_VERSION_MIN: notsupp_servers.append(srvaddr) if notsupp_servers: gluster_version_min_str = '.'.join( six.text_type(c) for c in self.driver.GLUSTERFS_VERSION_MIN) for srvaddr in notsupp_servers: LOG.error( "GlusterFS version %(version)s on server " "%(server)s is not supported, " "minimum requirement: %(minvers)s", { 'server': srvaddr, 'version': '.'.join(glusterfs_versions[srvaddr]), 'minvers': gluster_version_min_str }) raise exception.GlusterfsException( _("Unsupported GlusterFS version on servers %(servers)s, " "minimum requirement: %(minvers)s") % { 'servers': ','.join(notsupp_servers), 'minvers': gluster_version_min_str }) self.glusterfs_versions = glusterfs_versions gluster_volumes_initial = set( self._fetch_gluster_volumes(filter_used=False)) if not gluster_volumes_initial: # No suitable volumes are found on the Gluster end. # Raise exception. msg = (_("Gluster backend does not provide any volume " "matching pattern %s") % self.configuration.glusterfs_volume_pattern) LOG.error(msg) raise exception.GlusterfsException(msg) LOG.info("Found %d Gluster volumes allocated for Manila.", len(gluster_volumes_initial)) self._check_mount_glusterfs()
def create_share_from_snapshot(self, context, share, snapshot, share_server=None): old_gmgr = self._share_manager(snapshot['share_instance']) # Snapshot clone feature in GlusterFS server essential to support this # API is available in GlusterFS server versions 3.7 and higher. So do # a version check. vers = self.glusterfs_versions[old_gmgr.host_access] minvers = (3, 7) if common.numreduct(vers) < minvers: minvers_str = '.'.join(six.text_type(c) for c in minvers) vers_str = '.'.join(vers) msg = (_("GlusterFS version %(version)s on server %(server)s does " "not support creation of shares from snapshot. " "minimum requirement: %(minversion)s") % {'version': vers_str, 'server': old_gmgr.host, 'minversion': minvers_str}) LOG.error(msg) raise exception.GlusterfsException(msg) # Clone the snapshot. The snapshot clone, a new GlusterFS volume # would serve as a share. backend_snapshot_name = self._find_actual_backend_snapshot_name( old_gmgr, snapshot) volume = ''.join(['manila-', share['id']]) args_tuple = (('snapshot', 'activate', backend_snapshot_name, 'force', '--mode=script'), ('snapshot', 'clone', volume, backend_snapshot_name)) for args in args_tuple: out, err = old_gmgr.gluster_call( *args, log=("Creating share from snapshot")) # Get a manager for the new volume/share. comp_vol = old_gmgr.components.copy() comp_vol.update({'volume': volume}) gmgr = self._glustermanager(comp_vol) export = self.driver._setup_via_manager( {'share': share, 'manager': gmgr}, {'share': snapshot['share_instance'], 'manager': old_gmgr}) argseq = (('set', [USER_CLONED_FROM, snapshot['share_id']]), ('set', [USER_MANILA_SHARE, share['id']]), ('start', [])) for op, opargs in argseq: args = ['volume', op, gmgr.volume] + opargs gmgr.gluster_call(*args, log=("Creating share from snapshot")) self.gluster_used_vols.add(gmgr.qualified) self.private_storage.update(share['id'], {'volume': gmgr.qualified}) return export
def do_setup(self, context): """Setup the GlusterFS volumes.""" glusterfs_versions, exceptions = {}, {} for srvaddr in self.configuration.glusterfs_servers: try: glusterfs_versions[srvaddr] = self._glustermanager( srvaddr, False).get_gluster_version() except exception.GlusterfsException as exc: exceptions[srvaddr] = six.text_type(exc) if exceptions: for srvaddr, excmsg in exceptions.items(): LOG.error("'gluster version' failed on server " "%(server)s with: %(message)s", {'server': srvaddr, 'message': excmsg}) raise exception.GlusterfsException(_( "'gluster version' failed on servers %s") % ( ','.join(exceptions.keys()))) notsupp_servers = [] for srvaddr, vers in glusterfs_versions.items(): if common.numreduct(vers) < self.driver.GLUSTERFS_VERSION_MIN: notsupp_servers.append(srvaddr) if notsupp_servers: gluster_version_min_str = '.'.join( six.text_type(c) for c in self.driver.GLUSTERFS_VERSION_MIN) for srvaddr in notsupp_servers: LOG.error("GlusterFS version %(version)s on server " "%(server)s is not supported, " "minimum requirement: %(minvers)s", {'server': srvaddr, 'version': '.'.join(glusterfs_versions[srvaddr]), 'minvers': gluster_version_min_str}) raise exception.GlusterfsException(_( "Unsupported GlusterFS version on servers %(servers)s, " "minimum requirement: %(minvers)s") % { 'servers': ','.join(notsupp_servers), 'minvers': gluster_version_min_str}) self.glusterfs_versions = glusterfs_versions gluster_volumes_initial = set( self._fetch_gluster_volumes(filter_used=False)) if not gluster_volumes_initial: # No suitable volumes are found on the Gluster end. # Raise exception. msg = (_("Gluster backend does not provide any volume " "matching pattern %s" ) % self.configuration.glusterfs_volume_pattern) LOG.error(msg) raise exception.GlusterfsException(msg) LOG.info("Found %d Gluster volumes allocated for Manila.", len(gluster_volumes_initial)) self._check_mount_glusterfs()
def _wipe_gluster_vol(self, gluster_mgr): # Create a temporary mount. gluster_export = gluster_mgr.export tmpdir = tempfile.mkdtemp() try: common._mount_gluster_vol(self.driver._execute, gluster_export, tmpdir) except exception.GlusterfsException: shutil.rmtree(tmpdir, ignore_errors=True) raise # Delete the contents of a GlusterFS volume that is temporarily # mounted. # From GlusterFS version 3.7, two directories, '.trashcan' at the root # of the GlusterFS volume and 'internal_op' within the '.trashcan' # directory, are internally created when a GlusterFS volume is started. # GlusterFS does not allow unlink(2) of the two directories. So do not # delete the paths of the two directories, but delete their contents # along with the rest of the contents of the volume. srvaddr = gluster_mgr.host_access if common.numreduct(self.glusterfs_versions[srvaddr]) < (3, 7): cmd = ['find', tmpdir, '-mindepth', '1', '-delete'] else: ignored_dirs = map(lambda x: os.path.join(tmpdir, *x), [('.trashcan', ), ('.trashcan', 'internal_op')]) ignored_dirs = list(ignored_dirs) cmd = [ 'find', tmpdir, '-mindepth', '1', '!', '-path', ignored_dirs[0], '!', '-path', ignored_dirs[1], '-delete' ] try: self.driver._execute(*cmd, run_as_root=True) except exception.ProcessExecutionError as exc: msg = (_("Error trying to wipe gluster volume. " "gluster_export: %(export)s, Error: %(error)s") % { 'export': gluster_export, 'error': exc.stderr }) LOG.error(msg) raise exception.GlusterfsException(msg) finally: # Unmount. common._umount_gluster_vol(self.driver._execute, tmpdir) shutil.rmtree(tmpdir, ignore_errors=True)
def create_snapshot(self, context, snapshot, share_server=None): """Creates a snapshot.""" gluster_mgr = self._share_manager(snapshot['share']) if gluster_mgr.qualified in self.gluster_nosnap_vols_dict: opret, operrno = -1, 0 operrstr = self.gluster_nosnap_vols_dict[gluster_mgr.qualified] else: args = ('--xml', 'snapshot', 'create', 'manila-' + snapshot['id'], gluster_mgr.volume) out, err = gluster_mgr.gluster_call(*args, log=("Retrieving volume info")) if not out: raise exception.GlusterfsException( 'gluster volume info %s: no data received' % gluster_mgr.volume) outxml = etree.fromstring(out) opret = int(common.volxml_get(outxml, 'opRet')) operrno = int(common.volxml_get(outxml, 'opErrno')) operrstr = common.volxml_get(outxml, 'opErrstr', default=None) if opret == -1: vers = self.glusterfs_versions[gluster_mgr.host_access] if common.numreduct(vers) > (3, 6): # This logic has not yet been implemented in GlusterFS 3.6 if operrno == 0: self.gluster_nosnap_vols_dict[ gluster_mgr.qualified] = operrstr msg = _("Share %(share_id)s does not support snapshots: " "%(errstr)s.") % { 'share_id': snapshot['share_id'], 'errstr': operrstr } LOG.error(msg) raise exception.ShareSnapshotNotSupported(msg) raise exception.GlusterfsException( _("Creating snapshot for share %(share_id)s failed " "with %(errno)d: %(errstr)s") % { 'share_id': snapshot['share_id'], 'errno': operrno, 'errstr': operrstr })
def create_snapshot(self, context, snapshot, share_server=None): """Creates a snapshot.""" gluster_mgr = self._share_manager(snapshot['share']) if gluster_mgr.qualified in self.gluster_nosnap_vols_dict: opret, operrno = -1, 0 operrstr = self.gluster_nosnap_vols_dict[gluster_mgr.qualified] else: args = ('--xml', 'snapshot', 'create', 'manila-' + snapshot['id'], gluster_mgr.volume) out, err = gluster_mgr.gluster_call( *args, log=("Retrieving volume info")) if not out: raise exception.GlusterfsException( 'gluster volume info %s: no data received' % gluster_mgr.volume ) outxml = etree.fromstring(out) opret = int(common.volxml_get(outxml, 'opRet')) operrno = int(common.volxml_get(outxml, 'opErrno')) operrstr = common.volxml_get(outxml, 'opErrstr', default=None) if opret == -1: vers = self.glusterfs_versions[gluster_mgr.host_access] if common.numreduct(vers) > (3, 6): # This logic has not yet been implemented in GlusterFS 3.6 if operrno == 0: self.gluster_nosnap_vols_dict[ gluster_mgr.qualified] = operrstr msg = _("Share %(share_id)s does not support snapshots: " "%(errstr)s.") % {'share_id': snapshot['share_id'], 'errstr': operrstr} LOG.error(msg) raise exception.ShareSnapshotNotSupported(msg) raise exception.GlusterfsException( _("Creating snapshot for share %(share_id)s failed " "with %(errno)d: %(errstr)s") % { 'share_id': snapshot['share_id'], 'errno': operrno, 'errstr': operrstr})
def _wipe_gluster_vol(self, gluster_mgr): # Create a temporary mount. gluster_export = gluster_mgr.export tmpdir = tempfile.mkdtemp() try: common._mount_gluster_vol(self.driver._execute, gluster_export, tmpdir) except exception.GlusterfsException: shutil.rmtree(tmpdir, ignore_errors=True) raise # Delete the contents of a GlusterFS volume that is temporarily # mounted. # From GlusterFS version 3.7, two directories, '.trashcan' at the root # of the GlusterFS volume and 'internal_op' within the '.trashcan' # directory, are internally created when a GlusterFS volume is started. # GlusterFS does not allow unlink(2) of the two directories. So do not # delete the paths of the two directories, but delete their contents # along with the rest of the contents of the volume. srvaddr = gluster_mgr.host_access if common.numreduct(self.glusterfs_versions[srvaddr]) < (3, 7): cmd = ['find', tmpdir, '-mindepth', '1', '-delete'] else: ignored_dirs = map(lambda x: os.path.join(tmpdir, *x), [('.trashcan', ), ('.trashcan', 'internal_op')]) ignored_dirs = list(ignored_dirs) cmd = ['find', tmpdir, '-mindepth', '1', '!', '-path', ignored_dirs[0], '!', '-path', ignored_dirs[1], '-delete'] try: self.driver._execute(*cmd, run_as_root=True) except exception.ProcessExecutionError as exc: msg = (_("Error trying to wipe gluster volume. " "gluster_export: %(export)s, Error: %(error)s") % {'export': gluster_export, 'error': exc.stderr}) LOG.error(msg) raise exception.GlusterfsException(msg) finally: # Unmount. common._umount_gluster_vol(self.driver._execute, tmpdir) shutil.rmtree(tmpdir, ignore_errors=True)
def test_numreduct(self, vers): ret = common.numreduct(vers) self.assertEqual((3, 6), ret)