Beispiel #1
0
    def test_copy_image_to_volume(self):
        """resize_image common case usage."""
        mox = self._mox
        drv = self._driver

        TEST_IMG_SOURCE = 'foo.img'

        volume = {'size': self.TEST_SIZE_IN_GB, 'name': TEST_IMG_SOURCE}

        def fake_local_path(volume):
            return volume['name']

        self.stubs.Set(drv, 'local_path', fake_local_path)

        mox.StubOutWithMock(image_utils, 'fetch_to_raw')
        image_utils.fetch_to_raw(None, None, None, TEST_IMG_SOURCE)

        mox.StubOutWithMock(image_utils, 'resize_image')
        image_utils.resize_image(TEST_IMG_SOURCE, self.TEST_SIZE_IN_GB)

        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        data = mox_lib.MockAnything()
        data.virtual_size = 1024 ** 3
        image_utils.qemu_img_info(TEST_IMG_SOURCE).AndReturn(data)

        mox.ReplayAll()

        drv.copy_image_to_volume(None, volume, None, None)

        mox.VerifyAll()
    def test_clone_image_cloneableshare_raw(self):
        drv = self._driver
        mox = self.mox
        volume = {'name': 'vol', 'size': '20'}
        mox.StubOutWithMock(drv, '_find_image_in_cache')
        mox.StubOutWithMock(drv, '_is_cloneable_share')
        mox.StubOutWithMock(drv, '_get_mount_point_for_share')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(drv, '_clone_volume')
        mox.StubOutWithMock(drv, '_discover_file_till_timeout')
        mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
        mox.StubOutWithMock(drv, '_resize_image_file')
        mox.StubOutWithMock(drv, '_is_share_vol_compatible')

        drv._find_image_in_cache(IgnoreArg()).AndReturn([])
        drv._is_cloneable_share(IgnoreArg()).AndReturn('127.0.0.1:/share')
        drv._is_share_vol_compatible(IgnoreArg(), IgnoreArg()).AndReturn(True)
        drv._get_mount_point_for_share(IgnoreArg()).AndReturn('/mnt')
        image_utils.qemu_img_info('/mnt/img-id').AndReturn(
            self.get_img_info('raw'))
        drv._clone_volume('img-id',
                          'vol',
                          share='127.0.0.1:/share',
                          volume_id=None)
        drv._get_mount_point_for_share(IgnoreArg()).AndReturn('/mnt')
        drv._discover_file_till_timeout(IgnoreArg()).AndReturn(True)
        drv._set_rw_permissions_for_all('/mnt/vol')
        drv._resize_image_file({'name': 'vol'}, IgnoreArg())

        mox.ReplayAll()
        drv.clone_image(volume, ('nfs://127.0.0.1:/share/img-id', None),
                        'image_id', {})
        mox.VerifyAll()
Beispiel #3
0
    def test_copy_image_to_volume(self):
        """resize_image common case usage."""
        mox = self._mox
        drv = self._driver

        TEST_IMG_SOURCE = "foo.img"

        volume = {"size": self.TEST_SIZE_IN_GB, "name": TEST_IMG_SOURCE}

        def fake_local_path(volume):
            return volume["name"]

        self.stubs.Set(drv, "local_path", fake_local_path)

        mox.StubOutWithMock(image_utils, "fetch_to_raw")
        image_utils.fetch_to_raw(None, None, None, TEST_IMG_SOURCE, mox_lib.IgnoreArg(), size=self.TEST_SIZE_IN_GB)

        mox.StubOutWithMock(image_utils, "resize_image")
        image_utils.resize_image(TEST_IMG_SOURCE, self.TEST_SIZE_IN_GB)

        mox.StubOutWithMock(image_utils, "qemu_img_info")
        data = mox_lib.MockAnything()
        data.virtual_size = 1 * units.GiB
        image_utils.qemu_img_info(TEST_IMG_SOURCE).AndReturn(data)

        mox.ReplayAll()

        drv.copy_image_to_volume(None, volume, None, None)

        mox.VerifyAll()
Beispiel #4
0
    def test_clone_image_cloneableshare_raw(self):
        drv = self._driver
        mox = self.mox
        volume = {'name': 'vol', 'size': '20'}
        mox.StubOutWithMock(drv, '_find_image_in_cache')
        mox.StubOutWithMock(drv, '_is_cloneable_share')
        mox.StubOutWithMock(drv, '_get_mount_point_for_share')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(drv, '_clone_volume')
        mox.StubOutWithMock(drv, '_discover_file_till_timeout')
        mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
        mox.StubOutWithMock(drv, '_resize_image_file')
        mox.StubOutWithMock(drv, '_is_share_vol_compatible')

        drv._find_image_in_cache(IgnoreArg()).AndReturn([])
        drv._is_cloneable_share(IgnoreArg()).AndReturn('127.0.0.1:/share')
        drv._is_share_vol_compatible(IgnoreArg(), IgnoreArg()).AndReturn(True)
        drv._get_mount_point_for_share(IgnoreArg()).AndReturn('/mnt')
        image_utils.qemu_img_info('/mnt/img-id').AndReturn(
            self.get_img_info('raw'))
        drv._clone_volume(
            'img-id', 'vol', share='127.0.0.1:/share', volume_id=None)
        drv._get_mount_point_for_share(IgnoreArg()).AndReturn('/mnt')
        drv._discover_file_till_timeout(IgnoreArg()).AndReturn(True)
        drv._set_rw_permissions_for_all('/mnt/vol')
        drv._resize_image_file({'name': 'vol'}, IgnoreArg())

        mox.ReplayAll()
        drv. clone_image(
            volume, ('nfs://127.0.0.1:/share/img-id', None), 'image_id', {})
        mox.VerifyAll()
Beispiel #5
0
    def test_backup_volume(self):
        self.mox.StubOutWithMock(self._driver, 'db')
        self.mox.StubOutWithMock(self._driver.db, 'volume_get')

        volume = {'id': '2', 'name': self.TEST_VOLNAME}
        self._driver.db.volume_get(context, volume['id']).AndReturn(volume)

        info = imageutils.QemuImgInfo()
        info.file_format = 'raw'
        self.mox.StubOutWithMock(image_utils, 'qemu_img_info')
        image_utils.qemu_img_info(self.TEST_VOLPATH).AndReturn(info)

        self.mox.StubOutWithMock(utils, 'temporary_chown')
        mock_tempchown = self.mox.CreateMockAnything()
        utils.temporary_chown(self.TEST_VOLPATH).AndReturn(mock_tempchown)
        mock_tempchown.__enter__()
        mock_tempchown.__exit__(None, None, None)

        self.mox.StubOutWithMock(fileutils, 'file_open')
        mock_fileopen = self.mox.CreateMockAnything()
        fileutils.file_open(self.TEST_VOLPATH).AndReturn(mock_fileopen)
        mock_fileopen.__enter__()
        mock_fileopen.__exit__(None, None, None)

        backup = {'volume_id': volume['id']}
        mock_servicebackup = self.mox.CreateMockAnything()
        mock_servicebackup.backup(backup, mox_lib.IgnoreArg())

        self.mox.ReplayAll()
        self._driver.backup_volume(context, backup, mock_servicebackup)
        self.mox.VerifyAll()
Beispiel #6
0
    def test_initialize_connection(self):
        (mox, drv) = self._mox, self._driver

        volume = self._simple_volume()
        vol_dir = os.path.join(self.TEST_MNT_POINT_BASE,
                               drv._get_hash_str(self.TEST_QUOBYTE_VOLUME))
        vol_path = os.path.join(vol_dir, volume['name'])

        qemu_img_output = """image: %s
        file format: raw
        virtual size: 1.0G (1073741824 bytes)
        disk size: 173K
        """ % volume['name']
        img_info = imageutils.QemuImgInfo(qemu_img_output)

        mox.StubOutWithMock(drv, 'get_active_image_from_info')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')

        drv.get_active_image_from_info(volume).AndReturn(volume['name'])
        image_utils.qemu_img_info(vol_path).AndReturn(img_info)

        mox.ReplayAll()

        conn_info = drv.initialize_connection(volume, None)

        mox.VerifyAll()

        self.assertEqual(conn_info['data']['format'], 'raw')
        self.assertEqual(conn_info['driver_volume_type'], 'quobyte')
        self.assertEqual(conn_info['data']['name'], volume['name'])
        self.assertEqual(conn_info['mount_point_base'],
                         self.TEST_MNT_POINT_BASE)
