def bytes_to_extents(self, size):
'''
- Return a how many extents we can fit into a size in bytes.
+ Return a how many free extents we can fit into a size in bytes. This has
+ some uncertainty involved. If size/extent_size is within 1% of the
+ actual free extents we will return the extent count, otherwise we'll
+ throw an error.
+ This accomodates for the size calculation in batch. We need to report
+ the OSD layout but have not yet created any LVM structures. We use the
+ disk size in batch if no VG is present and that will overshoot the
+ actual free_extent count due to LVM overhead.
+
'''
- return int(size / int(self.vg_extent_size))
+ b_to_ext = int(size / int(self.vg_extent_size))
+ if b_to_ext < int(self.vg_free_count):
+ # return bytes in extents if there is more space
+ return b_to_ext
+ elif b_to_ext / int(self.vg_free_count) - 1 < 0.01:
+ # return vg_fre_count if its less then 1% off
+ logger.info(
+ 'bytes_to_extents results in {} but only {} '
+ 'are available, adjusting the latter'.format(b_to_ext,
+ self.vg_free_count))
+ return int(self.vg_free_count)
+ # else raise an exception
+ raise RuntimeError('Can\'t convert {} to free extents, only {} ({} '
+ 'bytes) are free'.format(size, self.vg_free_count,
+ self.free))
def slots_to_extents(self, slots):
'''
@patch('ceph_volume.api.lvm.get_first_lv')
def test_uses_size(self, m_get_first_lv, m_call, m_run, monkeypatch):
m_get_first_lv.return_value = self.foo_volume
- api.create_lv('foo', 0, vg=self.foo_group, size=5368709120, tags={'ceph.type': 'data'})
- expected = ['lvcreate', '--yes', '-l', '1280', '-n', 'foo-0', 'foo_group']
+ api.create_lv('foo', 0, vg=self.foo_group, size=419430400, tags={'ceph.type': 'data'})
+ expected = ['lvcreate', '--yes', '-l', '100', '-n', 'foo-0', 'foo_group']
m_run.assert_called_with(expected)
+ @patch('ceph_volume.api.lvm.process.run')
+ @patch('ceph_volume.api.lvm.process.call')
+ @patch('ceph_volume.api.lvm.get_first_lv')
+ def test_uses_size_adjust_if_1percent_over(self, m_get_first_lv, m_call, m_run, monkeypatch):
+ foo_volume = api.Volume(lv_name='foo', lv_path='/path', vg_name='foo_group', lv_tags='')
+ foo_group = api.VolumeGroup(vg_name='foo_group',
+ vg_extent_size=4194304,
+ vg_extent_count=1000,
+ vg_free_count=1000)
+ m_get_first_lv.return_value = foo_volume
+ # 423624704 should be just under 1% off of the available size 419430400
+ api.create_lv('foo', 0, vg=foo_group, size=4232052736, tags={'ceph.type': 'data'})
+ expected = ['lvcreate', '--yes', '-l', '1000', '-n', 'foo-0', 'foo_group']
+ m_run.assert_called_with(expected)
+
+ @patch('ceph_volume.api.lvm.process.run')
+ @patch('ceph_volume.api.lvm.process.call')
+ @patch('ceph_volume.api.lvm.get_first_lv')
+ def test_uses_size_too_large(self, m_get_first_lv, m_call, m_run, monkeypatch):
+ m_get_first_lv.return_value = self.foo_volume
+ with pytest.raises(RuntimeError):
+ api.create_lv('foo', 0, vg=self.foo_group, size=5368709120, tags={'ceph.type': 'data'})
+
@patch('ceph_volume.api.lvm.process.run')
@patch('ceph_volume.api.lvm.process.call')
@patch('ceph_volume.api.lvm.get_first_lv')