Skip to content

Commit baa7044

Browse files
Millefeuille42MarkSymsCtx
authored andcommitted
refactor: use VHD header to fetch block size
Signed-off-by: Mathieu Labourier <mathieu.labourier@vates.tech>
1 parent 2ac4e67 commit baa7044

File tree

9 files changed

+113
-35
lines changed

9 files changed

+113
-35
lines changed

libs/sm/VDI.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def __init__(self, sr, uuid):
101101
self.description = ''
102102
self.vbds = []
103103
self.size = 0
104+
self._block_size = -1
104105
self.utilisation = 0
105106
self.vdi_type = ''
106107
self.has_child = 0
@@ -120,6 +121,12 @@ def __init__(self, sr, uuid):
120121

121122
self.load(uuid)
122123

124+
@property
125+
def block_size(self):
126+
if self._block_size < 0:
127+
self._block_size = vhdutil.getBlockSize(self.path)
128+
return self._block_size
129+
123130
@staticmethod
124131
def from_uuid(session, vdi_uuid):
125132

libs/sm/cleanup.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -535,12 +535,19 @@ def __init__(self, sr, uuid, raw):
535535
self.sizeVirt = -1
536536
self._sizeVHD = -1
537537
self._sizeAllocated = -1
538+
self._block_size = -1
538539
self._hidden = False
539540
self.parent = None
540541
self.children = []
541542
self._vdiRef = None
542543
self._clearRef()
543544

545+
@property
546+
def block_size(self):
547+
if self._block_size < 0:
548+
self._block_size = vhdutil.getBlockSize(self.path)
549+
return self._block_size
550+
544551
@staticmethod
545552
def extractUuid(path):
546553
raise NotImplementedError("Implement in sub class")
@@ -1078,14 +1085,16 @@ def _getCoalescedSizeData(self):
10781085
blocksParent = self.parent.getVHDBlocks()
10791086
numBlocks = Util.countBits(blocksChild, blocksParent)
10801087
Util.log("Num combined blocks = %d" % numBlocks)
1081-
sizeData = numBlocks * vhdutil.VHD_BLOCK_SIZE
1088+
sizeData = numBlocks * self.block_size
10821089
assert(sizeData <= self.sizeVirt)
10831090
return sizeData
10841091

10851092
def _calcExtraSpaceForCoalescing(self):
10861093
sizeData = self._getCoalescedSizeData()
1087-
sizeCoalesced = sizeData + vhdutil.calcOverheadBitmap(sizeData) + \
1088-
vhdutil.calcOverheadEmpty(self.sizeVirt)
1094+
sizeCoalesced = sizeData + vhdutil.calcOverheadBitmap(
1095+
sizeData,
1096+
self.block_size
1097+
) + vhdutil.calcOverheadEmpty(self.sizeVirt)
10891098
Util.log("Coalesced size = %s" % Util.num2str(sizeCoalesced))
10901099
return sizeCoalesced - self.parent.getSizeVHD()
10911100

@@ -1241,7 +1250,7 @@ def deflate(self):
12411250
self._sizeAllocated = -1
12421251

12431252
def inflateFully(self):
1244-
self.inflate(lvhdutil.calcSizeVHDLV(self.sizeVirt))
1253+
self.inflate(lvhdutil.calcSizeVHDLV(self.sizeVirt, self.block_size))
12451254

12461255
def inflateParentForCoalesce(self):
12471256
"""Inflate the parent only as much as needed for the purposes of
@@ -1467,7 +1476,10 @@ def _queryVHDBlocks(self):
14671476
def _calcExtraSpaceForCoalescing(self):
14681477
if self.parent.raw:
14691478
return 0 # raw parents are never deflated in the first place
1470-
sizeCoalesced = lvhdutil.calcSizeVHDLV(self._getCoalescedSizeData())
1479+
sizeCoalesced = lvhdutil.calcSizeVHDLV(
1480+
self._getCoalescedSizeData(),
1481+
self.block_size
1482+
)
14711483
Util.log("Coalesced size = %s" % Util.num2str(sizeCoalesced))
14721484
return sizeCoalesced - self.parent.sizeLV
14731485

@@ -2809,7 +2821,7 @@ def _finishCoalesceLeaf(self, parent):
28092821
parent.deflate()
28102822

28112823
def _calcExtraSpaceNeeded(self, child, parent):
2812-
return lvhdutil.calcSizeVHDLV(parent.sizeVirt) - parent.sizeLV
2824+
return lvhdutil.calcSizeVHDLV(parent.sizeVirt, parent.block_size) - parent.sizeLV
28132825

28142826
def _handleInterruptedCoalesceLeaf(self):
28152827
entries = self.journaler.getAll(VDI.JRN_LEAF)

libs/sm/drivers/FileSR.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ def resize(self, sr_uuid, vdi_uuid, size):
656656
return VDI.VDI.get_params(self)
657657

658658
# We already checked it is a VDI_TYPE_VHD
659-
size = vhdutil.validate_and_round_vhd_size(int(size))
659+
size = vhdutil.validate_and_round_vhd_size(int(size), self.block_size)
660660

661661
jFile = JOURNAL_FILE_PREFIX + self.uuid
662662
try:

libs/sm/drivers/LVHDSR.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,10 @@ def scan(self, uuid):
721721
util.roundup(lvutil.LVM_SIZE_INCREMENT,
722722
vhdutil.calcOverheadEmpty(lvhdutil.MSIZE))
723723
else:
724-
utilisation = lvhdutil.calcSizeVHDLV(int(size))
724+
utilisation = lvhdutil.calcSizeVHDLV(
725+
int(size),
726+
vhdutil.getBlockSize(lvPath)
727+
)
725728

726729
vdi_ref = self.session.xenapi.VDI.db_introduce(
727730
vdi_uuid,
@@ -989,7 +992,10 @@ def _undoCloneOp(self, lvs, origUuid, baseUuid, clonUuid):
989992

990993
# inflate the parent to fully-allocated size
991994
if base.vdiType == vhdutil.VDI_TYPE_VHD:
992-
fullSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt)
995+
fullSize = lvhdutil.calcSizeVHDLV(
996+
vhdInfo.sizeVirt,
997+
vhdutil.getBlockSize(basePath)
998+
)
993999
lvhdutil.inflate(self.journaler, self.uuid, baseUuid, fullSize)
9941000

9951001
# rename back
@@ -1178,7 +1184,7 @@ def _undoAllVHDJournals(self):
11781184
util.SMlog("Found VHD journal %s, reverting %s" % (uuid, vdi.path))
11791185
self.lvActivator.activate(uuid, vdi.lvname, False)
11801186
self.lvmCache.activateNoRefcount(jlvName)
1181-
fullSize = lvhdutil.calcSizeVHDLV(vdi.size)
1187+
fullSize = lvhdutil.calcSizeVHDLV(vdi.size, vdi.block_size)
11821188
lvhdutil.inflate(self.journaler, self.uuid, vdi.uuid, fullSize)
11831189
try:
11841190
jFile = os.path.join(self.path, jlvName)
@@ -1189,7 +1195,7 @@ def _undoAllVHDJournals(self):
11891195
util.SMlog("VHD revert failed but VHD ok: removing journal")
11901196
# Attempt to reclaim unused space
11911197
vhdInfo = vhdutil.getVHDInfo(vdi.path, lvhdutil.extractUuid, False)
1192-
NewSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt)
1198+
NewSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt, vdi.block_size)
11931199
if NewSize < fullSize:
11941200
lvhdutil.deflate(self.lvmCache, vdi.lvname, int(NewSize))
11951201
lvhdutil.lvRefreshOnAllSlaves(self.session, self.uuid,
@@ -1444,7 +1450,10 @@ def attach(self, sr_uuid, vdi_uuid):
14441450
needInflate = False
14451451
else:
14461452
self._loadThis()
1447-
if self.utilisation >= lvhdutil.calcSizeVHDLV(self.size):
1453+
if (
1454+
self.utilisation >=
1455+
lvhdutil.calcSizeVHDLV(self.size, self.block_size)
1456+
):
14481457
needInflate = False
14491458

14501459
if needInflate:
@@ -1464,7 +1473,7 @@ def detach(self, sr_uuid, vdi_uuid):
14641473
util.SMlog("LVHDVDI.detach for %s" % self.uuid)
14651474
self._loadThis()
14661475
already_deflated = (self.utilisation < \
1467-
lvhdutil.calcSizeVHDLV(self.size))
1476+
lvhdutil.calcSizeVHDLV(self.size, self.block_size))
14681477
needDeflate = True
14691478
if self.vdi_type == vhdutil.VDI_TYPE_RAW or already_deflated:
14701479
needDeflate = False
@@ -1505,7 +1514,7 @@ def resize(self, sr_uuid, vdi_uuid, size):
15051514
'(current size: %d, new size: %d)' % (self.size, size))
15061515
raise xs_errors.XenError('VDISize', opterr='shrinking not allowed')
15071516

1508-
size = vhdutil.validate_and_round_vhd_size(int(size))
1517+
size = vhdutil.validate_and_round_vhd_size(int(size), self.block_size)
15091518

15101519
if size == self.size:
15111520
return VDI.VDI.get_params(self)
@@ -1515,7 +1524,7 @@ def resize(self, sr_uuid, vdi_uuid, size):
15151524
lvSizeNew = util.roundup(lvutil.LVM_SIZE_INCREMENT, size)
15161525
else:
15171526
lvSizeOld = self.utilisation
1518-
lvSizeNew = lvhdutil.calcSizeVHDLV(size)
1527+
lvSizeNew = lvhdutil.calcSizeVHDLV(size, self.block_size)
15191528
if self.sr.provision == "thin":
15201529
# VDI is currently deflated, so keep it deflated
15211530
lvSizeNew = lvSizeOld
@@ -1681,7 +1690,7 @@ def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None):
16811690
self.issnap = self.session.xenapi.VDI.get_is_a_snapshot( \
16821691
self.sr.srcmd.params['vdi_ref'])
16831692

1684-
fullpr = lvhdutil.calcSizeVHDLV(self.size)
1693+
fullpr = lvhdutil.calcSizeVHDLV(self.size, self.block_size)
16851694
thinpr = util.roundup(lvutil.LVM_SIZE_INCREMENT, \
16861695
vhdutil.calcOverheadEmpty(lvhdutil.MSIZE))
16871696
lvSizeOrig = thinpr

libs/sm/lvhdutil.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ def calcSizeLV(sizeVHD):
9595
return util.roundup(LVM_SIZE_INCREMENT, sizeVHD)
9696

9797

98-
def calcSizeVHDLV(sizeVirt):
98+
def calcSizeVHDLV(sizeVirt, block_size=vhdutil.DEFAULT_VHD_BLOCK_SIZE):
9999
# all LVHD VDIs have the metadata area preallocated for the maximum
100100
# possible virtual size (for fast online VDI.resize)
101101
metaOverhead = vhdutil.calcOverheadEmpty(MSIZE)
102-
bitmapOverhead = vhdutil.calcOverheadBitmap(sizeVirt)
102+
bitmapOverhead = vhdutil.calcOverheadBitmap(sizeVirt, block_size)
103103
return calcSizeLV(sizeVirt + metaOverhead + bitmapOverhead)
104104

105105

@@ -208,7 +208,12 @@ def setSizeVirt(journaler, srUuid, vdiUuid, size, jFile):
208208
lvName = LV_PREFIX[vhdutil.VDI_TYPE_VHD] + vdiUuid
209209
vgName = VG_PREFIX + srUuid
210210
path = os.path.join(VG_LOCATION, vgName, lvName)
211-
inflate(journaler, srUuid, vdiUuid, calcSizeVHDLV(size))
211+
inflate(
212+
journaler,
213+
srUuid,
214+
vdiUuid,
215+
calcSizeVHDLV(size, vhdutil.getBlockSize(path))
216+
)
212217
vhdutil.setSizeVirt(path, size, jFile)
213218

214219

@@ -233,7 +238,8 @@ def attachThin(journaler, srUuid, vdiUuid):
233238
_tryAcquire(lock)
234239
lvmCache.refresh()
235240
vhdInfo = vhdutil.getVHDInfoLVM(lvName, extractUuid, vgName)
236-
newSize = calcSizeVHDLV(vhdInfo.sizeVirt)
241+
path = os.path.join(VG_LOCATION, vgName, lvName)
242+
newSize = calcSizeVHDLV(vhdInfo.sizeVirt, vhdutil.getBlockSize(path))
237243
currSizeLV = lvmCache.getSize(lvName)
238244
if newSize <= currSizeLV:
239245
return

libs/sm/vhdutil.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
MAX_CHAIN_SIZE = 30 # max VHD parent chain size
3131
VHD_UTIL = "/usr/bin/vhd-util"
3232
OPT_LOG_ERR = "--debug"
33-
VHD_BLOCK_SIZE = 2 * 1024 * 1024
33+
DEFAULT_VHD_BLOCK_SIZE = 2 * 1024 * 1024
3434
VHD_FOOTER_SIZE = 512
3535

3636
# lock to lock the entire SR for short ops
@@ -82,9 +82,9 @@ def calcOverheadEmpty(virtual_size):
8282
return overhead
8383

8484

85-
def calcOverheadBitmap(virtual_size):
86-
num_blocks = virtual_size // VHD_BLOCK_SIZE
87-
if virtual_size % VHD_BLOCK_SIZE:
85+
def calcOverheadBitmap(virtual_size, block_size=DEFAULT_VHD_BLOCK_SIZE):
86+
num_blocks = virtual_size // block_size
87+
if virtual_size % block_size:
8888
num_blocks += 1
8989
return num_blocks * 4096
9090

@@ -93,10 +93,18 @@ def ioretry(cmd, text=True):
9393
return util.ioretry(lambda: util.pread2(cmd, text=text),
9494
errlist=[errno.EIO, errno.EAGAIN])
9595

96+
def getBlockSize(path):
97+
cmd = [VHD_UTIL, "read", "-pn", path]
98+
ret = ioretry(cmd)
99+
for field in ret.split('\n'):
100+
field = field.lstrip()
101+
if not field.startswith("Block size"): continue
102+
return int(field.split(':')[1].lstrip().split()[0])
103+
raise util.SMException("Unable to find block size in VHD with path: {}".format(path))
104+
96105

97-
def convertAllocatedSizeToBytes(size):
98-
# Assume we have standard 2MB allocation blocks
99-
return size * 2 * 1024 * 1024
106+
def convertAllocatedSizeToBytes(size, block_size=DEFAULT_VHD_BLOCK_SIZE):
107+
return size * block_size
100108

101109

102110
def getVHDInfo(path, extractUuidFunction, includeParent=True):
@@ -120,7 +128,10 @@ def getVHDInfo(path, extractUuidFunction, includeParent=True):
120128
vhdInfo.parentUuid = extractUuidFunction(fields[nextIndex])
121129
nextIndex += 1
122130
vhdInfo.hidden = int(fields[nextIndex].replace("hidden: ", ""))
123-
vhdInfo.sizeAllocated = convertAllocatedSizeToBytes(int(fields[nextIndex+1]))
131+
vhdInfo.sizeAllocated = convertAllocatedSizeToBytes(
132+
int(fields[nextIndex+1]),
133+
getBlockSize(path)
134+
)
124135
vhdInfo.path = path
125136
return vhdInfo
126137

@@ -279,7 +290,7 @@ def setSizePhys(path, size, debug=True):
279290
def getAllocatedSize(path):
280291
cmd = [VHD_UTIL, "query", OPT_LOG_ERR, '-a', '-n', path]
281292
ret = ioretry(cmd)
282-
return convertAllocatedSizeToBytes(int(ret))
293+
return convertAllocatedSizeToBytes(int(ret), getBlockSize(path))
283294

284295
def killData(path):
285296
"zero out the disk (kill all data inside the VHD file)"
@@ -406,7 +417,7 @@ def repair(path):
406417
ioretry([VHD_UTIL, 'repair', '-n', path])
407418

408419

409-
def validate_and_round_vhd_size(size):
420+
def validate_and_round_vhd_size(size, block_size=DEFAULT_VHD_BLOCK_SIZE):
410421
""" Take the supplied vhd size, in bytes, and check it is positive and less
411422
that the maximum supported size, rounding up to the next block boundary
412423
"""
@@ -419,7 +430,7 @@ def validate_and_round_vhd_size(size):
419430
if size < MIN_VHD_SIZE:
420431
size = MIN_VHD_SIZE
421432

422-
size = util.roundup(VHD_BLOCK_SIZE, size)
433+
size = util.roundup(block_size, size)
423434

424435
return size
425436

tests/test_FileSR.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def test_create_vdi_vhd(self, mock_vhdutil):
327327
vdi = FakeFileVDI(sr, vdi_uuid)
328328
vdi.vdi_type = vhdutil.VDI_TYPE_VHD
329329
mock_vhdutil.validate_and_round_vhd_size.side_effect = vhdutil.validate_and_round_vhd_size
330+
mock_vhdutil.DEFAULT_VHD_BLOCK_SIZE = vhdutil.DEFAULT_VHD_BLOCK_SIZE
330331

331332
# Act
332333
vdi.create(sr_uuid, vdi_uuid, 20 * 1024 * 1024)

tests/test_LVHDSR.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,13 @@ def setUp(self):
515515
self.mock_lvhdutil.LV_PREFIX = lvhdutil.LV_PREFIX
516516
vhdutil_patcher = mock.patch('sm.drivers.LVHDSR.vhdutil', autospec=True)
517517
self.mock_vhdutil = vhdutil_patcher.start()
518+
self.mock_vhdutil.getBlockSize.return_value = vhdutil.DEFAULT_VHD_BLOCK_SIZE
518519
self.mock_vhdutil.VDI_TYPE_VHD = vhdutil.VDI_TYPE_VHD
519520
self.mock_vhdutil.VDI_TYPE_RAW = vhdutil.VDI_TYPE_RAW
520521
self.mock_vhdutil.MAX_CHAIN_SIZE = vhdutil.MAX_CHAIN_SIZE
522+
vdi_vhdutil_patcher = mock.patch('sm.VDI.vhdutil', autospec=True)
523+
self.mock_vdi_vhdutil = vdi_vhdutil_patcher.start()
524+
self.mock_vdi_vhdutil.getBlockSize.return_value = vhdutil.DEFAULT_VHD_BLOCK_SIZE
521525
lvutil_patcher = mock.patch('sm.drivers.LVHDSR.lvutil', autospec=True)
522526
self.mock_lvutil = lvutil_patcher.start()
523527
vdi_util_patcher = mock.patch('sm.VDI.util', autospec=True)

tests/test_vhdutil.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,10 +376,14 @@ def test_function(args, inp):
376376

377377
context.add_executable(VHD_UTIL, test_function)
378378
from sm.drivers import FileSR
379-
vhdinfo = vhdutil.getVHDInfo(TEST_VHD_PATH, FileSR.FileVDI.extractUuid)
379+
with unittest.mock.patch(
380+
"sm.vhdutil.getBlockSize",
381+
return_value=vhdutil.DEFAULT_VHD_BLOCK_SIZE
382+
):
383+
vhdinfo = vhdutil.getVHDInfo(TEST_VHD_PATH, FileSR.FileVDI.extractUuid)
380384

381-
# Act/Assert
382-
self.assertEqual(18856*2*1024*1024 , vhdinfo.sizeAllocated)
385+
# Act/Assert
386+
self.assertEqual(18856*2*1024*1024 , vhdinfo.sizeAllocated)
383387

384388
@testlib.with_context
385389
def test_get_allocated_size(self, context):
@@ -396,11 +400,35 @@ def test_function(args, inp):
396400
context.add_executable(VHD_UTIL, test_function)
397401

398402
# Act
399-
result = vhdutil.getAllocatedSize(TEST_VHD_NAME)
403+
result = 0
404+
with unittest.mock.patch(
405+
"sm.vhdutil.getBlockSize",
406+
return_value=vhdutil.DEFAULT_VHD_BLOCK_SIZE
407+
):
408+
result = vhdutil.getAllocatedSize(TEST_VHD_NAME)
400409

401410
# Assert
402411
self.assertEqual(18856*2*1024*1024, result)
403412
self.assertEqual(
404413
[VHD_UTIL, "query", "--debug", "-a",
405414
"-n", TEST_VHD_NAME],
406415
call_args)
416+
417+
@testlib.with_context
418+
def test_get_block_size(self, context):
419+
"""
420+
Test that vhdutil.getBlockSize returns the block size in bytes
421+
"""
422+
423+
# Arrange
424+
call_args = None
425+
426+
def test_function(args, inp):
427+
nonlocal call_args
428+
call_args = args
429+
return 0, "Header version: 0x00010000\nBlock size: {} (2 MB)".format(vhdutil.DEFAULT_VHD_BLOCK_SIZE), ""
430+
431+
context.add_executable(VHD_UTIL, test_function)
432+
433+
# Act/Assert
434+
self.assertEqual(vhdutil.DEFAULT_VHD_BLOCK_SIZE, vhdutil.getBlockSize(TEST_VHD_NAME))

0 commit comments

Comments
 (0)