Beispiel #7
0
 def _direct_nfs_clone(self, volume, image_location, image_id):
     """Clone directly in nfs share."""
     LOG.info(_("Cloning image %s directly in share"), image_id)
     cloned = False
     image_location = self._construct_image_nfs_url(image_location)
     share = self._is_cloneable_share(image_location)
     if share and self._is_share_eligible(share, volume["size"]):
         LOG.debug(_("Share is cloneable %s"), share)
         volume["provider_location"] = share
         (__, ___, img_file) = image_location.rpartition("/")
         dir_path = self._get_mount_point_for_share(share)
         img_path = "%s/%s" % (dir_path, img_file)
         img_info = image_utils.qemu_img_info(img_path)
         if img_info.file_format == "raw":
             LOG.debug(_("Image is raw %s"), image_id)
             self._clone_volume(img_file, volume["name"], volume_id=None, share=share)
             cloned = True
         else:
             LOG.info(_("Image will locally be converted to raw %s"), image_id)
             dst = "%s/%s" % (dir_path, volume["name"])
             image_utils.convert_image(img_path, dst, "raw")
             data = image_utils.qemu_img_info(dst)
             if data.file_format != "raw":
                 raise exception.InvalidResults(_("Converted to raw, but" " format is now %s") % data.file_format)
             else:
                 cloned = True
                 self._register_image_in_cache(volume, image_id)
     return cloned
Beispiel #8
0
    def test_extend_volume(self):
        (mox, drv) = self._mox, self._driver

        volume = self._simple_volume()

        volume_path = '%s/%s/volume-%s' % (
            self.TEST_MNT_POINT_BASE,
            drv._get_hash_str(self.TEST_QUOBYTE_VOLUME), self.VOLUME_UUID)

        qemu_img_info_output = """image: volume-%s
        file format: qcow2
        virtual size: 1.0G (1073741824 bytes)
        disk size: 473K
        """ % self.VOLUME_UUID

        img_info = imageutils.QemuImgInfo(qemu_img_info_output)

        mox.StubOutWithMock(drv, '_execute')
        mox.StubOutWithMock(drv, 'get_active_image_from_info')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(image_utils, 'resize_image')

        drv.get_active_image_from_info(volume).AndReturn(volume['name'])

        image_utils.qemu_img_info(volume_path).AndReturn(img_info)

        image_utils.resize_image(volume_path, 3)

        mox.ReplayAll()

        drv.extend_volume(volume, 3)

        mox.VerifyAll()
Beispiel #9
0
    def test_copy_volume_from_snapshot(self):
        (mox, drv) = self._mox, self._driver

        mox.StubOutWithMock(image_utils, 'convert_image')
        mox.StubOutWithMock(drv, '_read_info_file')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')

        dest_volume = self._simple_volume(
            'c1073000-0000-0000-0000-0000000c1073')
        src_volume = self._simple_volume()

        vol_dir = os.path.join(self.TEST_MNT_POINT_BASE,
                               drv._get_hash_str(self.TEST_QUOBYTE_VOLUME))
        src_vol_path = os.path.join(vol_dir, src_volume['name'])
        dest_vol_path = os.path.join(vol_dir, dest_volume['name'])
        info_path = os.path.join(vol_dir, src_volume['name']) + '.info'

        snapshot = {
            'volume_name': src_volume['name'],
            'name': 'clone-snap-%s' % src_volume['id'],
            'size': src_volume['size'],
            'volume_size': src_volume['size'],
            'volume_id': src_volume['id'],
            'id': 'tmp-snap-%s' % src_volume['id'],
            'volume': src_volume
        }

        snap_file = dest_volume['name'] + '.' + snapshot['id']
        snap_path = os.path.join(vol_dir, snap_file)

        size = dest_volume['size']

        drv._read_info_file(info_path).AndReturn({
            'active': snap_file,
            snapshot['id']: snap_file
        })

        qemu_img_output = """image: %s
        file format: raw
        virtual size: 1.0G (1073741824 bytes)
        disk size: 173K
        backing file: %s
        """ % (snap_file, src_volume['name'])
        img_info = imageutils.QemuImgInfo(qemu_img_output)

        image_utils.qemu_img_info(snap_path).AndReturn(img_info)

        image_utils.convert_image(src_vol_path,
                                  dest_vol_path,
                                  'raw',
                                  run_as_root=self.execute_as_root)

        drv._set_rw_permissions_for_all(dest_vol_path)

        mox.ReplayAll()

        drv._copy_volume_from_snapshot(snapshot, dest_volume, size)

        mox.VerifyAll()
Beispiel #10
0
    def test_clone_image_cloneableshare_notraw(self):
        drv = self._driver
        mox = self.mox
        volume = {"name": "vol", "size": "20"}
        mox.StubOutWithMock(drv, "_find_image_in_cache")
        mox.StubOutWithMock(drv, "_is_cloneable_share")
        mox.StubOutWithMock(drv, "_get_mount_point_for_share")
        mox.StubOutWithMock(image_utils, "qemu_img_info")
        mox.StubOutWithMock(drv, "_clone_volume")
        mox.StubOutWithMock(drv, "_discover_file_till_timeout")
        mox.StubOutWithMock(drv, "_set_rw_permissions_for_all")
        mox.StubOutWithMock(drv, "_resize_image_file")
        mox.StubOutWithMock(image_utils, "convert_image")
        mox.StubOutWithMock(drv, "_register_image_in_cache")
        mox.StubOutWithMock(drv, "_is_share_vol_compatible")

        drv._find_image_in_cache(IgnoreArg()).AndReturn([])
        drv._is_cloneable_share("nfs://127.0.0.1/share/img-id").AndReturn("127.0.0.1:/share")
        drv._is_share_vol_compatible(IgnoreArg(), IgnoreArg()).AndReturn(True)
        drv._get_mount_point_for_share("127.0.0.1:/share").AndReturn("/mnt")
        image_utils.qemu_img_info("/mnt/img-id").AndReturn(self.get_img_info("notraw"))
        image_utils.convert_image(IgnoreArg(), IgnoreArg(), "raw")
        image_utils.qemu_img_info("/mnt/vol").AndReturn(self.get_img_info("raw"))
        drv._register_image_in_cache(IgnoreArg(), IgnoreArg())
        drv._get_mount_point_for_share("127.0.0.1:/share").AndReturn("/mnt")
        drv._discover_file_till_timeout(IgnoreArg()).AndReturn(True)
        drv._set_rw_permissions_for_all("/mnt/vol")
        drv._resize_image_file({"name": "vol"}, IgnoreArg())

        mox.ReplayAll()
        drv.clone_image(volume, ("nfs://127.0.0.1/share/img-id", None), "image_id")
        mox.VerifyAll()
Beispiel #11
0
    def test_initialize_connection(self):
        (mox, drv) = self._mox, self._driver

        volume = self._simple_volume()
        vol_dir = os.path.join(self.TEST_MNT_POINT_BASE,
                               drv._get_hash_str(self.TEST_QUOBYTE_VOLUME))
        vol_path = os.path.join(vol_dir, volume['name'])

        qemu_img_output = """image: %s
        file format: raw
        virtual size: 1.0G (1073741824 bytes)
        disk size: 173K
        """ % volume['name']
        img_info = imageutils.QemuImgInfo(qemu_img_output)

        mox.StubOutWithMock(drv, 'get_active_image_from_info')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')

        drv.get_active_image_from_info(volume).AndReturn(volume['name'])
        image_utils.qemu_img_info(vol_path).AndReturn(img_info)

        mox.ReplayAll()

        conn_info = drv.initialize_connection(volume, None)

        mox.VerifyAll()

        self.assertEqual(conn_info['data']['format'], 'raw')
        self.assertEqual(conn_info['driver_volume_type'], 'quobyte')
        self.assertEqual(conn_info['data']['name'], volume['name'])
        self.assertEqual(conn_info['mount_point_base'],
                         self.TEST_MNT_POINT_BASE)
Beispiel #12
0
    def test_copy_image_to_volume(self):
        """resize_image common case usage."""
        mox = self._mox
        drv = self._driver

        TEST_IMG_SOURCE = 'foo.img'

        volume = {'size': self.TEST_SIZE_IN_GB, 'name': TEST_IMG_SOURCE}

        def fake_local_path(volume):
            return volume['name']

        self.stubs.Set(drv, 'local_path', fake_local_path)

        mox.StubOutWithMock(image_utils, 'fetch_to_raw')
        image_utils.fetch_to_raw(None, None, None, TEST_IMG_SOURCE)

        mox.StubOutWithMock(image_utils, 'resize_image')
        image_utils.resize_image(TEST_IMG_SOURCE, self.TEST_SIZE_IN_GB)

        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        data = mox_lib.MockAnything()
        data.virtual_size = 1024**3
        image_utils.qemu_img_info(TEST_IMG_SOURCE).AndReturn(data)

        mox.ReplayAll()

        drv.copy_image_to_volume(None, volume, None, None)

        mox.VerifyAll()
