Merge "Support Stream Optimized VMDKs"
This commit is contained in:
commit
2fa64aea03
|
@ -512,7 +512,7 @@ class VHDXInspector(FileInspector):
|
||||||
#
|
#
|
||||||
# https://www.vmware.com/app/vmdk/?src=vmdk
|
# https://www.vmware.com/app/vmdk/?src=vmdk
|
||||||
class VMDKInspector(FileInspector):
|
class VMDKInspector(FileInspector):
|
||||||
"""vmware VMDK format (monolithicSparse variant only)
|
"""vmware VMDK format (monolithicSparse and streamOptimized variants only)
|
||||||
|
|
||||||
This needs to store the 512 byte header and the descriptor region
|
This needs to store the 512 byte header and the descriptor region
|
||||||
which should be just after that. The descriptor region is some
|
which should be just after that. The descriptor region is some
|
||||||
|
@ -582,7 +582,7 @@ class VMDKInspector(FileInspector):
|
||||||
vmdktype = descriptor[type_idx:type_end]
|
vmdktype = descriptor[type_idx:type_end]
|
||||||
else:
|
else:
|
||||||
vmdktype = b'formatnotfound'
|
vmdktype = b'formatnotfound'
|
||||||
if vmdktype != b'monolithicSparse':
|
if vmdktype not in (b'monolithicSparse', b'streamOptimized'):
|
||||||
LOG.warning('Unsupported VMDK format %s', vmdktype)
|
LOG.warning('Unsupported VMDK format %s', vmdktype)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
@ -51,38 +51,51 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _create_img(self, fmt, size):
|
def _create_img(self, fmt, size, subformat=None):
|
||||||
if fmt == 'vhd':
|
if fmt == 'vhd':
|
||||||
# QEMU calls the vhd format vpc
|
# QEMU calls the vhd format vpc
|
||||||
fmt = 'vpc'
|
fmt = 'vpc'
|
||||||
|
|
||||||
fn = tempfile.mktemp(prefix='glance-unittest-formatinspector-',
|
opt = ''
|
||||||
|
prefix = 'glance-unittest-formatinspector-'
|
||||||
|
|
||||||
|
if subformat:
|
||||||
|
opt = ' -o subformat=%s' % subformat
|
||||||
|
prefix += subformat + '-'
|
||||||
|
|
||||||
|
fn = tempfile.mktemp(prefix=prefix,
|
||||||
suffix='.%s' % fmt)
|
suffix='.%s' % fmt)
|
||||||
self._created_files.append(fn)
|
self._created_files.append(fn)
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
'qemu-img create -f %s %s %i' % (fmt, fn, size),
|
'qemu-img create -f %s %s %s %i' % (fmt, opt, fn, size),
|
||||||
shell=True)
|
shell=True)
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
def _create_allocated_vmdk(self, size_mb):
|
def _create_allocated_vmdk(self, size_mb, subformat=None):
|
||||||
# We need a "big" VMDK file to exercise some parts of the code of the
|
# We need a "big" VMDK file to exercise some parts of the code of the
|
||||||
# format_inspector. A way to create one is to first create an empty
|
# format_inspector. A way to create one is to first create an empty
|
||||||
# file, and then to convert it with the -S 0 option.
|
# file, and then to convert it with the -S 0 option.
|
||||||
fn = tempfile.mktemp(prefix='glance-unittest-formatinspector-',
|
|
||||||
suffix='.vmdk')
|
|
||||||
self._created_files.append(fn)
|
|
||||||
zeroes = tempfile.mktemp(prefix='glance-unittest-formatinspector-',
|
|
||||||
suffix='.zero')
|
|
||||||
self._created_files.append(zeroes)
|
|
||||||
|
|
||||||
# Create an empty file
|
if subformat is None:
|
||||||
|
# Matches qemu-img default, see `qemu-img convert -O vmdk -o help`
|
||||||
|
subformat = 'monolithicSparse'
|
||||||
|
|
||||||
|
prefix = 'glance-unittest-formatinspector-%s-' % subformat
|
||||||
|
fn = tempfile.mktemp(prefix=prefix, suffix='.vmdk')
|
||||||
|
self._created_files.append(fn)
|
||||||
|
raw = tempfile.mktemp(prefix=prefix, suffix='.raw')
|
||||||
|
self._created_files.append(raw)
|
||||||
|
|
||||||
|
# Create a file with pseudo-random data, otherwise it will get
|
||||||
|
# compressed in the streamOptimized format
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
'dd if=/dev/zero of=%s bs=1M count=%i' % (zeroes, size_mb),
|
'dd if=/dev/urandom of=%s bs=1M count=%i' % (raw, size_mb),
|
||||||
shell=True)
|
shell=True)
|
||||||
|
|
||||||
# Convert it to VMDK
|
# Convert it to VMDK
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
'qemu-img convert -f raw -O vmdk -S 0 %s %s' % (zeroes, fn),
|
'qemu-img convert -f raw -O vmdk -o subformat=%s -S 0 %s %s' % (
|
||||||
|
subformat, raw, fn),
|
||||||
shell=True)
|
shell=True)
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
@ -101,8 +114,9 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
wrapper.close()
|
wrapper.close()
|
||||||
return fmt
|
return fmt
|
||||||
|
|
||||||
def _test_format_at_image_size(self, format_name, image_size):
|
def _test_format_at_image_size(self, format_name, image_size,
|
||||||
img = self._create_img(format_name, image_size)
|
subformat=None):
|
||||||
|
img = self._create_img(format_name, image_size, subformat=subformat)
|
||||||
|
|
||||||
# Some formats have internal alignment restrictions making this not
|
# Some formats have internal alignment restrictions making this not
|
||||||
# always exactly like image_size, so get the real value for comparison
|
# always exactly like image_size, so get the real value for comparison
|
||||||
|
@ -124,11 +138,12 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
'Format used more than 512KiB of memory: %s' % (
|
'Format used more than 512KiB of memory: %s' % (
|
||||||
fmt.context_info))
|
fmt.context_info))
|
||||||
|
|
||||||
def _test_format(self, format_name):
|
def _test_format(self, format_name, subformat=None):
|
||||||
# Try a few different image sizes, including some odd and very small
|
# Try a few different image sizes, including some odd and very small
|
||||||
# sizes
|
# sizes
|
||||||
for image_size in (512, 513, 2057, 7):
|
for image_size in (512, 513, 2057, 7):
|
||||||
self._test_format_at_image_size(format_name, image_size * units.Mi)
|
self._test_format_at_image_size(format_name, image_size * units.Mi,
|
||||||
|
subformat=subformat)
|
||||||
|
|
||||||
def test_qcow2(self):
|
def test_qcow2(self):
|
||||||
self._test_format('qcow2')
|
self._test_format('qcow2')
|
||||||
|
@ -142,12 +157,15 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
def test_vmdk(self):
|
def test_vmdk(self):
|
||||||
self._test_format('vmdk')
|
self._test_format('vmdk')
|
||||||
|
|
||||||
def test_vmdk_bad_descriptor_offset(self):
|
def test_vmdk_stream_optimized(self):
|
||||||
|
self._test_format('vmdk', 'streamOptimized')
|
||||||
|
|
||||||
|
def _test_vmdk_bad_descriptor_offset(self, subformat=None):
|
||||||
format_name = 'vmdk'
|
format_name = 'vmdk'
|
||||||
image_size = 10 * units.Mi
|
image_size = 10 * units.Mi
|
||||||
descriptorOffsetAddr = 0x1c
|
descriptorOffsetAddr = 0x1c
|
||||||
BAD_ADDRESS = 0x400
|
BAD_ADDRESS = 0x400
|
||||||
img = self._create_img(format_name, image_size)
|
img = self._create_img(format_name, image_size, subformat=subformat)
|
||||||
|
|
||||||
# Corrupt the header
|
# Corrupt the header
|
||||||
fd = open(img, 'r+b')
|
fd = open(img, 'r+b')
|
||||||
|
@ -167,7 +185,13 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
'size %i block %i') % (format_name, image_size,
|
'size %i block %i') % (format_name, image_size,
|
||||||
block_size))
|
block_size))
|
||||||
|
|
||||||
def test_vmdk_bad_descriptor_mem_limit(self):
|
def test_vmdk_bad_descriptor_offset(self):
|
||||||
|
self._test_vmdk_bad_descriptor_offset()
|
||||||
|
|
||||||
|
def test_vmdk_bad_descriptor_offset_stream_optimized(self):
|
||||||
|
self._test_vmdk_bad_descriptor_offset(subformat='streamOptimized')
|
||||||
|
|
||||||
|
def _test_vmdk_bad_descriptor_mem_limit(self, subformat=None):
|
||||||
format_name = 'vmdk'
|
format_name = 'vmdk'
|
||||||
image_size = 5 * units.Mi
|
image_size = 5 * units.Mi
|
||||||
virtual_size = 5 * units.Mi
|
virtual_size = 5 * units.Mi
|
||||||
|
@ -176,7 +200,8 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
twoMBInSectors = (2 << 20) // 512
|
twoMBInSectors = (2 << 20) // 512
|
||||||
# We need a big VMDK because otherwise we will not have enough data to
|
# We need a big VMDK because otherwise we will not have enough data to
|
||||||
# fill-up the CaptureRegion.
|
# fill-up the CaptureRegion.
|
||||||
img = self._create_allocated_vmdk(image_size // units.Mi)
|
img = self._create_allocated_vmdk(image_size // units.Mi,
|
||||||
|
subformat=subformat)
|
||||||
|
|
||||||
# Corrupt the end of descriptor address so it "ends" at 2MB
|
# Corrupt the end of descriptor address so it "ends" at 2MB
|
||||||
fd = open(img, 'r+b')
|
fd = open(img, 'r+b')
|
||||||
|
@ -200,6 +225,12 @@ class TestFormatInspectors(test_utils.BaseTestCase):
|
||||||
'Format used more than 1.5MiB of memory: %s' % (
|
'Format used more than 1.5MiB of memory: %s' % (
|
||||||
fmt.context_info))
|
fmt.context_info))
|
||||||
|
|
||||||
|
def test_vmdk_bad_descriptor_mem_limit(self):
|
||||||
|
self._test_vmdk_bad_descriptor_mem_limit()
|
||||||
|
|
||||||
|
def test_vmdk_bad_descriptor_mem_limit_stream_optimized(self):
|
||||||
|
self._test_vmdk_bad_descriptor_mem_limit(subformat='streamOptimized')
|
||||||
|
|
||||||
def test_vdi(self):
|
def test_vdi(self):
|
||||||
self._test_format('vdi')
|
self._test_format('vdi')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue