Skip to content

Commit ef22a74

Browse files
fwieselrajivmucheli
authored andcommitted
Support Stream Optimized VMDKs
Stream optimized VMDKs are also monolithic disks images, and start with the same sparse extend header as normal monolithic sparse files, so we can parse the virtual disk size in the same manner. See "VMware Virtual Disks Virtual Disk Format 1.1" p. 17. > Header and Footer > The header and the footer are both described by the same SparseExtentHeader > structure shown in Hosted Sparse Extent Header on page 8. Closes-Bug: #2052291 Change-Id: I7d63951ff080dc699b8d11babc0a5998d90621e4 Co-Authored-By: Rajiv Mucheli <[email protected]> (cherry picked from commit 5e7e6bf) (cherry picked from commit 9d45e8d4b87e992be23974a831811bff563ce721)
1 parent a45667b commit ef22a74

File tree

2 files changed

+53
-22
lines changed

2 files changed

+53
-22
lines changed

glance/common/format_inspector.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ def __str__(self):
512512
#
513513
# https://www.vmware.com/app/vmdk/?src=vmdk
514514
class VMDKInspector(FileInspector):
515-
"""vmware VMDK format (monolithicSparse variant only)
515+
"""vmware VMDK format (monolithicSparse and streamOptimized variants only)
516516
517517
This needs to store the 512 byte header and the descriptor region
518518
which should be just after that. The descriptor region is some
@@ -582,7 +582,7 @@ def virtual_size(self):
582582
vmdktype = descriptor[type_idx:type_end]
583583
else:
584584
vmdktype = b'formatnotfound'
585-
if vmdktype != b'monolithicSparse':
585+
if vmdktype not in (b'monolithicSparse', b'streamOptimized'):
586586
LOG.warning('Unsupported VMDK format %s', vmdktype)
587587
return 0
588588

glance/tests/unit/common/test_format_inspector.py

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,38 +51,51 @@ def tearDown(self):
5151
except Exception:
5252
pass
5353

54-
def _create_img(self, fmt, size):
54+
def _create_img(self, fmt, size, subformat=None):
5555
if fmt == 'vhd':
5656
# QEMU calls the vhd format vpc
5757
fmt = 'vpc'
5858

59-
fn = tempfile.mktemp(prefix='glance-unittest-formatinspector-',
59+
opt = ''
60+
prefix = 'glance-unittest-formatinspector-'
61+
62+
if subformat:
63+
opt = ' -o subformat=%s' % subformat
64+
prefix += subformat + '-'
65+
66+
fn = tempfile.mktemp(prefix=prefix,
6067
suffix='.%s' % fmt)
6168
self._created_files.append(fn)
6269
subprocess.check_output(
63-
'qemu-img create -f %s %s %i' % (fmt, fn, size),
70+
'qemu-img create -f %s %s %s %i' % (fmt, opt, fn, size),
6471
shell=True)
6572
return fn
6673

67-
def _create_allocated_vmdk(self, size_mb):
74+
def _create_allocated_vmdk(self, size_mb, subformat=None):
6875
# We need a "big" VMDK file to exercise some parts of the code of the
6976
# format_inspector. A way to create one is to first create an empty
7077
# file, and then to convert it with the -S 0 option.
71-
fn = tempfile.mktemp(prefix='glance-unittest-formatinspector-',
72-
suffix='.vmdk')
78+
79+
if subformat is None:
80+
# Matches qemu-img default, see `qemu-img convert -O vmdk -o help`
81+
subformat = 'monolithicSparse'
82+
83+
prefix = 'glance-unittest-formatinspector-%s-' % subformat
84+
fn = tempfile.mktemp(prefix=prefix, suffix='.vmdk')
7385
self._created_files.append(fn)
74-
zeroes = tempfile.mktemp(prefix='glance-unittest-formatinspector-',
75-
suffix='.zero')
76-
self._created_files.append(zeroes)
86+
raw = tempfile.mktemp(prefix=prefix, suffix='.raw')
87+
self._created_files.append(raw)
7788

78-
# Create an empty file
89+
# Create a file with pseudo-random data, otherwise it will get
90+
# compressed in the streamOptimized format
7991
subprocess.check_output(
80-
'dd if=/dev/zero of=%s bs=1M count=%i' % (zeroes, size_mb),
92+
'dd if=/dev/urandom of=%s bs=1M count=%i' % (raw, size_mb),
8193
shell=True)
8294

8395
# Convert it to VMDK
8496
subprocess.check_output(
85-
'qemu-img convert -f raw -O vmdk -S 0 %s %s' % (zeroes, fn),
97+
'qemu-img convert -f raw -O vmdk -o subformat=%s -S 0 %s %s' % (
98+
subformat, raw, fn),
8699
shell=True)
87100
return fn
88101

@@ -101,8 +114,9 @@ def _test_format_at_block_size(self, format_name, img, block_size):
101114
wrapper.close()
102115
return fmt
103116

104-
def _test_format_at_image_size(self, format_name, image_size):
105-
img = self._create_img(format_name, image_size)
117+
def _test_format_at_image_size(self, format_name, image_size,
118+
subformat=None):
119+
img = self._create_img(format_name, image_size, subformat=subformat)
106120

107121
# Some formats have internal alignment restrictions making this not
108122
# always exactly like image_size, so get the real value for comparison
@@ -124,11 +138,12 @@ def _test_format_at_image_size(self, format_name, image_size):
124138
'Format used more than 512KiB of memory: %s' % (
125139
fmt.context_info))
126140