Beispiel #13
0
    def test_backup_volume(self):
        self.mox = mox_lib.Mox()
        self._driver.db = self.mox.CreateMockAnything()
        self.mox.StubOutWithMock(self._driver.db, 'volume_get')

        volume = {'id': '2', 'name': self.TEST_VOLNAME}
        self._driver.db.volume_get(context, volume['id']).AndReturn(volume)

        info = imageutils.QemuImgInfo()
        info.file_format = 'raw'
        self.mox.StubOutWithMock(image_utils, 'qemu_img_info')
        image_utils.qemu_img_info(self.TEST_VOLPATH).AndReturn(info)

        self.mox.StubOutWithMock(utils, 'temporary_chown')
        mock_tempchown = mock.MagicMock()
        utils.temporary_chown(self.TEST_VOLPATH).AndReturn(mock_tempchown)

        self.mox.StubOutWithMock(fileutils, 'file_open')
        mock_fileopen = mock.MagicMock()
        fileutils.file_open(self.TEST_VOLPATH).AndReturn(mock_fileopen)

        backup = {'volume_id': volume['id']}
        mock_servicebackup = self.mox.CreateMockAnything()
        mock_servicebackup.backup(backup, mox_lib.IgnoreArg())

        self.mox.ReplayAll()

        self._driver.backup_volume(context, backup, mock_servicebackup)
Beispiel #14
0
 def _direct_nfs_clone(self, volume, image_location, image_id):
     """Clone directly in nfs share."""
     LOG.info(_('Cloning image %s directly in share'), image_id)
     cloned = False
     image_location = self._construct_image_nfs_url(image_location)
     share = self._is_cloneable_share(image_location)
     if share and self._is_share_eligible(share, volume['size']):
         LOG.debug(_('Share is cloneable %s'), share)
         volume['provider_location'] = share
         (__, ___, img_file) = image_location.rpartition('/')
         dir_path = self._get_mount_point_for_share(share)
         img_path = '%s/%s' % (dir_path, img_file)
         img_info = image_utils.qemu_img_info(img_path)
         if img_info.file_format == 'raw':
             LOG.debug(_('Image is raw %s'), image_id)
             self._clone_volume(img_file,
                                volume['name'],
                                volume_id=None,
                                share=share)
             cloned = True
         else:
             LOG.info(_('Image will locally be converted to raw %s'),
                      image_id)
             dst = '%s/%s' % (dir_path, volume['name'])
             image_utils.convert_image(img_path, dst, 'raw')
             data = image_utils.qemu_img_info(dst)
             if data.file_format != "raw":
                 raise exception.InvalidResults(
                     _("Converted to raw, but"
                       " format is now %s") % data.file_format)
             else:
                 cloned = True
                 self._register_image_in_cache(volume, image_id)
     return cloned
Beispiel #15
0
    def test_backup_volume(self):
        self.mox.StubOutWithMock(self._driver, "db")
        self.mox.StubOutWithMock(self._driver.db, "volume_get")

        volume = {"id": "2", "name": self.TEST_VOLNAME}
        self._driver.db.volume_get(context, volume["id"]).AndReturn(volume)

        info = imageutils.QemuImgInfo()
        info.file_format = "raw"
        self.mox.StubOutWithMock(image_utils, "qemu_img_info")
        image_utils.qemu_img_info(self.TEST_VOLPATH).AndReturn(info)

        self.mox.StubOutWithMock(utils, "temporary_chown")
        mock_tempchown = self.mox.CreateMockAnything()
        utils.temporary_chown(self.TEST_VOLPATH).AndReturn(mock_tempchown)
        mock_tempchown.__enter__()
        mock_tempchown.__exit__(None, None, None)

        self.mox.StubOutWithMock(fileutils, "file_open")
        mock_fileopen = self.mox.CreateMockAnything()
        fileutils.file_open(self.TEST_VOLPATH).AndReturn(mock_fileopen)
        mock_fileopen.__enter__()
        mock_fileopen.__exit__(None, None, None)

        backup = {"volume_id": volume["id"]}
        mock_servicebackup = self.mox.CreateMockAnything()
        mock_servicebackup.backup(backup, mox_lib.IgnoreArg())

        self.mox.ReplayAll()
        self._driver.backup_volume(context, backup, mock_servicebackup)
        self.mox.VerifyAll()
Beispiel #16
0
    def test_extend_volume(self):
        (mox, drv) = self._mox, self._driver

        volume = self._simple_volume()

        volume_path = '%s/%s/volume-%s' % (self.TEST_MNT_POINT_BASE,
                                           drv._get_hash_str(
                                               self.TEST_QUOBYTE_VOLUME),
                                           self.VOLUME_UUID)

        qemu_img_info_output = """image: volume-%s
        file format: qcow2
        virtual size: 1.0G (1073741824 bytes)
        disk size: 473K
        """ % self.VOLUME_UUID

        img_info = imageutils.QemuImgInfo(qemu_img_info_output)

        mox.StubOutWithMock(drv, '_execute')
        mox.StubOutWithMock(drv, 'get_active_image_from_info')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(image_utils, 'resize_image')

        drv.get_active_image_from_info(volume).AndReturn(volume['name'])

        image_utils.qemu_img_info(volume_path).AndReturn(img_info)

        image_utils.resize_image(volume_path, 3)

        mox.ReplayAll()

        drv.extend_volume(volume, 3)

        mox.VerifyAll()
Beispiel #17
0
    def _cache_vol_2_2(self, context, vol, image_meta, image_service):
        image_id = image_meta['id']
        # Pull down image and determine if valid
        with image_utils.TemporaryImages.fetch(image_service,
                                               context,
                                               image_id) as tmp_image:
            data = image_utils.qemu_img_info(tmp_image)
            fmt = data.file_format
            if fmt is None:
                raise exception.ImageUnacceptable(
                    reason=_("'qemu-img info' parsing failed."),
                    image_id=image_id)

            backing_file = data.backing_file
            if backing_file is not None:
                raise exception.ImageUnacceptable(
                    image_id=image_id,
                    reason=_("fmt=%(fmt)s backed by:%(backing_file)s")
                    % {'fmt': fmt, 'backing_file': backing_file, })

            vsize = int(
                math.ceil(float(data.virtual_size) / units.Gi))
            vol['size'] = vsize
            vtype = vol['volume_type_id']
            LOG.info("Creating cached image with volume type: %(vtype)s and "
                     "size %(size)s", {'vtype': vtype, 'size': vsize})
            self._create_volume_2_2(vol)
            with self._connect_vol(context, vol) as device:
                LOG.debug("Moving image %s to volume %s",
                          image_meta['id'], datc.get_name(vol))
                image_utils.convert_image(tmp_image,
                                          device,
                                          'raw',
                                          run_as_root=True)
                LOG.debug("Finished moving image %s to volume %s",
                          image_meta['id'], datc.get_name(vol))
                data = image_utils.qemu_img_info(device, run_as_root=True)
                if data.file_format != 'raw':
                    raise exception.ImageUnacceptable(
                        image_id=image_id,
                        reason=_(
                            "Converted to %(vol_format)s, but format is "
                            "now %(file_format)s") % {
                                'vol_format': 'raw',
                                'file_format': data.file_format})
        # TODO(_alastor_): Remove this snapshot creation when we fix
        # "created_at" attribute in the frontend
        # We don't actually care about the snapshot uuid, we just want
        # a single snapshot
        snapshot = {'id': str(uuid.uuid4()),
                    'volume_id': vol['id'],
                    'project_id': vol['project_id']}
        self._create_snapshot_2_2(snapshot)
        metadata = {'type': 'cached_image'}
        tenant = self.get_tenant(vol['project_id'])
        ai = self.cvol_to_ai(vol, tenant=tenant)
        ai.metadata.set(tenant=tenant, **metadata)
        # Cloning offline AI is ~4 seconds faster than cloning online AI
        self._detach_volume_2_2(None, vol)
