102
115
shutil.rmtree(staging_path)
118
def _handle_old_style_images(staging_path):
119
"""Rename files to conform to new image format, if needed.
123
snap.vhd -> image.vhd -> base.vhd
127
0.vhd -> 1.vhd -> ... (n-1).vhd
129
The New-Style format has the benefit of being able to support a VDI chain
133
for filename in ('snap.vhd', 'image.vhd', 'base.vhd'):
134
path = os.path.join(staging_path, filename)
135
if os.path.exists(path):
136
_rename(path, os.path.join(staging_path, "%d.vhd" % file_num))
140
def _assert_vhd_not_hidden(path):
141
"""Sanity check to ensure that only appropriate VHDs are marked as hidden.
143
If this flag is incorrectly set, then when we move the VHD into the SR, it
144
will be deleted out from under us.
146
query_cmd = "vhd-util query -n %(path)s -f" % locals()
147
query_proc = make_subprocess(query_cmd, stdout=True, stderr=True)
148
out, err = finish_subprocess(query_proc, query_cmd)
150
for line in out.splitlines():
151
if line.startswith('hidden'):
152
value = line.split(':')[1].strip()
155
"VHD %(path)s is marked as hidden without child" %
159
def _validate_footer_timestamp(vdi_path):
161
This check ensures that the timestamps listed in the VHD footer aren't in
162
the future. This can occur during a migration if the clocks on the the two
163
Dom0's are out-of-sync. This would corrupt the SR if it were imported, so
164
generate an exception to bail.
166
check_cmd = "vhd-util check -n %(vdi_path)s -p" % locals()
167
check_proc = make_subprocess(check_cmd, stdout=True, stderr=True)
168
out, err = finish_subprocess(
169
check_proc, check_cmd, ok_exit_codes=[0, 22])
170
first_line = out.splitlines()[0].strip()
172
if 'primary footer invalid' in first_line:
173
raise Exception("VDI '%(vdi_path)s' has timestamp in the future,"
174
" ensure source and destination host machines have"
175
" time set correctly" % locals())
176
elif check_proc.returncode != 0:
177
raise Exception("Unexpected output '%(out)s' from vhd-util" %
181
def _validate_vdi_chain(vdi_path):
183
This check ensures that the parent pointers on the VHDs are valid
184
before we move the VDI chain to the SR. This is *very* important
185
because a bad parent pointer will corrupt the SR causing a cascade of
188
def get_parent_path(path):
189
query_cmd = "vhd-util query -n %(path)s -p" % locals()
190
query_proc = make_subprocess(query_cmd, stdout=True, stderr=True)
191
out, err = finish_subprocess(
192
query_proc, query_cmd, ok_exit_codes=[0, 22])
193
first_line = out.splitlines()[0].strip()
195
if first_line.endswith(".vhd"):
197
elif 'has no parent' in first_line:
199
elif 'query failed' in first_line:
200
raise Exception("VDI '%(path)s' not present which breaks"
201
" the VDI chain, bailing out" % locals())
203
raise Exception("Unexpected output '%(out)s' from vhd-util" %
208
_validate_footer_timestamp(cur_path)
209
cur_path = get_parent_path(cur_path)
212
def _validate_sequenced_vhds(staging_path):
213
"""This check ensures that the VHDs in the staging area are sequenced
214
properly from 0 to n-1 with no gaps.
217
filenames = os.listdir(staging_path)
218
for filename in filenames:
219
if not filename.endswith('.vhd'):
222
if filename == "swap.vhd":
225
vhd_path = os.path.join(staging_path, "%d.vhd" % seq_num)
226
if not os.path.exists(vhd_path):
227
raise Exception("Corrupt image. Expected seq number: %d. Files: %s"
228
% (seq_num, filenames))
105
233
def import_vhds(sr_path, staging_path, uuid_stack):
106
"""Import the VHDs found in the staging path.
108
We cannot extract VHDs directly into the SR since they don't yet have
109
UUIDs, aren't properly associated with each other, and would be subject to
110
a race-condition of one-file being present and the other not being
113
To avoid these we problems, we use a staging area to fixup the VHDs before
114
moving them into the SR. The steps involved are:
116
1. Extracting tarball into staging area (done prior to this call)
118
2. Renaming VHDs to use UUIDs ('snap.vhd' -> 'ffff-aaaa-...vhd')
120
3. Linking VHDs together if there's a snap.vhd
122
4. Pseudo-atomically moving the images into the SR. (It's not really
123
atomic because it takes place as multiple os.rename operations;
124
however, the chances of an SR.scan occuring between the rename()s
125
invocations is so small that we can safely ignore it)
127
Returns: A dict of the VDIs imported. For example:
234
"""Move VHDs from staging area into the SR.
236
The staging area is necessary because we need to perform some fixups
237
(assigning UUIDs, relinking the VHD chain) before moving into the SR,
238
otherwise the SR manager process could potentially delete the VHDs out from
241
Returns: A dict of imported VHDs:
129
243
{'root': {'uuid': 'ffff-aaaa'},
130
244
'swap': {'uuid': 'ffff-bbbb'}}
132
def rename_with_uuid(orig_path):
133
"""Rename VHD using UUID so that it will be recognized by SR on a
136
Since Python2.4 doesn't have the `uuid` module, we pass a stack of
137
pre-computed UUIDs from the compute worker.
139
orig_dirname = os.path.dirname(orig_path)
140
uuid = uuid_stack.pop()
141
new_path = os.path.join(orig_dirname, "%s.vhd" % uuid)
142
os.rename(orig_path, new_path)
143
return new_path, uuid
145
def link_vhds(child_path, parent_path):
146
"""Use vhd-util to associate the snapshot VHD with its base_copy.
148
This needs to be done before we move both VHDs into the SR to prevent
149
the base_copy from being DOA (deleted-on-arrival).
151
modify_cmd = ("vhd-util modify -n %(child_path)s -p %(parent_path)s"
153
modify_proc = make_subprocess(modify_cmd, stderr=True)
154
finish_subprocess(modify_proc, modify_cmd)
156
def move_into_sr(orig_path):
157
"""Move a file into the SR"""
158
filename = os.path.basename(orig_path)
159
new_path = os.path.join(sr_path, filename)
160
os.rename(orig_path, new_path)
163
def assert_vhd_not_hidden(path):
165
This is a sanity check on the image; if a snap.vhd isn't
166
present, then the image.vhd better not be marked 'hidden' or it will
167
be deleted when moved into the SR.
169
query_cmd = "vhd-util query -n %(path)s -f" % locals()
170
query_proc = make_subprocess(query_cmd, stdout=True, stderr=True)
171
out, err = finish_subprocess(query_proc, query_cmd)
173
for line in out.splitlines():
174
if line.startswith('hidden'):
175
value = line.split(':')[1].strip()
178
"VHD %(path)s is marked as hidden without child" %
181
def prepare_if_exists(staging_path, vhd_name, parent_path=None):
183
Check for existance of a particular VHD in the staging path and
184
preparing it for moving into the SR.
186
Returns: Tuple of (Path to move into the SR, VDI_UUID)
187
None, if the vhd_name doesn't exist in the staging path
189
If the VHD exists, we will do the following:
190
1. Rename it with a UUID.
191
2. If parent_path exists, we'll link up the VHDs.
193
orig_path = os.path.join(staging_path, vhd_name)
194
if not os.path.exists(orig_path):
196
new_path, vdi_uuid = rename_with_uuid(orig_path)
246
_handle_old_style_images(staging_path)
247
_validate_sequenced_vhds(staging_path)
252
# Collect sequenced VHDs and assign UUIDs to them
255
orig_vhd_path = os.path.join(staging_path, "%d.vhd" % seq_num)
256
if not os.path.exists(orig_vhd_path):
259
# Rename (0, 1 .. N).vhd -> aaaa-bbbb-cccc-dddd.vhd
260
vhd_uuid = uuid_stack.pop()
261
vhd_path = os.path.join(staging_path, "%s.vhd" % vhd_uuid)
262
_rename(orig_vhd_path, vhd_path)
265
leaf_vhd_path = vhd_path
266
leaf_vhd_uuid = vhd_uuid
268
files_to_move.append(vhd_path)
271
# Re-link VHDs, in reverse order, from base-copy -> leaf
273
for vhd_path in reversed(files_to_move):
198
# NOTE(sirp): this step is necessary so that an SR scan won't
199
# delete the base_copy out from under us (since it would be
201
link_vhds(new_path, parent_path)
202
return (new_path, vdi_uuid)
204
def validate_vdi_chain(vdi_path):
206
This check ensures that the parent pointers on the VHDs are valid
207
before we move the VDI chain to the SR. This is *very* important
208
because a bad parent pointer will corrupt the SR causing a cascade of
211
def get_parent_path(path):
212
query_cmd = "vhd-util query -n %(path)s -p" % locals()
213
query_proc = make_subprocess(query_cmd, stdout=True, stderr=True)
214
out, err = finish_subprocess(
215
query_proc, query_cmd, ok_exit_codes=[0, 22])
216
first_line = out.splitlines()[0].strip()
217
if first_line.endswith(".vhd"):
219
elif 'has no parent' in first_line:
221
elif 'query failed' in first_line:
222
raise Exception("VDI '%(path)s' not present which breaks"
223
" the VDI chain, bailing out" % locals())
225
raise Exception("Unexpected output '%(out)s' from vhd-util" %
230
cur_path = get_parent_path(cur_path)
236
base_info = prepare_if_exists(staging_path, 'base.vhd')
238
paths_to_move.append(base_info[0])
239
image_parent = base_info[0]
241
image_info = prepare_if_exists(staging_path, 'image.vhd', image_parent)
243
raise Exception("Invalid image: image.vhd not present")
245
paths_to_move.insert(0, image_info[0])
247
snap_info = prepare_if_exists(staging_path, 'snap.vhd',
250
validate_vdi_chain(snap_info[0])
251
# NOTE(sirp): this is an insert rather than an append since the
252
# 'snapshot' vhd needs to be copied into the SR before the base copy.
253
# If it doesn't, then there is a possibliity that snapwatchd will
254
# delete the base_copy since it is an unreferenced parent.
255
paths_to_move.insert(0, snap_info[0])
256
# We return this snap as the VDI instead of image.vhd
257
imported_vhds['root'] = dict(uuid=snap_info[1])
259
validate_vdi_chain(image_info[0])
260
assert_vhd_not_hidden(image_info[0])
261
# If there's no snap, we return the image.vhd UUID
262
imported_vhds['root'] = dict(uuid=image_info[1])
264
swap_info = prepare_if_exists(staging_path, 'swap.vhd')
266
assert_vhd_not_hidden(swap_info[0])
267
paths_to_move.append(swap_info[0])
268
imported_vhds['swap'] = dict(uuid=swap_info[1])
270
for path in paths_to_move:
276
modify_cmd = ("vhd-util modify -n %(vhd_path)s"
277
" -p %(parent_path)s" % locals())
278
modify_proc = make_subprocess(modify_cmd, stderr=True)
279
finish_subprocess(modify_proc, modify_cmd)
281
parent_path = vhd_path
283
# Sanity check the leaf VHD
284
_assert_vhd_not_hidden(leaf_vhd_path)
285
_validate_vdi_chain(leaf_vhd_path)
287
imported_vhds["root"] = {"uuid": leaf_vhd_uuid}
289
# Handle swap file if present
290
orig_swap_path = os.path.join(staging_path, "swap.vhd")
291
if os.path.exists(orig_swap_path):
292
# Rename swap.vhd -> aaaa-bbbb-cccc-dddd.vhd
293
vhd_uuid = uuid_stack.pop()
294
swap_path = os.path.join(staging_path, "%s.vhd" % vhd_uuid)
295
_rename(orig_swap_path, swap_path)
297
_assert_vhd_not_hidden(swap_path)
299
imported_vhds["swap"] = {"uuid": vhd_uuid}
300
files_to_move.append(swap_path)
303
for orig_path in files_to_move:
304
new_path = os.path.join(sr_path, os.path.basename(orig_path))
305
_rename(orig_path, new_path)
273
307
return imported_vhds
276
def prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids):
277
"""Hard-link VHDs into staging area with appropriate filename
278
('snap' or 'image.vhd')
280
for name, uuid in vdi_uuids.items():
282
source = os.path.join(sr_path, "%s.vhd" % uuid)
283
link_name = os.path.join(staging_path, "%s.vhd" % name)
284
os.link(source, link_name)
310
def prepare_staging_area(sr_path, staging_path, vdi_uuids, seq_num=0):
311
"""Hard-link VHDs into staging area."""
312
for vdi_uuid in vdi_uuids:
313
source = os.path.join(sr_path, "%s.vhd" % vdi_uuid)
314
link_name = os.path.join(staging_path, "%d.vhd" % seq_num)
315
_link(source, link_name)
287
319
def create_tarball(fileobj, path, callback=None):