def test_allocate(tmpdir, monkeypatch): # Test that allocate call made correctly monkeypatch.setattr(fallocate, '_FALLOCATE', '../helpers/fallocate') size = 4096 image = str(tmpdir.join("image")) fallocate.allocate(image, size).run() allocated = os.stat(image).st_blocks * 512 assert allocated == size
def test_allocate(self): # Test that allocate call made correctly size = 4096 with namedTemporaryDir() as tmpdir: image = os.path.join(tmpdir, "image") fallocate.allocate(image, size).run() allocated = os.stat(image).st_blocks * 512 self.assertEqual(allocated, size)
def test_resize(self): # Test that resize call actually works size = 4096 with temporaryPath(data=b'x' * size) as image: fallocate.allocate(image, size, offset=size).run() with io.open(image, 'rb') as f: actual = f.read() expected = b'x' * size + b'\0' * size self.assertEqual(expected, actual) allocated = os.stat(image).st_blocks * 512 self.assertEqual(allocated, size * 2)
def _extendSizeRaw(self, new_capacity): volPath = self.getVolumePath() cur_capacity = self.oop.os.stat(volPath).st_size # No real sanity checks here, they should be included in the calling # function/method. We just validate the sizes to be consistent since # they're computed and used in the pre-allocated case. if new_capacity == cur_capacity: return # Nothing to do elif cur_capacity <= 0: raise se.StorageException("Volume capacity is impossible: %s" % cur_capacity) elif new_capacity < cur_capacity: raise se.VolumeResizeValueError(new_capacity) if self.getType() == sc.PREALLOCATED_VOL: self.log.info("Preallocating volume %s to %s", volPath, new_capacity) op = fallocate.allocate(volPath, new_capacity - cur_capacity, offset=cur_capacity) with vars.task.abort_callback(op.abort): with utils.stopwatch("Preallocating volume {}".format(volPath), level=logging.INFO, log=self.log): op.run() else: # for sparse files we can just truncate to the correct size # also good fallback for failed preallocation self.log.info("Truncating volume %s to %s", volPath, new_capacity) self.oop.truncateFile(volPath, new_capacity)
def _allocate_volume(cls, vol_path, size, preallocate): try: # Always create sparse image, since qemu-img create uses # posix_fallocate() which is inefficient and harmful. op = qemuimg.create(vol_path, size=size, format=qemuimg.FORMAT.RAW) # This is fast but it can get stuck if storage is inaccessible. with vars.task.abort_callback(op.abort): with utils.stopwatch("Creating image {}".format(vol_path), level=logging.INFO, log=cls.log): op.run() # If the image is preallocated, allocate the rest of the image # using fallocate helper. qemu-img create always writes zeroes to # the first block so we should skip it during preallocation. if preallocate == sc.PREALLOCATED_VOL: op = fallocate.allocate(vol_path, size - 4096, offset=4096) # This is fast on NFS 4.2, GlusterFS, XFS and ext4, but can be # extremely slow on NFS < 4.2, writing zeroes to entire image. with vars.task.abort_callback(op.abort): with utils.stopwatch( "Preallocating volume {}".format(vol_path), level=logging.INFO, log=cls.log): op.run() except exception.ActionStopped: raise except Exception: cls.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(vol_path)
def _extendSizeRaw(self, newSize): volPath = self.getVolumePath() curSizeBytes = self.oop.os.stat(volPath).st_size newSizeBytes = newSize * BLOCK_SIZE # No real sanity checks here, they should be included in the calling # function/method. We just validate the sizes to be consistent since # they're computed and used in the pre-allocated case. if newSizeBytes == curSizeBytes: return # Nothing to do elif curSizeBytes <= 0: raise se.StorageException( "Volume size is impossible: %s" % curSizeBytes) elif newSizeBytes < curSizeBytes: raise se.VolumeResizeValueError(newSize) if self.getType() == sc.PREALLOCATED_VOL: self.log.info("Preallocating volume %s to %s bytes", volPath, newSizeBytes) operation = fallocate.allocate(volPath, newSizeBytes - curSizeBytes, curSizeBytes) with vars.task.abort_callback(operation.abort): with utils.stopwatch("Preallocating volume %s" % volPath): operation.run() else: # for sparse files we can just truncate to the correct size # also good fallback for failed preallocation self.log.info("Truncating volume %s to %s bytes", volPath, newSizeBytes) self.oop.truncateFile(volPath, newSizeBytes)
def _extendSizeRaw(self, newSize): volPath = self.getVolumePath() curSizeBytes = self.oop.os.stat(volPath).st_size newSizeBytes = newSize * BLOCK_SIZE # No real sanity checks here, they should be included in the calling # function/method. We just validate the sizes to be consistent since # they're computed and used in the pre-allocated case. if newSizeBytes == curSizeBytes: return # Nothing to do elif curSizeBytes <= 0: raise se.StorageException("Volume size is impossible: %s" % curSizeBytes) elif newSizeBytes < curSizeBytes: raise se.VolumeResizeValueError(newSize) if self.getType() == sc.PREALLOCATED_VOL: self.log.info("Preallocating volume %s to %s bytes", volPath, newSizeBytes) operation = fallocate.allocate(volPath, newSizeBytes - curSizeBytes, curSizeBytes) with vars.task.abort_callback(operation.abort): with utils.stopwatch("Preallocating volume %s" % volPath): operation.run() else: # for sparse files we can just truncate to the correct size # also good fallback for failed preallocation self.log.info("Truncating volume %s to %s bytes", volPath, newSizeBytes) self.oop.truncateFile(volPath, newSizeBytes)
def _create(cls, dom, imgUUID, volUUID, size, volFormat, preallocate, volParent, srcImgUUID, srcVolUUID, volPath, initialSize=None): """ Class specific implementation of volumeCreate. All the exceptions are properly handled and logged in volume.create() """ if initialSize: cls.log.error("initialSize is not supported for file-based " "volumes") raise se.InvalidParameterException("initial size", initialSize) sizeBytes = size * BLOCK_SIZE truncSize = sizeBytes if volFormat == sc.RAW_FORMAT else 0 try: oop.getProcessPool(dom.sdUUID).truncateFile( volPath, truncSize, mode=sc.FILE_VOLUME_PERMISSIONS, creatExcl=True) except OSError as e: if e.errno == errno.EEXIST: raise se.VolumeAlreadyExists(volUUID) raise if preallocate == sc.PREALLOCATED_VOL: try: operation = fallocate.allocate(volPath, sizeBytes) with vars.task.abort_callback(operation.abort): with utils.stopwatch("Preallocating volume %s" % volPath): operation.run() except exception.ActionStopped: raise except Exception: cls.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(volPath) if not volParent: cls.log.info("Request to create %s volume %s with size = %s " "sectors", sc.type2name(volFormat), volPath, size) if volFormat == sc.COW_FORMAT: qemuimg.create(volPath, size=sizeBytes, format=sc.fmt2str(volFormat), qcow2Compat=dom.qcow2_compat()) else: # Create hardlink to template and its meta file cls.log.info("Request to create snapshot %s/%s of volume %s/%s", imgUUID, volUUID, srcImgUUID, srcVolUUID) volParent.clone(volPath, volFormat) # Forcing the volume permissions in case one of the tools we use # (dd, qemu-img, etc.) will mistakenly change the file permissiosn. dom.oop.os.chmod(volPath, sc.FILE_VOLUME_PERMISSIONS) return (volPath,)
def _fallocate_volume(cls, vol_path, size): try: operation = fallocate.allocate(vol_path, size) with vars.task.abort_callback(operation.abort): with utils.stopwatch("Preallocating volume %s" % vol_path): operation.run() except exception.ActionStopped: raise except Exception: cls.log.error("Unexpected error", exc_info=True) raise se.VolumesZeroingError(vol_path)
def test_resize(tmpdir, monkeypatch): # Test that resize call actually works monkeypatch.setattr(fallocate, '_FALLOCATE', '../helpers/fallocate') size = 4096 image = str(tmpdir.join("image")) with io.open(image, "wb") as f: f.write(b'x' * size) fallocate.allocate(image, size, offset=size).run() with io.open(image, 'rb') as f: actual = f.read() expected = b'x' * size + b'\0' * size assert expected == actual allocated = os.stat(image).st_blocks * 512 assert allocated == size * 2
def test_zero_size(self): # Test that fallocate call throws exception on error with namedTemporaryDir() as tmpdir: image = os.path.join(tmpdir, "image") with self.assertRaises(cmdutils.Error): fallocate.allocate(image, 0).run()
def test_zero_size(tmpdir, monkeypatch): # Test that fallocate call throws exception on error monkeypatch.setattr(fallocate, '_FALLOCATE', '../helpers/fallocate') image = str(tmpdir.join("image")) with pytest.raises(cmdutils.Error): fallocate.allocate(image, 0).run()