Beispiel #18
0
    def test_copy_volume_from_snapshot(self):
        (mox, drv) = self._mox, self._driver

        mox.StubOutWithMock(image_utils, 'convert_image')
        mox.StubOutWithMock(drv, '_read_info_file')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')

        dest_volume = self._simple_volume(
            'c1073000-0000-0000-0000-0000000c1073')
        src_volume = self._simple_volume()

        vol_dir = os.path.join(self.TEST_MNT_POINT_BASE,
                               drv._get_hash_str(self.TEST_QUOBYTE_VOLUME))
        src_vol_path = os.path.join(vol_dir, src_volume['name'])
        dest_vol_path = os.path.join(vol_dir, dest_volume['name'])
        info_path = os.path.join(vol_dir, src_volume['name']) + '.info'

        snapshot = {'volume_name': src_volume['name'],
                    'name': 'clone-snap-%s' % src_volume['id'],
                    'size': src_volume['size'],
                    'volume_size': src_volume['size'],
                    'volume_id': src_volume['id'],
                    'id': 'tmp-snap-%s' % src_volume['id'],
                    'volume': src_volume}

        snap_file = dest_volume['name'] + '.' + snapshot['id']
        snap_path = os.path.join(vol_dir, snap_file)

        size = dest_volume['size']

        drv._read_info_file(info_path).AndReturn(
            {'active': snap_file,
             snapshot['id']: snap_file}
        )

        qemu_img_output = """image: %s
        file format: raw
        virtual size: 1.0G (1073741824 bytes)
        disk size: 173K
        backing file: %s
        """ % (snap_file, src_volume['name'])
        img_info = imageutils.QemuImgInfo(qemu_img_output)

        image_utils.qemu_img_info(snap_path).AndReturn(img_info)

        image_utils.convert_image(src_vol_path,
                                  dest_vol_path,
                                  'raw',
                                  run_as_root=self.execute_as_root)

        drv._set_rw_permissions_for_all(dest_vol_path)

        mox.ReplayAll()

        drv._copy_volume_from_snapshot(snapshot, dest_volume, size)

        mox.VerifyAll()
Beispiel #19
0
    def _direct_clone(self, volume, image_location, image_id, image_name):
        """Clones directly in nfs share."""
        LOG.info(_LI('Checking image clone %s from glance share.'), image_id)
        cloned = False
        image_location = self._get_image_nfs_url(image_location)
        share = self._is_cloneable_share(image_location)
        run_as_root = self._execute_as_root

        dst_share = None
        for dst in self._mounted_shares:
            if dst and self._is_share_vol_compatible(volume, dst):
                dst_share = dst
                LOG.debug('Image dst share: %s', dst)
                break
        if not dst_share:
            return cloned

        LOG.debug('Share is cloneable %s', dst_share)
        volume['provider_location'] = dst_share
        (__, ___, img_file) = image_location.rpartition('/')
        dir_path = self._get_mount_point_for_share(share)
        dst_path = self._get_mount_point_for_share(dst_share)
        img_path = '%s/%s' % (dir_path, img_file)
        img_info = image_utils.qemu_img_info(img_path, run_as_root=run_as_root)
        if img_info.file_format == 'raw':
            LOG.debug('Image is raw %s', image_id)
            self._clone_volume_to_volume(img_file,
                                         volume['name'],
                                         image_name,
                                         volume_id=None,
                                         share=share,
                                         dst=dst_share,
                                         image_id=image_id)
            cloned = True
        else:
            LOG.info(_LI('Image will locally be converted to raw %s'),
                     image_id)
            dst = '%s/%s' % (dst_path, volume['name'])
            image_utils.convert_image(img_path,
                                      dst,
                                      'raw',
                                      run_as_root=run_as_root)
            data = image_utils.qemu_img_info(dst, run_as_root=run_as_root)
            if data.file_format != "raw":
                raise exception.InvalidResults(
                    _('Converted to raw, but '
                      'format is now %s') % data.file_format)
            else:
                cloned = True
                self._create_image_snapshot(volume['name'],
                                            volume['provider_location'],
                                            image_id, image_name)
        return cloned
Beispiel #20
0
    def _gpfs_fetch_to_raw(self,
                           context,
                           image_service,
                           image_id,
                           dest,
                           user_id=None,
                           project_id=None):
        if (self.configuration.image_conversion_dir and
                not os.path.exists(self.configuration.image_conversion_dir)):
            os.makedirs(self.configuration.image_conversion_dir)

        tmp = "%s.part" % dest
        with fileutils.remove_path_on_error(tmp):
            self._gpfs_fetch(context, image_service, image_id, tmp, user_id,
                             project_id)

            data = image_utils.qemu_img_info(tmp)
            fmt = data.file_format
            backing_file = data.backing_file

            if backing_file is not None:
                msg = (_("fmt = %(fmt)s backed by: %(backing_file)s") % {
                    'fmt': fmt,
                    'backing_file': backing_file
                })
                LOG.error(msg)
                raise exception.ImageUnacceptable(image_id=image_id,
                                                  reason=msg)

            if fmt is None:
                msg = _("'qemu-img info' parsing failed.")
                LOG.error(msg)
                raise exception.ImageUnacceptable(reason=msg,
                                                  image_id=image_id)
            elif fmt == 'raw':  # already in raw format - just rename to dest
                self._execute('mv', tmp, dest, run_as_root=True)
            else:  # conversion to raw format required
                LOG.debug("%s was %s, converting to raw" % (image_id, fmt))
                image_utils.convert_image(tmp, dest, 'raw')
                os.unlink(tmp)

            data = image_utils.qemu_img_info(dest)
            if data.file_format != "raw":
                msg = (_("Expected image to be in raw format, but is %s") %
                       data.file_format)
                LOG.error(msg)
                raise exception.ImageUnacceptable(image_id=image_id,
                                                  reason=msg)
            return {'size': math.ceil(data.virtual_size / 1024.0**3)}
Beispiel #21
0
    def _direct_clone(self, volume, image_location, image_id, image_name):
        """Clones directly in nfs share."""
        LOG.info('Checking image clone %s from glance share.', image_id)
        cloned = False
        image_location = self._get_image_nfs_url(image_location)
        share = self._is_cloneable_share(image_location)
        run_as_root = self._execute_as_root

        dst_share = None
        for dst in self._mounted_shares:
            if dst and self._is_share_vol_compatible(volume, dst):
                dst_share = dst
                LOG.debug('Image dst share: %s', dst)
                break
        if not dst_share:
            return cloned

        LOG.debug('Share is cloneable %s', dst_share)
        volume['provider_location'] = dst_share
        (__, ___, img_file) = image_location.rpartition('/')
        dir_path = self._get_mount_point_for_share(share)
        dst_path = self._get_mount_point_for_share(dst_share)
        img_path = '%s/%s' % (dir_path, img_file)
        img_info = image_utils.qemu_img_info(img_path,
                                             run_as_root=run_as_root)
        if img_info.file_format == 'raw':
            LOG.debug('Image is raw %s', image_id)
            self._clone_volume_to_volume(
                img_file, volume['name'], image_name,
                volume_id=None, share=share, dst=dst_share, image_id=image_id)
            cloned = True
        else:
            LOG.info('Image will locally be converted to raw %s',
                     image_id)
            dst = '%s/%s' % (dst_path, volume['name'])
            image_utils.convert_image(img_path, dst, 'raw',
                                      run_as_root=run_as_root)
            data = image_utils.qemu_img_info(dst, run_as_root=run_as_root)
            if data.file_format != "raw":
                raise exception.InvalidResults(
                    _('Converted to raw, but '
                      'format is now %s') % data.file_format)
            else:
                cloned = True
                self._create_image_snapshot(
                    volume['name'], volume['provider_location'],
                    image_id, image_name)
        return cloned
Beispiel #22
0
    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """Fetch the image from image_service and write it to the volume."""
        volume_format = self.get_volume_format(volume, qemu_format=True)
        image_meta = image_service.show(context, image_id)
        qemu_version = self.get_qemu_version()

        if (qemu_version < [1, 7]
                and (volume_format == self._DISK_FORMAT_VHDX
                     and image_meta['disk_format'] != volume_format)):
            err_msg = _("Unsupported volume format: vhdx. qemu-img 1.7 or "
                        "higher is required in order to properly support this "
                        "format.")
            raise exception.InvalidVolume(err_msg)

        image_utils.fetch_to_volume_format(
            context, image_service, image_id, self.local_path(volume),
            volume_format, self.configuration.volume_dd_blocksize)

        self._do_extend_volume(self.local_path(volume), volume['size'],
                               volume['name'])

        data = image_utils.qemu_img_info(self.local_path(volume))
        virt_size = data.virtual_size / units.Gi
        if virt_size != volume['size']:
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=(_("Expected volume size was %d") % volume['size']) +
                (_(" but size is now %d.") % virt_size))