127-
def _test_format(self, format_name):
141+
def _test_format(self, format_name, subformat=None):
128142
# Try a few different image sizes, including some odd and very small
129143
# sizes
130144
for image_size in (512, 513, 2057, 7):
131-
self._test_format_at_image_size(format_name, image_size * units.Mi)
145+
self._test_format_at_image_size(format_name, image_size * units.Mi,
146+
subformat=subformat)
132147

133148
def test_qcow2(self):
134149
self._test_format('qcow2')
@@ -142,12 +157,15 @@ def test_vhdx(self):
142157
def test_vmdk(self):
143158
self._test_format('vmdk')
144159

145-
def test_vmdk_bad_descriptor_offset(self):
160+
def test_vmdk_stream_optimized(self):
161+
self._test_format('vmdk', 'streamOptimized')
162+
163+
def _test_vmdk_bad_descriptor_offset(self, subformat=None):
146164
format_name = 'vmdk'
147165
image_size = 10 * units.Mi
148166
descriptorOffsetAddr = 0x1c
149167
BAD_ADDRESS = 0x400
150-
img = self._create_img(format_name, image_size)
168+
img = self._create_img(format_name, image_size, subformat=subformat)
151169

152170
# Corrupt the header
153171
fd = open(img, 'r+b')
@@ -167,7 +185,13 @@ def test_vmdk_bad_descriptor_offset(self):
167185
'size %i block %i') % (format_name, image_size,
168186
block_size))
169187

170-
def test_vmdk_bad_descriptor_mem_limit(self):
188+
def test_vmdk_bad_descriptor_offset(self):
189+
self._test_vmdk_bad_descriptor_offset()
190+
191+
def test_vmdk_bad_descriptor_offset_stream_optimized(self):
192+
self._test_vmdk_bad_descriptor_offset(subformat='streamOptimized')
193+
194+
def _test_vmdk_bad_descriptor_mem_limit(self, subformat=None):
171195
format_name = 'vmdk'
172196
image_size = 5 * units.Mi
173197
virtual_size = 5 * units.Mi
@@ -176,7 +200,8 @@ def test_vmdk_bad_descriptor_mem_limit(self):
176200
twoMBInSectors = (2 << 20) // 512
177201
# We need a big VMDK because otherwise we will not have enough data to
178202
# fill-up the CaptureRegion.
179-
img = self._create_allocated_vmdk(image_size // units.Mi)
203+
img = self._create_allocated_vmdk(image_size // units.Mi,
204+
subformat=subformat)
180205

181206
# Corrupt the end of descriptor address so it "ends" at 2MB
182207
fd = open(img, 'r+b')
@@ -200,6 +225,12 @@ def test_vmdk_bad_descriptor_mem_limit(self):
200225
'Format used more than 1.5MiB of memory: %s' % (
201226
fmt.context_info))
202227

228+
def test_vmdk_bad_descriptor_mem_limit(self):
229+
self._test_vmdk_bad_descriptor_mem_limit()
230+
231+
def test_vmdk_bad_descriptor_mem_limit_stream_optimized(self):
232+
self._test_vmdk_bad_descriptor_mem_limit(subformat='streamOptimized')
233+
203234
def test_vdi(self):
204235
self._test_format('vdi')
205236

0 commit comments

Comments
 (0)