Coverage for drivers/srmetadata.py : 66%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) Citrix Systems Inc.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published
5# by the Free Software Foundation; version 2.1 only.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software Foundation, Inc.,
14# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15#
16# Functions to read and write SR metadata
17#
18from io import SEEK_SET
20import util
21import metadata
22import os
23import xs_errors
24import lvutil
25import xml.sax.saxutils
27SECTOR_SIZE = 512
28XML_HEADER = "<?xml version=\"1.0\" ?>"
29SECTOR2_FMT = "%s%s%s"
30MAX_METADATA_LENGTH_SIZE = 10
31LEN_FMT = "%" + "-%ds" % MAX_METADATA_LENGTH_SIZE
32SECTOR_STRUCT = "%-512s"
33OFFSET_TAG = 'offset'
35# define xml tags for metadata
36ALLOCATION_TAG = 'allocation'
37NAME_LABEL_TAG = 'name_label'
38NAME_DESCRIPTION_TAG = 'name_description'
39VDI_TAG = 'vdi'
40VDI_CLOSING_TAG = '</%s>' % VDI_TAG
41VDI_DELETED_TAG = 'deleted'
42UUID_TAG = 'uuid'
43IS_A_SNAPSHOT_TAG = 'is_a_snapshot'
44SNAPSHOT_OF_TAG = 'snapshot_of'
45TYPE_TAG = 'type'
46VDI_TYPE_TAG = 'vdi_type'
47READ_ONLY_TAG = 'read_only'
48MANAGED_TAG = 'managed'
49SNAPSHOT_TIME_TAG = 'snapshot_time'
50METADATA_OF_POOL_TAG = 'metadata_of_pool'
51SVID_TAG = 'svid'
52LUN_LABEL_TAG = 'll'
53VDI_SECTOR_1 = "<%s><%s>%s</%s><%s>%s</%s>" % (VDI_TAG,
54 NAME_LABEL_TAG,
55 '%s',
56 NAME_LABEL_TAG,
57 NAME_DESCRIPTION_TAG,
58 '%s',
59 NAME_DESCRIPTION_TAG)
60MAX_VDI_NAME_LABEL_DESC_LENGTH = SECTOR_SIZE - 2 * len(NAME_LABEL_TAG) - \
61 2 * len(NAME_DESCRIPTION_TAG) - len(VDI_TAG) - 12
63ATOMIC_UPDATE_PARAMS_AND_OFFSET = {NAME_LABEL_TAG: 2,
64 NAME_DESCRIPTION_TAG: 3}
65SR_INFO_SIZE_IN_SECTORS = 4
66HEADER_SEP = ':'
67METADATA_UPDATE_OBJECT_TYPE_TAG = 'objtype'
68METADATA_OBJECT_TYPE_SR = 'sr'
69METADATA_OBJECT_TYPE_VDI = 'vdi'
70METADATA_BLK_SIZE = 512
73# ----------------- # General helper functions - begin # -----------------
74def get_min_blk_size_wrapper(fd):
75 return METADATA_BLK_SIZE
78def open_file(path, write=False):
79 if write:
80 try:
81 file_p = open(path, 'wb+')
82 except OSError as e:
83 raise OSError(
84 "Failed to open file %s for read-write. Error: %s" %
85 (path, e.errno))
86 else:
87 try:
88 file_p = open(path, 'rb')
89 except OSError as e:
90 raise OSError(
91 "Failed to open file %s for read. Error: %s" %
92 (path, e.errno))
93 return file_p
96def file_write_wrapper(fd, offset, blocksize, data, length):
97 try:
98 newlength = length
99 if length % blocksize:
100 newlength = length + (blocksize - length % blocksize)
101 fd.seek(offset, SEEK_SET)
102 to_write = data + ' ' * (newlength - length)
103 result = fd.write(to_write.encode())
104 except OSError as e:
105 raise OSError(
106 "Failed to write file with params %s. Error: %s" %
107 ([fd, offset, blocksize, data, length], e.errno))
108 return result
111def file_read_wrapper(fd, offset, bytesToRead, min_block_size):
112 try:
113 fd.seek(offset, SEEK_SET)
114 result = fd.read(bytesToRead)
115 except OSError as e:
116 raise OSError(
117 "Failed to read file with params %s. Error: %s" %
118 ([fd, offset, min_block_size, bytesToRead], e.errno))
119 return result.decode()
122# get a range which is block aligned, contains 'offset' and allows
123# length bytes to be written
124def getBlockAlignedRange(block_size, offset, length):
125 lower = 0
126 if offset % block_size == 0: 126 ↛ 129line 126 didn't jump to line 129, because the condition on line 126 was never false
127 lower = offset
128 else:
129 lower = offset - offset % block_size
131 upper = lower + block_size
133 while upper < (lower + length):
134 upper += block_size
136 return (lower, upper)
139def buildHeader(len, major=metadata.MD_MAJOR, minor=metadata.MD_MINOR):
140 # build the header, which is the first sector
141 output = ("%s%s%s%s%s%s%s" % (metadata.HDR_STRING,
142 HEADER_SEP,
143 LEN_FMT,
144 HEADER_SEP,
145 str(major),
146 HEADER_SEP,
147 str(minor)
148 )) % len
149 return output
152def unpackHeader(header):
153 vals = header.split(HEADER_SEP)
154 if len(vals) != 4 or vals[0] != metadata.HDR_STRING: 154 ↛ 155line 154 didn't jump to line 155, because the condition on line 154 was never true
155 util.SMlog("Exception unpacking metadata header: "
156 "Error: Bad header '%s'" % (header))
157 raise xs_errors.XenError('MetadataError', \
158 opterr='Bad header')
159 return (vals[0], vals[1], vals[2], vals[3])
162def getSector(str):
163 sector = SECTOR_STRUCT % str
164 return sector
167def getSectorAlignedXML(tagName, value):
168 # truncate data if we breach the 512 limit
169 if len("<%s>%s</%s>" % (tagName, value, tagName)) > SECTOR_SIZE: 169 ↛ 170line 169 didn't jump to line 170, because the condition on line 169 was never true
170 length = util.unictrunc(value, SECTOR_SIZE - 2 * len(tagName) - 5)
171 util.SMlog('warning: SR ' + tagName + ' truncated from ' \
172 + str(len(value)) + ' to ' + str(length) + ' bytes')
173 value = value[:length]
175 return "<%s>%s</%s>" % (tagName, value, tagName)
178def getXMLTag(tagName):
179 return "<%s>%s</%s>" % (tagName, '%s', tagName)
182def updateLengthInHeader(fd, length, major=metadata.MD_MAJOR, \
183 minor=metadata.MD_MINOR):
184 try:
185 min_block_size = get_min_blk_size_wrapper(fd)
186 md = ''
187 md = file_read_wrapper(fd, 0, min_block_size, min_block_size)
188 updated_md = buildHeader(length, major, minor)
189 updated_md += md[SECTOR_SIZE:]
191 # Now write the new length
192 file_write_wrapper(fd, 0, min_block_size, updated_md, len(updated_md))
193 except Exception as e:
194 util.SMlog("Exception updating metadata length with length: %d."
195 "Error: %s" % (length, str(e)))
196 raise
199def getMetadataLength(fd):
200 try:
201 min_blk_size = get_min_blk_size_wrapper(fd)
202 sector1 = \
203 file_read_wrapper(fd, 0, SECTOR_SIZE, min_blk_size).strip()
204 hdr = unpackHeader(sector1)
205 len = int(hdr[1])
206 return len
207 except Exception as e:
208 util.SMlog("Exception getting metadata length: "
209 "Error: %s" % str(e))
210 raise
213# ----------------- # General helper functions - end # -----------------
214class MetadataHandler:
216 VDI_INFO_SIZE_IN_SECTORS = None
218 # constructor
219 def __init__(self, path=None, write=True):
221 self.fd = None
222 self.path = path
223 if self.path is not None: 223 ↛ exitline 223 didn't return from function '__init__', because the condition on line 223 was never false
224 self.fd = open_file(self.path, write)
226 def __del__(self):
227 if self.fd: 227 ↛ exitline 227 didn't return from function '__del__', because the condition on line 227 was never false
228 self.fd.close()
230 def spaceAvailableForVdis(self, count):
231 raise NotImplementedError("spaceAvailableForVdis is undefined")
233 # common utility functions
234 def getMetadata(self, params={}):
235 try:
236 sr_info = {}
237 vdi_info = {}
238 try:
239 md = self.getMetadataInternal(params)
240 sr_info = md['sr_info']
241 vdi_info = md['vdi_info']
242 except:
243 # Maybe there is no metadata yet
244 pass
246 except Exception as e:
247 util.SMlog('Exception getting metadata. Error: %s' % str(e))
248 raise xs_errors.XenError('MetadataError', \
249 opterr='%s' % str(e))
251 return (sr_info, vdi_info)
253 def writeMetadata(self, sr_info, vdi_info):
254 try:
255 self.writeMetadataInternal(sr_info, vdi_info)
256 except Exception as e:
257 util.SMlog('Exception writing metadata. Error: %s' % str(e))
258 raise xs_errors.XenError('MetadataError', \
259 opterr='%s' % str(e))
261 # read metadata for this SR and find if a metadata VDI exists
262 def findMetadataVDI(self):
263 try:
264 vdi_info = self.getMetadata()[1]
265 for offset in vdi_info.keys():
266 if vdi_info[offset][TYPE_TAG] == 'metadata' and \
267 vdi_info[offset][IS_A_SNAPSHOT_TAG] == '0':
268 return vdi_info[offset][UUID_TAG]
270 return None
271 except Exception as e:
272 util.SMlog('Exception checking if SR metadata a metadata VDI.' \
273 'Error: %s' % str(e))
274 raise xs_errors.XenError('MetadataError', \
275 opterr='%s' % str(e))
277 # update the SR information or one of the VDIs information
278 # the passed in map would have a key 'objtype', either sr or vdi.
279 # if the key is sr, the following might be passed in
280 # SR name-label
281 # SR name_description
282 # if the key is vdi, the following information per VDI may be passed in
283 # uuid - mandatory
284 # name-label
285 # name_description
286 # is_a_snapshot
287 # snapshot_of, if snapshot status is true
288 # snapshot time
289 # type (system, user or metadata etc)
290 # vdi_type: raw or vhd
291 # read_only
292 # location
293 # managed
294 # metadata_of_pool
295 def updateMetadata(self, update_map={}):
296 util.SMlog("Updating metadata : %s" % update_map)
298 try:
299 objtype = update_map[METADATA_UPDATE_OBJECT_TYPE_TAG]
300 del update_map[METADATA_UPDATE_OBJECT_TYPE_TAG]
302 if objtype == METADATA_OBJECT_TYPE_SR: 302 ↛ 304line 302 didn't jump to line 304, because the condition on line 302 was never false
303 self.updateSR(update_map)
304 elif objtype == METADATA_OBJECT_TYPE_VDI:
305 self.updateVdi(update_map)
306 except Exception as e:
307 util.SMlog('Error updating Metadata Volume with update' \
308 'map: %s. Error: %s' % (update_map, str(e)))
309 raise xs_errors.XenError('MetadataError', \
310 opterr='%s' % str(e))
312 def deleteVdiFromMetadata(self, vdi_uuid):
313 util.SMlog("Deleting vdi: %s" % vdi_uuid)
314 try:
315 self.deleteVdi(vdi_uuid)
316 except Exception as e:
317 util.SMlog('Error deleting vdi %s from the metadata. ' \
318 'Error: %s' % (vdi_uuid, str(e)))
319 raise xs_errors.XenError('MetadataError', \
320 opterr='%s' % str(e))
322 def addVdi(self, vdi_info={}):
323 util.SMlog("Adding VDI with info: %s" % vdi_info)
324 try:
325 self.addVdiInternal(vdi_info)
326 except Exception as e:
327 util.SMlog('Error adding VDI to Metadata Volume with ' \
328 'update map: %s. Error: %s' % (vdi_info, str(e)))
329 raise xs_errors.XenError('MetadataError', \
330 opterr='%s' % (str(e)))
332 def ensureSpaceIsAvailableForVdis(self, count):
333 util.SMlog("Checking if there is space in the metadata for %d VDI." % \
334 count)
335 try:
336 self.spaceAvailableForVdis(count)
337 except Exception as e:
338 raise xs_errors.XenError('MetadataError', \
339 opterr='%s' % str(e))
341 # common functions
342 def deleteVdi(self, vdi_uuid, offset=0):
343 util.SMlog("Entering deleteVdi")
344 try:
345 md = self.getMetadataInternal({'vdi_uuid': vdi_uuid})
346 if 'offset' not in md: 346 ↛ 347line 346 didn't jump to line 347, because the condition on line 346 was never true
347 util.SMlog("Metadata for VDI %s not present, or already removed, " \
348 "no further deletion action required." % vdi_uuid)
349 return
351 md['vdi_info'][md['offset']][VDI_DELETED_TAG] = '1'
352 self.updateVdi(md['vdi_info'][md['offset']])
354 try:
355 mdlength = getMetadataLength(self.fd)
356 if (mdlength - md['offset']) == \
357 self.VDI_INFO_SIZE_IN_SECTORS * SECTOR_SIZE:
358 updateLengthInHeader(self.fd, (mdlength - \
359 self.VDI_INFO_SIZE_IN_SECTORS * SECTOR_SIZE))
360 except:
361 raise
362 except Exception as e:
363 raise Exception("VDI delete operation failed for " \
364 "parameters: %s, %s. Error: %s" % \
365 (self.path, vdi_uuid, str(e)))
367 # common functions with some details derived from the child class
368 def generateVDIsForRange(self, vdi_info, lower, upper, update_map={}, \
369 offset=0):
370 value = ''
371 if not len(vdi_info.keys()) or offset not in vdi_info: 371 ↛ 372line 371 didn't jump to line 372, because the condition on line 371 was never true
372 return self.getVdiInfo(update_map)
374 for vdi_offset in vdi_info.keys():
375 if vdi_offset < lower:
376 continue
378 if len(value) >= (upper - lower): 378 ↛ 379line 378 didn't jump to line 379, because the condition on line 378 was never true
379 break
381 vdi_map = vdi_info[vdi_offset]
382 if vdi_offset == offset: 382 ↛ 387line 382 didn't jump to line 387, because the condition on line 382 was never false
383 # write passed in VDI info
384 for key in update_map.keys():
385 vdi_map[key] = update_map[key]
387 for i in range(1, self.VDI_INFO_SIZE_IN_SECTORS + 1):
388 if len(value) < (upper - lower): 388 ↛ 387line 388 didn't jump to line 387, because the condition on line 388 was never false
389 value += self.getVdiInfo(vdi_map, i)
391 return value
393 def addVdiInternal(self, Dict):
394 util.SMlog("Entering addVdiInternal")
395 try:
396 value = ''
397 Dict[VDI_DELETED_TAG] = '0'
398 min_block_size = get_min_blk_size_wrapper(self.fd)
399 mdlength = getMetadataLength(self.fd)
400 md = self.getMetadataInternal({'firstDeleted': 1, 'includeDeletedVdis': 1})
401 if 'foundDeleted' not in md:
402 md['offset'] = mdlength
403 (md['lower'], md['upper']) = \
404 getBlockAlignedRange(min_block_size, mdlength, \
405 SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS)
406 # If this has created a new VDI, update metadata length
407 if 'foundDeleted' in md:
408 value = self.getMetadataToWrite(md['sr_info'], md['vdi_info'], \
409 md['lower'], md['upper'], Dict, md['offset'])
410 else:
411 value = self.getMetadataToWrite(md['sr_info'], md['vdi_info'], \
412 md['lower'], md['upper'], Dict, mdlength)
414 file_write_wrapper(self.fd, md['lower'], min_block_size, \
415 value, len(value))
417 if 'foundDeleted' in md:
418 updateLengthInHeader(self.fd, mdlength)
419 else:
420 updateLengthInHeader(self.fd, mdlength + \
421 SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS)
422 return True
423 except Exception as e:
424 util.SMlog("Exception adding vdi with info: %s. Error: %s" % \
425 (Dict, str(e)))
426 raise
428 # Get metadata from the file name passed in
429 # additional params:
430 # includeDeletedVdis - include deleted VDIs in the returned metadata
431 # vdi_uuid - only fetch metadata till a particular VDI
432 # offset - only fetch metadata till a particular offset
433 # firstDeleted - get the first deleted VDI
434 # indexByUuid - index VDIs by uuid
435 # the return value of this function is a dictionary having the following keys
436 # sr_info: dictionary containing sr information
437 # vdi_info: dictionary containing vdi information indexed by offset
438 # offset: when passing in vdi_uuid/firstDeleted below
439 # deleted - true if deleted VDI found to be replaced
440 def getMetadataInternal(self, params={}):
441 try:
442 lower = 0
443 upper = 0
444 retmap = {}
445 sr_info_map = {}
446 ret_vdi_info = {}
447 length = getMetadataLength(self.fd)
448 min_blk_size = get_min_blk_size_wrapper(self.fd)
450 # Read in the metadata fil
451 metadataxml = ''
452 metadataxml = file_read_wrapper(self.fd, 0, length, min_blk_size)
454 # At this point we have the complete metadata in metadataxml
455 offset = SECTOR_SIZE + len(XML_HEADER)
456 sr_info = metadataxml[offset: SECTOR_SIZE * 4]
457 offset = SECTOR_SIZE * 4
458 sr_info = sr_info.replace('\x00', '')
460 parsable_metadata = '%s<%s>%s</%s>' % (XML_HEADER, metadata.XML_TAG,
461 sr_info, metadata.XML_TAG)
462 retmap['sr_info'] = metadata._parseXML(parsable_metadata)
464 # At this point we check if an offset has been passed in
465 if 'offset' in params:
466 upper = getBlockAlignedRange(min_blk_size, params['offset'], \
467 0)[1]
468 else:
469 upper = length
471 # Now look at the VDI objects
472 while offset < upper:
473 vdi_info = metadataxml[offset:
474 offset +
475 (SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS)]
476 vdi_info = vdi_info.replace('\x00', '')
477 parsable_metadata = '%s<%s>%s</%s>' % (XML_HEADER, metadata.XML_TAG,
478 vdi_info, metadata.XML_TAG)
479 vdi_info_map = metadata._parseXML(parsable_metadata)[VDI_TAG]
480 vdi_info_map[OFFSET_TAG] = offset
482 if 'includeDeletedVdis' not in params and \ 482 ↛ 484line 482 didn't jump to line 484, because the condition on line 482 was never true
483 vdi_info_map[VDI_DELETED_TAG] == '1':
484 offset += SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS
485 continue
487 if 'indexByUuid' in params: 487 ↛ 488line 487 didn't jump to line 488, because the condition on line 487 was never true
488 ret_vdi_info[vdi_info_map[UUID_TAG]] = vdi_info_map
489 else:
490 ret_vdi_info[offset] = vdi_info_map
492 if 'vdi_uuid' in params:
493 if vdi_info_map[UUID_TAG] == params['vdi_uuid']:
494 retmap['offset'] = offset
495 (lower, upper) = \
496 getBlockAlignedRange(min_blk_size, offset, \
497 SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS)
499 elif 'firstDeleted' in params: 499 ↛ 500line 499 didn't jump to line 500, because the condition on line 499 was never true
500 if vdi_info_map[VDI_DELETED_TAG] == '1':
501 retmap['foundDeleted'] = 1
502 retmap['offset'] = offset
503 (lower, upper) = \
504 getBlockAlignedRange(min_blk_size, offset, \
505 SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS)
507 offset += SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS
509 retmap['lower'] = lower
510 retmap['upper'] = upper
511 retmap['vdi_info'] = ret_vdi_info
512 return retmap
513 except Exception as e:
514 util.SMlog("Exception getting metadata with params" \
515 "%s. Error: %s" % (params, str(e)))
516 raise
518 # This function expects both sr name_label and sr name_description to be
519 # passed in
520 def updateSR(self, Dict):
521 util.SMlog('entering updateSR')
523 value = ''
525 # Find the offset depending on what we are updating
526 diff = set(Dict.keys()) - set(ATOMIC_UPDATE_PARAMS_AND_OFFSET.keys())
527 if diff == set([]): 527 ↛ 561line 527 didn't jump to line 561, because the condition on line 527 was never false
528 offset = SECTOR_SIZE * 2
529 (lower, upper) = getBlockAlignedRange(get_min_blk_size_wrapper( \
530 self.fd), offset, SECTOR_SIZE * 2)
531 md = self.getMetadataInternal({'offset': \
532 SECTOR_SIZE * (SR_INFO_SIZE_IN_SECTORS - 1)})
534 sr_info = md['sr_info']
535 vdi_info_by_offset = md['vdi_info']
537 # update SR info with Dict
538 for key in Dict.keys():
539 sr_info[key] = Dict[key]
541 # if lower is less than SR header size
542 if lower < SR_INFO_SIZE_IN_SECTORS * SECTOR_SIZE: 542 ↛ 556line 542 didn't jump to line 556, because the condition on line 542 was never false
543 # if upper is less than SR header size
544 if upper <= SR_INFO_SIZE_IN_SECTORS * SECTOR_SIZE: 544 ↛ 548line 544 didn't jump to line 548, because the condition on line 544 was never false
545 for i in range(lower // SECTOR_SIZE, upper // SECTOR_SIZE):
546 value += self.getSRInfoForSectors(sr_info, range(i, i + 1))
547 else:
548 for i in range(lower // SECTOR_SIZE, SR_INFO_SIZE_IN_SECTORS):
549 value += self.getSRInfoForSectors(sr_info, range(i, i + 1))
551 # generate the remaining VDI
552 value += self.generateVDIsForRange(vdi_info_by_offset,
553 SR_INFO_SIZE_IN_SECTORS, upper)
554 else:
555 # generate the remaining VDI
556 value += self.generateVDIsForRange(vdi_info_by_offset, lower, upper)
558 file_write_wrapper(self.fd, lower, \
559 get_min_blk_size_wrapper(self.fd), value, len(value))
560 else:
561 raise Exception("SR Update operation not supported for "
562 "parameters: %s" % diff)
564 def updateVdi(self, Dict):
565 util.SMlog('entering updateVdi')
566 try:
567 value = ''
568 min_block_size = get_min_blk_size_wrapper(self.fd)
569 mdlength = getMetadataLength(self.fd)
570 md = self.getMetadataInternal({'vdi_uuid': Dict[UUID_TAG]})
571 value = self.getMetadataToWrite(md['sr_info'], md['vdi_info'], \
572 md['lower'], md['upper'], Dict, md['offset'])
573 file_write_wrapper(self.fd, md['lower'], min_block_size, value, len(value))
574 return True
575 except Exception as e:
576 util.SMlog("Exception updating vdi with info: %s. Error: %s" % \
577 (Dict, str(e)))
578 raise
580 # This should be called only in the cases where we are initially writing
581 # metadata, the function would expect a dictionary which had all information
582 # about the SRs and all its VDIs
583 def writeMetadataInternal(self, sr_info, vdi_info):
584 try:
585 md = ''
586 md = self.getSRInfoForSectors(sr_info, range(0, SR_INFO_SIZE_IN_SECTORS))
588 # Go over the VDIs passed and for each
589 for key in vdi_info.keys():
590 md += self.getVdiInfo(vdi_info[key])
592 # Now write the metadata on disk.
593 min_block_size = get_min_blk_size_wrapper(self.fd)
594 file_write_wrapper(self.fd, 0, min_block_size, md, len(md))
595 updateLengthInHeader(self.fd, len(md))
597 except Exception as e:
598 util.SMlog("Exception writing metadata with info: %s, %s. " \
599 "Error: %s" % (sr_info, vdi_info, str(e)))
600 raise
602 # generates metadata info to write taking the following parameters:
603 # a range, lower - upper
604 # sr and vdi information
605 # VDI information to update
606 # an optional offset to the VDI to update
607 def getMetadataToWrite(self, sr_info, vdi_info, lower, upper, update_map, \
608 offset):
609 util.SMlog("Entering getMetadataToWrite")
610 try:
611 value = ''
612 vdi_map = {}
614 # if lower is less than SR info
615 if lower < SECTOR_SIZE * SR_INFO_SIZE_IN_SECTORS: 615 ↛ 617line 615 didn't jump to line 617, because the condition on line 615 was never true
616 # generate SR info
617 for i in range(lower // SECTOR_SIZE, SR_INFO_SIZE_IN_SECTORS):
618 value += self.getSRInfoForSectors(sr_info, range(i, i + 1))
620 # generate the rest of the VDIs till upper
621 value += self.generateVDIsForRange(vdi_info, \
622 SECTOR_SIZE * SR_INFO_SIZE_IN_SECTORS, upper, update_map, offset)
623 else:
624 # skip till you get a VDI with lower as the offset, then generate
625 value += self.generateVDIsForRange(vdi_info, lower, upper, \
626 update_map, offset)
627 return value
628 except Exception as e:
629 util.SMlog("Exception generating metadata to write with info: " \
630 "sr_info: %s, vdi_info: %s, lower: %d, upper: %d, " \
631 "update_map: %s, offset: %d. Error: %s" % \
632 (sr_info, vdi_info, lower, upper, update_map, offset, str(e)))
633 raise
635 # specific functions, to be implement by the child classes
636 def getVdiInfo(self, Dict, generateSector=0):
637 return ''
639 def getSRInfoForSectors(self, sr_info, range):
640 return ''
643class LVMMetadataHandler(MetadataHandler):
645 VDI_INFO_SIZE_IN_SECTORS = 2
647 # constructor
648 def __init__(self, path=None, write=True):
649 lvutil.ensurePathExists(path)
650 MetadataHandler.__init__(self, path, write)
652 def spaceAvailableForVdis(self, count):
653 try:
654 created = False
655 try:
656 # The easiest way to do this, is to create a dummy vdi and write it
657 uuid = util.gen_uuid()
658 vdi_info = {UUID_TAG: uuid,
659 NAME_LABEL_TAG: 'dummy vdi for space check',
660 NAME_DESCRIPTION_TAG: 'dummy vdi for space check',
661 IS_A_SNAPSHOT_TAG: 0,
662 SNAPSHOT_OF_TAG: '',
663 SNAPSHOT_TIME_TAG: '',
664 TYPE_TAG: 'user',
665 VDI_TYPE_TAG: 'vhd',
666 READ_ONLY_TAG: 0,
667 MANAGED_TAG: 0,
668 'metadata_of_pool': ''
669 }
671 created = self.addVdiInternal(vdi_info)
672 except IOError as e:
673 raise
674 finally:
675 if created:
676 # Now delete the dummy VDI created above
677 self.deleteVdi(uuid)
678 return
680 # This function generates VDI info based on the passed in information
681 # it also takes in a parameter to determine whether both the sector
682 # or only one sector needs to be generated, and which one
683 # generateSector - can be 1 or 2, defaults to 0 and generates both sectors
684 def getVdiInfo(self, Dict, generateSector=0):
685 util.SMlog("Entering VDI info")
686 try:
687 vdi_info = ''
688 # HP split into 2 functions, 1 for generating the first 2 sectors,
689 # which will be called by all classes
690 # and one specific to this class
691 if generateSector == 1 or generateSector == 0:
692 Dict[NAME_LABEL_TAG] = \
693 xml.sax.saxutils.escape(Dict[NAME_LABEL_TAG])
694 Dict[NAME_DESCRIPTION_TAG] = \
695 xml.sax.saxutils.escape(Dict[NAME_DESCRIPTION_TAG])
696 if len(Dict[NAME_LABEL_TAG]) + len(Dict[NAME_DESCRIPTION_TAG]) > \ 696 ↛ 698line 696 didn't jump to line 698, because the condition on line 696 was never true
697 MAX_VDI_NAME_LABEL_DESC_LENGTH:
698 if len(Dict[NAME_LABEL_TAG]) > MAX_VDI_NAME_LABEL_DESC_LENGTH // 2:
699 length = util.unictrunc(
700 Dict[NAME_LABEL_TAG],
701 MAX_VDI_NAME_LABEL_DESC_LENGTH // 2)
703 util.SMlog('warning: name-label truncated from ' +
704 str(len(Dict[NAME_LABEL_TAG])) + ' to ' +
705 str(length) + ' bytes')
707 Dict[NAME_LABEL_TAG] = Dict[NAME_LABEL_TAG][:length]
709 if (len(Dict[NAME_DESCRIPTION_TAG]) >
710 MAX_VDI_NAME_LABEL_DESC_LENGTH // 2):
711 length = util.unictrunc(
712 Dict[NAME_DESCRIPTION_TAG],
713 MAX_VDI_NAME_LABEL_DESC_LENGTH // 2)
715 util.SMlog('warning: description truncated from ' +
716 str(len(Dict[NAME_DESCRIPTION_TAG])) +
717 ' to ' + str(length) + ' bytes')
719 Dict[NAME_DESCRIPTION_TAG] = \
720 Dict[NAME_DESCRIPTION_TAG][:length]
722 # Fill the open struct and write it
723 vdi_info += getSector(VDI_SECTOR_1 % (Dict[NAME_LABEL_TAG],
724 Dict[NAME_DESCRIPTION_TAG]))
726 if generateSector == 2 or generateSector == 0:
727 sector2 = ''
729 if VDI_DELETED_TAG not in Dict:
730 Dict.update({VDI_DELETED_TAG: '0'})
732 for tag in Dict.keys():
733 if tag == NAME_LABEL_TAG or tag == NAME_DESCRIPTION_TAG:
734 continue
735 sector2 += getXMLTag(tag) % Dict[tag]
737 sector2 += VDI_CLOSING_TAG
738 vdi_info += getSector(sector2)
739 return vdi_info
741 except Exception as e:
742 util.SMlog("Exception generating vdi info: %s. Error: %s" % \
743 (Dict, str(e)))
744 raise
746 def getSRInfoForSectors(self, sr_info, range):
747 srinfo = ''
749 try:
750 # write header, name_labael and description in that function
751 # as its common to all
752 # Fill up the first sector
753 if 0 in range:
754 srinfo = getSector(buildHeader(SECTOR_SIZE))
756 if 1 in range:
757 uuid = getXMLTag(UUID_TAG) % sr_info[UUID_TAG]
758 allocation = getXMLTag(ALLOCATION_TAG) % sr_info[ALLOCATION_TAG]
760 second = SECTOR2_FMT % (XML_HEADER, uuid, allocation)
761 srinfo += getSector(second)
763 if 2 in range:
764 # Fill up the SR name_label
765 srinfo += getSector(getSectorAlignedXML(NAME_LABEL_TAG,
766 xml.sax.saxutils.escape(sr_info[NAME_LABEL_TAG])))
768 if 3 in range:
769 # Fill the name_description
770 srinfo += getSector(getSectorAlignedXML(NAME_DESCRIPTION_TAG,
771 xml.sax.saxutils.escape(sr_info[NAME_DESCRIPTION_TAG])))
773 return srinfo
775 except Exception as e:
776 util.SMlog("Exception getting SR info with parameters: sr_info: %s," \
777 "range: %s. Error: %s" % (sr_info, range, str(e)))
778 raise