Beispiel #23
0
    def optimize_volume(self, volume):
        """Optimizes a volume for Quobyte

        This optimization is normally done during creation but volumes created
        from e.g. snapshots require additional grooming.

        :param volume: volume reference
        """
        volume_path = self.local_path(volume)
        volume_size = volume.size
        data = image_utils.qemu_img_info(self.local_path(volume),
                                         run_as_root=self._execute_as_root)
        if data.disk_size >= (volume_size * units.Gi):
            LOG.debug(
                "Optimization of volume %(volpath)s is not required, "
                "skipping this step.", {'volpath': volume_path})
            return

        LOG.debug("Optimizing volume %(optpath)s", {'optpath': volume_path})

        if (self.configuration.quobyte_qcow2_volumes
                or self.configuration.quobyte_sparsed_volumes):
            self._execute('truncate',
                          '-s',
                          '%sG' % volume_size,
                          volume_path,
                          run_as_root=self._execute_as_root)
        else:
            self._create_regular_file(volume_path, volume_size)
Beispiel #24
0
    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """Fetch the image from image_service and write it to the volume."""
        image_utils.fetch_to_raw(context,
                                 image_service,
                                 image_id,
                                 self.local_path(volume),
                                 self.configuration.volume_dd_blocksize,
                                 size=volume['size'])

        # NOTE (leseb): Set the virtual size of the image
        # the raw conversion overwrote the destination file
        # (which had the correct size)
        # with the fetched glance image size,
        # thus the initial 'size' parameter is not honored
        # this sets the size to the one asked in the first place by the user
        # and then verify the final virtual size
        image_utils.resize_image(self.local_path(volume), volume['size'])

        data = image_utils.qemu_img_info(self.local_path(volume))
        virt_size = data.virtual_size / units.GiB
        if virt_size != volume['size']:
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=(_("Expected volume size was %d") % volume['size'])
                + (_(" but size is now %d") % virt_size))
Beispiel #25
0
    def _qemu_img_info_base(self, path, volume_name, basedir):
        """Sanitize image_utils' qemu_img_info.

        This code expects to deal only with relative filenames.
        """

        info = image_utils.qemu_img_info(path)
        if info.image:
            info.image = os.path.basename(info.image)
        if info.backing_file:
            backing_file_template = \
                "(%(basedir)s/[0-9a-f]+/)?%" \
                "(volname)s(.(tmp-snap-)?[0-9a-f-]+)?$" % {
                    'basedir': basedir,
                    'volname': volume_name
                }
            if not re.match(backing_file_template, info.backing_file):
                msg = _("File %(path)s has invalid backing file "
                        "%(bfile)s, aborting.") % {'path': path,
                                                   'bfile': info.backing_file}
                raise exception.RemoteFSException(msg)

            info.backing_file = os.path.basename(info.backing_file)

        return info
Beispiel #26
0
    def _qemu_img_info_base(self, path, volume_name, basedir):
        """Sanitize image_utils' qemu_img_info.

        This code expects to deal only with relative filenames.
        """

        info = image_utils.qemu_img_info(path)
        if info.image:
            info.image = os.path.basename(info.image)
        if info.backing_file:
            backing_file_template = \
                "(%(basedir)s/[0-9a-f]+/)?%" \
                "(volname)s(.(tmp-snap-)?[0-9a-f-]+)?$" % {
                    'basedir': basedir,
                    'volname': volume_name
                }
            if not re.match(backing_file_template, info.backing_file):
                msg = _("File %(path)s has invalid backing file "
                        "%(bfile)s, aborting.") % {
                            'path': path,
                            'bfile': info.backing_file
                        }
                raise exception.GlusterfsException(msg)

            info.backing_file = os.path.basename(info.backing_file)

        return info
Beispiel #27
0
    def optimize_volume(self, volume):
        """Optimizes a volume for Quobyte

        This optimization is normally done during creation but volumes created
        from e.g. snapshots require additional grooming.

        :param volume: volume reference
        """
        volume_path = self.local_path(volume)
        volume_size = volume.size
        data = image_utils.qemu_img_info(self.local_path(volume),
                                         run_as_root=self._execute_as_root)
        if data.disk_size >= (volume_size * units.Gi):
            LOG.debug("Optimization of volume %(volpath)s is not required, "
                      "skipping this step.", {'volpath': volume_path})
            return

        LOG.debug("Optimizing volume %(optpath)s", {'optpath': volume_path})

        if (self.configuration.quobyte_qcow2_volumes or
                self.configuration.quobyte_sparsed_volumes):
                self._execute('truncate', '-s', '%sG' % volume_size,
                              volume_path, run_as_root=self._execute_as_root)
        else:
            self._create_regular_file(volume_path, volume_size)
Beispiel #28
0
    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """Fetch the image from image_service and write it to the volume."""
        image_utils.fetch_to_raw(context,
                                 image_service,
                                 image_id,
                                 self.local_path(volume),
                                 self.configuration.volume_dd_blocksize,
                                 size=volume['size'])

        # NOTE (leseb): Set the virtual size of the image
        # the raw conversion overwrote the destination file
        # (which had the correct size)
        # with the fetched glance image size,
        # thus the initial 'size' parameter is not honored
        # this sets the size to the one asked in the first place by the user
        # and then verify the final virtual size
        image_utils.resize_image(self.local_path(volume), volume['size'])

        data = image_utils.qemu_img_info(self.local_path(volume))
        virt_size = data.virtual_size / units.Gi
        if virt_size != volume['size']:
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=(_("Expected volume size was %d") % volume['size'])
                + (_(" but size is now %d") % virt_size))
Beispiel #29
0
    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """Fetch the image from image_service and write it to the volume."""
        volume_format = self.get_volume_format(volume, qemu_format=True)
        image_meta = image_service.show(context, image_id)
        qemu_version = self.get_qemu_version()

        if qemu_version < [1, 7] and (
            volume_format == self._DISK_FORMAT_VHDX and image_meta["disk_format"] != volume_format
        ):
            err_msg = _(
                "Unsupported volume format: vhdx. qemu-img 1.7 or "
                "higher is required in order to properly support this "
                "format."
            )
            raise exception.InvalidVolume(err_msg)

        image_utils.fetch_to_volume_format(
            context,
            image_service,
            image_id,
            self.local_path(volume),
            volume_format,
            self.configuration.volume_dd_blocksize,
        )

        self._do_extend_volume(self.local_path(volume), volume["size"], volume["name"])

        data = image_utils.qemu_img_info(self.local_path(volume))
        virt_size = data.virtual_size / units.Gi
        if virt_size != volume["size"]:
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=(_("Expected volume size was %d") % volume["size"]) + (_(" but size is now %d.") % virt_size),
            )
Beispiel #30
0
 def create_volume_from_snapshot(self, volume, snapshot):
     """Creates a GPFS volume from a snapshot."""
     volume_path = self.local_path(volume)
     snapshot_path = self.local_path(snapshot)
     self._create_gpfs_copy(src=snapshot_path, dest=volume_path)
     self._gpfs_redirect(volume_path)
     data = image_utils.qemu_img_info(volume_path)
     return {'size': math.ceil(data.virtual_size / 1024.0 ** 3)}
    def test_clone_image_resizefails(self):
        drv = self._driver
        mox = self.mox
        volume = {'name': 'vol', 'size': '20'}
        mox.StubOutWithMock(drv, '_find_image_in_cache')
        mox.StubOutWithMock(drv, '_is_cloneable_share')
        mox.StubOutWithMock(drv, '_get_mount_point_for_share')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(drv, '_clone_volume')
        mox.StubOutWithMock(drv, '_discover_file_till_timeout')
        mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
        mox.StubOutWithMock(drv, '_resize_image_file')
        mox.StubOutWithMock(image_utils, 'convert_image')
        mox.StubOutWithMock(drv, '_register_image_in_cache')
        mox.StubOutWithMock(drv, '_is_share_vol_compatible')
        mox.StubOutWithMock(drv, 'local_path')
        mox.StubOutWithMock(os.path, 'exists')
        mox.StubOutWithMock(drv, '_delete_file')

        drv._find_image_in_cache(IgnoreArg()).AndReturn([])
        drv._is_cloneable_share('nfs://127.0.0.1/share/img-id').AndReturn(
            '127.0.0.1:/share')
        drv._is_share_vol_compatible(IgnoreArg(), IgnoreArg()).AndReturn(True)
        drv._get_mount_point_for_share('127.0.0.1:/share').AndReturn('/mnt')
        image_utils.qemu_img_info('/mnt/img-id').AndReturn(
            self.get_img_info('notraw'))
        image_utils.convert_image(IgnoreArg(), IgnoreArg(), 'raw')
        image_utils.qemu_img_info('/mnt/vol').AndReturn(
            self.get_img_info('raw'))
        drv._register_image_in_cache(IgnoreArg(), IgnoreArg())
        drv.local_path(IgnoreArg()).AndReturn('/mnt/vol')
        drv._discover_file_till_timeout(IgnoreArg()).AndReturn(True)
        drv._set_rw_permissions_for_all('/mnt/vol')
        drv._resize_image_file(IgnoreArg(), IgnoreArg()).AndRaise(
            exception.InvalidResults())
        drv.local_path(IgnoreArg()).AndReturn('/mnt/vol')
        os.path.exists('/mnt/vol').AndReturn(True)
        drv._delete_file('/mnt/vol')

        mox.ReplayAll()
        vol_dict, result = drv.clone_image(
            volume, ('nfs://127.0.0.1/share/img-id', None), 'image_id', {})
        mox.VerifyAll()
        self.assertFalse(result)
        self.assertFalse(vol_dict['bootable'])
        self.assertIsNone(vol_dict['provider_location'])
Beispiel #32
0
 def create_volume_from_snapshot(self, volume, snapshot):
     """Creates a GPFS volume from a snapshot."""
     volume_path = self.local_path(volume)
     snapshot_path = self.local_path(snapshot)
     self._create_gpfs_copy(src=snapshot_path, dest=volume_path)
     self._gpfs_redirect(volume_path)
     data = image_utils.qemu_img_info(volume_path)
     return {'size': math.ceil(data.virtual_size / 1024.0 ** 3)}
Beispiel #33
0
 def _is_file_size_equal(self, path, size):
     """Checks if file size at path is equal to size."""
     data = image_utils.qemu_img_info(path)
     virt_size = data.virtual_size / units.GiB
     if virt_size == size:
         return True
     else:
         return False
Beispiel #34
0
 def _is_file_size_equal(self, path, size):
     """Checks if file size at path is equal to size."""
     data = image_utils.qemu_img_info(path)
     virt_size = data.virtual_size / units.GiB
     if virt_size == size:
         return True
     else:
         return False
Beispiel #35
0
    def test_clone_image_resizefails(self):
        drv = self._driver
        mox = self.mox
        volume = {'name': 'vol', 'size': '20'}
        mox.StubOutWithMock(drv, '_find_image_in_cache')
        mox.StubOutWithMock(drv, '_is_cloneable_share')
        mox.StubOutWithMock(drv, '_get_mount_point_for_share')
        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        mox.StubOutWithMock(drv, '_clone_volume')
        mox.StubOutWithMock(drv, '_discover_file_till_timeout')
        mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
        mox.StubOutWithMock(drv, '_resize_image_file')
        mox.StubOutWithMock(image_utils, 'convert_image')
        mox.StubOutWithMock(drv, '_register_image_in_cache')
        mox.StubOutWithMock(drv, '_is_share_vol_compatible')
        mox.StubOutWithMock(drv, 'local_path')
        mox.StubOutWithMock(os.path, 'exists')
        mox.StubOutWithMock(drv, '_delete_file')

        drv._find_image_in_cache(IgnoreArg()).AndReturn([])
        drv._is_cloneable_share('nfs://127.0.0.1/share/img-id').AndReturn(
            '127.0.0.1:/share')
        drv._is_share_vol_compatible(IgnoreArg(), IgnoreArg()).AndReturn(True)
        drv._get_mount_point_for_share('127.0.0.1:/share').AndReturn('/mnt')
        image_utils.qemu_img_info('/mnt/img-id').AndReturn(
            self.get_img_info('notraw'))
        image_utils.convert_image(IgnoreArg(), IgnoreArg(), 'raw')
        image_utils.qemu_img_info('/mnt/vol').AndReturn(
            self.get_img_info('raw'))
        drv._register_image_in_cache(IgnoreArg(), IgnoreArg())
        drv.local_path(IgnoreArg()).AndReturn('/mnt/vol')
        drv._discover_file_till_timeout(IgnoreArg()).AndReturn(True)
        drv._set_rw_permissions_for_all('/mnt/vol')
        drv._resize_image_file(
            IgnoreArg(), IgnoreArg()).AndRaise(exception.InvalidResults())
        drv.local_path(IgnoreArg()).AndReturn('/mnt/vol')
        os.path.exists('/mnt/vol').AndReturn(True)
        drv._delete_file('/mnt/vol')

        mox.ReplayAll()
        vol_dict, result = drv. clone_image(
            volume, ('nfs://127.0.0.1/share/img-id', None), 'image_id', {})
        mox.VerifyAll()
        self.assertFalse(result)
        self.assertFalse(vol_dict['bootable'])
        self.assertIsNone(vol_dict['provider_location'])
Beispiel #36
0
    def _gpfs_fetch_to_raw(self, context, image_service, image_id, dest,
                           user_id=None, project_id=None):
        if (self.configuration.image_conversion_dir and not
                os.path.exists(self.configuration.image_conversion_dir)):
            os.makedirs(self.configuration.image_conversion_dir)

        tmp = "%s.part" % dest
        with fileutils.remove_path_on_error(tmp):
            self._gpfs_fetch(context, image_service, image_id, tmp, user_id,
                             project_id)

            data = image_utils.qemu_img_info(tmp)
            fmt = data.file_format
            backing_file = data.backing_file

            if backing_file is not None:
                msg = (_("fmt = %(fmt)s backed by: %(backing_file)s") %
                       {'fmt': fmt, 'backing_file': backing_file})
                LOG.error(msg)
                raise exception.ImageUnacceptable(
                    image_id=image_id,
                    reason=msg)

            if fmt is None:
                msg = _("'qemu-img info' parsing failed.")
                LOG.error(msg)
                raise exception.ImageUnacceptable(
                    reason=msg,
                    image_id=image_id)
            elif fmt == 'raw':  # already in raw format - just rename to dest
                self._execute('mv', tmp, dest, run_as_root=True)
            else:  # conversion to raw format required
                LOG.debug("%s was %s, converting to raw" % (image_id, fmt))
                image_utils.convert_image(tmp, dest, 'raw')
                os.unlink(tmp)

            data = image_utils.qemu_img_info(dest)
            if data.file_format != "raw":
                msg = (_("Expected image to be in raw format, but is %s") %
                       data.file_format)
                LOG.error(msg)
                raise exception.ImageUnacceptable(
                    image_id=image_id,
                    reason=msg)
            return {'size': math.ceil(data.virtual_size / 1024.0 ** 3)}
Beispiel #37
0
 def _direct_nfs_clone(self, volume, image_location, image_id):
     """Clone directly in nfs share."""
     LOG.info(_LI('Checking image clone %s from glance share.'), image_id)
     cloned = False
     image_locations = self._construct_image_nfs_url(image_location)
     run_as_root = self._execute_as_root
     for loc in image_locations:
         share = self._is_cloneable_share(loc)
         if share and self._is_share_clone_compatible(volume, share):
             LOG.debug('Share is cloneable %s', share)
             volume['provider_location'] = share
             (__, ___, img_file) = loc.rpartition('/')
             dir_path = self._get_mount_point_for_share(share)
             img_path = '%s/%s' % (dir_path, img_file)
             img_info = image_utils.qemu_img_info(img_path,
                                                  run_as_root=run_as_root)
             if img_info.file_format == 'raw':
                 LOG.debug('Image is raw %s', image_id)
                 self._clone_backing_file_for_volume(img_file,
                                                     volume['name'],
                                                     volume_id=None,
                                                     share=share)
                 cloned = True
                 break
             else:
                 LOG.info(_LI('Image will locally be converted to raw %s'),
                          image_id)
                 dst = '%s/%s' % (dir_path, volume['name'])
                 image_utils.convert_image(img_path,
                                           dst,
                                           'raw',
                                           run_as_root=run_as_root)
                 data = image_utils.qemu_img_info(dst,
                                                  run_as_root=run_as_root)
                 if data.file_format != "raw":
                     raise exception.InvalidResults(
                         _("Converted to raw, but"
                           " format is now %s") % data.file_format)
                 else:
                     cloned = True
                     self._register_image_in_cache(volume, image_id)
                     break
     return cloned
Beispiel #38
0
    def test_extend_volume(self):
        new_vol_size = 15
        mox = mox_lib.Mox()
        volume = test_utils.create_volume(self.context, host=CONF.host)
        volpath = os.path.join(self.volumes_path, volume['name'])

        qemu_img_info_output = """image: %s
        file format: raw
        virtual size: %sG (%s bytes)
        backing file: %s
        """ % (volume['name'], new_vol_size, new_vol_size * units.GiB, volpath)
        mox.StubOutWithMock(image_utils, 'resize_image')
        image_utils.resize_image(volpath, new_vol_size)

        mox.StubOutWithMock(image_utils, 'qemu_img_info')
        img_info = imageutils.QemuImgInfo(qemu_img_info_output)
        image_utils.qemu_img_info(volpath).AndReturn(img_info)
        mox.ReplayAll()

        self.driver.extend_volume(volume, new_vol_size)
        mox.VerifyAll()
Beispiel #39
0
    def test_qemu_img_info_alt(self):
        """Test a slightly different variation of qemu-img output.

           (Based on Fedora 19's qemu-img 1.4.2.)
        """

        TEST_PATH = "img/qemu.qcow2"
        TEST_RETURN = "image: qemu.qcow2\n"\
                      "backing file: qemu.qcow2 (actual path: qemu.qcow2)\n"\
                      "file format: qcow2\n"\
                      "virtual size: 50M (52428800 bytes)\n"\
                      "cluster_size: 65536\n"\
                      "disk size: 196K (200704 bytes)\n"\
                      "Snapshot list:\n"\
                      "ID TAG  VM SIZE DATE VM CLOCK\n"\
                      "1  snap1 1.7G 2011-10-04 19:04:00 32:06:34.974"
        TEST_STR = "image: qemu.qcow2\n"\
                   "file_format: qcow2\n"\
                   "virtual_size: 52428800\n"\
                   "disk_size: 200704\n"\
                   "cluster_size: 65536\n"\
                   "backing_file: qemu.qcow2\n"\
                   "snapshots: [{'date': '2011-10-04', "\
                   "'vm_clock': '19:04:00 32:06:34.974', "\
                   "'vm_size': '1.7G', 'tag': 'snap1', 'id': '1'}]"

        mox = self._mox
        mox.StubOutWithMock(utils, 'execute')

        cmd = ['env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'info', TEST_PATH]
        utils.execute(*cmd, run_as_root=True).AndReturn(
            (TEST_RETURN, 'ignored'))

        mox.ReplayAll()

        inf = image_utils.qemu_img_info(TEST_PATH)

        self.assertEquals(inf.image, 'qemu.qcow2')
        self.assertEquals(inf.backing_file, 'qemu.qcow2')
        self.assertEquals(inf.file_format, 'qcow2')
        self.assertEquals(inf.virtual_size, 52428800)
        self.assertEquals(inf.cluster_size, 65536)
        self.assertEquals(inf.disk_size, 200704)

        self.assertEquals(inf.snapshots[0]['id'], '1')
        self.assertEquals(inf.snapshots[0]['tag'], 'snap1')
        self.assertEquals(inf.snapshots[0]['vm_size'], '1.7G')
        self.assertEquals(inf.snapshots[0]['date'], '2011-10-04')
        self.assertEquals(inf.snapshots[0]['vm_clock'],
                          '19:04:00 32:06:34.974')

        self.assertEquals(str(inf), TEST_STR)
Beispiel #40
0
    def _qemu_img_info(self, path):
        """Sanitize image_utils' qemu_img_info.

        This code expects to deal only with relative filenames.
        """

        info = image_utils.qemu_img_info(path)
        if info.image:
            info.image = os.path.basename(info.image)
        if info.backing_file:
            info.backing_file = os.path.basename(info.backing_file)

        return info
Beispiel #41
0
    def _resize_volume_file(self, volume, new_size):
        """Resize volume file to new size."""
        vol_path = self.local_path(volume)
        try:
            image_utils.resize_image(vol_path, new_size)
        except processutils.ProcessExecutionError as exc:
            LOG.error(_("Failed to resize volume "
                        "%(volume_id)s, error: %(error)s") %
                      {'volume_id': volume['id'],
                       'error': exc.stderr})
            raise exception.VolumeBackendAPIException(data=exc.stderr)

        data = image_utils.qemu_img_info(vol_path)
        return data.virtual_size
Beispiel #42
0
    def _clone_image(self, volume, image_location, image_id):
        """Attempt to create a volume by efficiently copying image to volume.

        If both source and target are backed by gpfs storage and the source
        image is in raw format move the image to create a volume using either
        gpfs clone operation or with a file copy. If the image format is not
        raw, convert it to raw at the volume path.
        """
        # Check if GPFS is mounted
        self._verify_gpfs_path_state(self.configuration.gpfs_mount_point_base)

        cloneable_image, reason, image_path = self._is_cloneable(image_id)
        if not cloneable_image:
            LOG.debug('Image %(img)s not cloneable: %(reas)s.' % {
                'img': image_id,
                'reas': reason
            })
            return (None, False)

        vol_path = self.local_path(volume)

        data = image_utils.qemu_img_info(image_path)

        # if image format is already raw either clone it or
        # copy it depending on config file settings
        if data.file_format == 'raw':
            if (self.configuration.gpfs_images_share_mode == 'copy_on_write'):
                LOG.debug('Clone image to vol %s using mmclone.' %
                          volume['id'])
                # if the image is not already a GPFS snap file make it so
                if not self._is_gpfs_parent_file(image_path):
                    self._create_gpfs_snap(image_path)

                self._create_gpfs_copy(image_path, vol_path)
            elif self.configuration.gpfs_images_share_mode == 'copy':
                LOG.debug('Clone image to vol %s using copyfile.' %
                          volume['id'])
                shutil.copyfile(image_path, vol_path)

        # if image is not raw convert it to raw into vol_path destination
        else:
            LOG.debug('Clone image to vol %s using qemu convert.' %
                      volume['id'])
            image_utils.convert_image(image_path, vol_path, 'raw')

        self._set_rw_permission(vol_path)
        self._resize_volume_file(volume, volume['size'])

        return {'provider_location': None}, True
Beispiel #43
0
 def _direct_nfs_clone(self, volume, image_location, image_id):
     """Clone directly in nfs share."""
     LOG.info(_LI('Checking image clone %s from glance share.'), image_id)
     cloned = False
     image_locations = self._construct_image_nfs_url(image_location)
     run_as_root = self._execute_as_root
     for loc in image_locations:
         share = self._is_cloneable_share(loc)
         if share and self._is_share_clone_compatible(volume, share):
             LOG.debug('Share is cloneable %s', share)
             volume['provider_location'] = share
             (__, ___, img_file) = loc.rpartition('/')
             dir_path = self._get_mount_point_for_share(share)
             img_path = '%s/%s' % (dir_path, img_file)
             img_info = image_utils.qemu_img_info(img_path,
                                                  run_as_root=run_as_root)
             if img_info.file_format == 'raw':
                 LOG.debug('Image is raw %s', image_id)
Beispiel #44
0
    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """Fetch the image from image_service and write it to the volume."""
        volume_format = self.get_volume_format(volume, qemu_format=True)

        image_utils.fetch_to_volume_format(
            context, image_service, image_id, self.local_path(volume),
            volume_format, self.configuration.volume_dd_blocksize)

        self._do_extend_volume(self.local_path(volume), volume['size'],
                               volume['name'])

        data = image_utils.qemu_img_info(self.local_path(volume))
        virt_size = data.virtual_size / units.Gi
        if virt_size != volume['size']:
            raise exception.ImageUnacceptable(
                image_id=image_id,
                reason=(_("Expected volume size was %d") % volume['size']) +
                (_(" but size is now %d.") % virt_size))
Beispiel #45
0
    def backup_volume(self, context, backup, backup_service):
        """Create a new backup from an existing volume."""
        volume = self.db.volume_get(context, backup['volume_id'])
        volume_local_path = self.local_path(volume)
        LOG.info(_LI('Begin backup of volume %s.') % volume['name'])

        qemu_img_info = image_utils.qemu_img_info(volume_local_path)
        if qemu_img_info.file_format != 'raw':
            msg = _('Backup is only supported for raw-formatted '
                    'SOFS volumes.')
            raise exception.InvalidVolume(msg)

        if qemu_img_info.backing_file is not None:
            msg = _('Backup is only supported for SOFS volumes '
                    'without backing file.')
            raise exception.InvalidVolume(msg)

        with utils.temporary_chown(volume_local_path):
            with fileutils.file_open(volume_local_path) as volume_file:
                backup_service.backup(backup, volume_file)
Beispiel #46
0
    def copy_image_to_volume(self, context, volume, image_service, image_id):
        """Fetch the image from image_service and write it to the volume."""

        img_cache_volume = {'name': 'img-%s' % image_id}
        img_cache_snapshot = {'volume_name': img_cache_volume['name'],
                              'name': 'snap'}

        if self._list_snapshots(volume):
            # If the volume has snapshots, there is no efficient way to replace
            # the contents. Do things the inefficient way.
            image_utils.fetch_to_raw(context, image_service, image_id,
                                     self.local_path(volume),
                                     self.configuration.volume_dd_blocksize,
                                     size=volume['size'])

        else:

            if not self.snapshot_exists(img_cache_snapshot):
                with image_utils.temporary_file() as tmp:
                    image_utils.fetch_verify_image(context, image_service,
                                                   image_id, tmp)

                    qemu_info = image_utils.qemu_img_info(tmp)
                    virtual_size_gb = math.ceil(
                        qemu_info.virtual_size / (1024.0 ** 3))
                    img_cache_volume['size'] = virtual_size_gb

                    self.create_volume(img_cache_volume)

                    image_utils.convert_image(
                        tmp, self.local_path(img_cache_volume), 'raw', None,
                        sparse=64 * 1024)

                self.create_snapshot(img_cache_snapshot)

            self.delete_volume(volume)
            self.create_volume_from_snapshot(volume, img_cache_snapshot)
            self.extend_volume(volume, volume['size'])
Beispiel #47
0
    def _create_from_image_cache_or_download(self, context, volume,
                                             image_location, image_id,
                                             image_meta, image_service):
        # Try and use the image cache.
        should_create_cache_entry = False
        cloned = False
        model_update = None
        if self.image_volume_cache:
            internal_context = cinder_context.get_internal_tenant_context()
            if not internal_context:
                LOG.info('Unable to get Cinder internal context, will '
                         'not use image-volume cache.')
            else:
                model_update, cloned = self._create_from_image_cache(
                    context,
                    internal_context,
                    volume,
                    image_id,
                    image_meta
                )
                # Don't cache encrypted volume.
                if not cloned and not volume.encryption_key_id:
                    should_create_cache_entry = True
                    # cleanup consistencygroup field in the volume,
                    # because when creating cache entry, it will need
                    # to update volume object.
                    self._cleanup_cg_in_volume(volume)

        # Fall back to default behavior of creating volume,
        # download the image data and copy it into the volume.
        original_size = volume.size
        backend_name = volume_utils.extract_host(volume.service_topic_queue)
        try:
            if not cloned:
                try:
                    with image_utils.TemporaryImages.fetch(
                            image_service, context, image_id,
                            backend_name) as tmp_image:
                        # Try to create the volume as the minimal size,
                        # then we can extend once the image has been
                        # downloaded.
                        data = image_utils.qemu_img_info(tmp_image)

                        virtual_size = image_utils.check_virtual_size(
                            data.virtual_size, volume.size, image_id)

                        if should_create_cache_entry:
                            if virtual_size and virtual_size != original_size:
                                    volume.size = virtual_size
                                    volume.save()
                        model_update = self._create_from_image_download(
                            context,
                            volume,
                            image_location,
                            image_meta,
                            image_service
                        )
                except exception.ImageTooBig as e:
                    with excutils.save_and_reraise_exception():
                        self.message.create(
                            context,
                            message_field.Action.COPY_IMAGE_TO_VOLUME,
                            resource_uuid=volume.id,
                            detail=
                            message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE,
                            exception=e)

            if should_create_cache_entry:
                # Update the newly created volume db entry before we clone it
                # for the image-volume creation.
                if model_update:
                    volume.update(model_update)
                    volume.save()
                self.manager._create_image_cache_volume_entry(internal_context,
                                                              volume,
                                                              image_id,
                                                              image_meta)
        finally:
            # If we created the volume as the minimal size, extend it back to
            # what was originally requested. If an exception has occurred or
            # extending it back failed, we still need to put this back before
            # letting it be raised further up the stack.
            if volume.size != original_size:
                try:
                    self.driver.extend_volume(volume, original_size)
                finally:
                    volume.size = original_size
                    volume.save()

        return model_update
Beispiel #48
0
    def _copy_from_img_service(self, context, volume, image_service, image_id):
        """Copies from the image service using copy offload."""

        LOG.debug("Trying copy from image service using copy offload.")
        image_loc = image_service.get_location(context, image_id)
        locations = self._construct_image_nfs_url(image_loc)
        src_ip = None
        selected_loc = None
        cloned = False

        # this will match the first location that has a valid IP on cluster
        for location in locations:
            conn, dr = self._check_get_nfs_path_segs(location)
            if conn:
                try:
                    src_ip = self._get_ip_verify_on_cluster(conn.split(':')[0])
                    selected_loc = location
                    break
                except exception.NotFound:
                    pass
        if src_ip is None:
            raise exception.NotFound(_("Source host details not found."))
        (__, ___, img_file) = selected_loc.rpartition('/')
        src_path = os.path.join(dr, img_file)

        dst_ip, vol_path = self._get_destination_ip_and_path(volume)
        share_path = vol_path.rsplit("/", 1)[0]
        dst_share = dst_ip + ':' + share_path

        # tmp file is required to deal with img formats
        tmp_img_file = six.text_type(uuid.uuid4())
        col_path = self.configuration.netapp_copyoffload_tool_path
        img_info = image_service.show(context, image_id)
        self._check_share_can_hold_size(dst_share, img_info['size'])
        run_as_root = self._execute_as_root

        dst_dir = self._get_mount_point_for_share(dst_share)
        dst_img_local = os.path.join(dst_dir, tmp_img_file)

        try:
            dst_img_serv_path = os.path.join(share_path, tmp_img_file)
            # Always run copy offload as regular user, it's sufficient
            # and rootwrap doesn't allow copy offload to run as root
            # anyways.
            self._execute(col_path,
                          src_ip,
                          dst_ip,
                          src_path,
                          dst_img_serv_path,
                          run_as_root=False,
                          check_exit_code=0)

            self._discover_file_till_timeout(dst_img_local, timeout=120)
            LOG.debug('Copied image %(img)s to tmp file %(tmp)s.', {
                'img': image_id,
                'tmp': tmp_img_file
            })
            dst_img_cache_local = os.path.join(dst_dir,
                                               'img-cache-%s' % image_id)
            if img_info['disk_format'] == 'raw':
                LOG.debug('Image is raw %s.', image_id)
                self._clone_file_dst_exists(dst_share,
                                            tmp_img_file,
                                            volume['name'],
                                            dest_exists=True)
                self._move_nfs_file(dst_img_local, dst_img_cache_local)
                LOG.debug('Copied raw image %(img)s to volume %(vol)s.', {
                    'img': image_id,
                    'vol': volume['id']
                })
            else:
                LOG.debug('Image will be converted to raw %s.', image_id)
                img_conv = six.text_type(uuid.uuid4())
                dst_img_conv_local = os.path.join(dst_dir, img_conv)

                # Checking against image size which is approximate check
                self._check_share_can_hold_size(dst_share, img_info['size'])
                try:
                    image_utils.convert_image(dst_img_local,
                                              dst_img_conv_local,
                                              'raw',
                                              run_as_root=run_as_root)
                    data = image_utils.qemu_img_info(dst_img_conv_local,
                                                     run_as_root=run_as_root)
                    if data.file_format != "raw":
                        raise exception.InvalidResults(
                            _("Converted to raw, but format is now %s.") %
                            data.file_format)
                    else:
                        self._clone_file_dst_exists(dst_share,
                                                    img_conv,
                                                    volume['name'],
                                                    dest_exists=True)
                        self._move_nfs_file(dst_img_conv_local,
                                            dst_img_cache_local)
                        LOG.debug(
                            'Copied locally converted raw image'
                            ' %(img)s to volume %(vol)s.', {
                                'img': image_id,
                                'vol': volume['id']
                            })
                finally:
                    if os.path.exists(dst_img_conv_local):
                        self._delete_file_at_path(dst_img_conv_local)
            cloned = True
        finally:
            if os.path.exists(dst_img_local):
                self._delete_file_at_path(dst_img_local)

        return cloned
Beispiel #49
0
 def _is_file_size_equal(self, path, size):
     """Checks if file size at path is equal to size."""
     data = image_utils.qemu_img_info(path,
                                      run_as_root=self._execute_as_root)
     virt_size = int(data.virtual_size / units.Gi)
     return virt_size == size