Hide keyboard shortcuts

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 

19 

20import util 

21import metadata 

22import os 

23import xs_errors 

24import lvutil 

25import xml.sax.saxutils 

26 

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' 

34 

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 

62 

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 

71 

72 

73# ----------------- # General helper functions - begin # ----------------- 

74def get_min_blk_size_wrapper(fd): 

75 return METADATA_BLK_SIZE 

76 

77 

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 

94 

95 

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 

109 

110 

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() 

120 

121 

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 

130 

131 upper = lower + block_size 

132 

133 while upper < (lower + length): 

134 upper += block_size 

135 

136 return (lower, upper) 

137 

138 

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 

150 

151 

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]) 

160 

161 

162def getSector(str): 

163 sector = SECTOR_STRUCT % str 

164 return sector 

165 

166 

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] 

174 

175 return "<%s>%s</%s>" % (tagName, value, tagName) 

176 

177 

178def getXMLTag(tagName): 

179 return "<%s>%s</%s>" % (tagName, '%s', tagName) 

180 

181 

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:] 

190 

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 

197 

198 

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 

211 

212 

213# ----------------- # General helper functions - end # ----------------- 

214class MetadataHandler: 

215 

216 VDI_INFO_SIZE_IN_SECTORS = None 

217 

218 # constructor 

219 def __init__(self, path=None, write=True): 

220 

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) 

225 

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() 

229 

230 def spaceAvailableForVdis(self, count): 

231 raise NotImplementedError("spaceAvailableForVdis is undefined") 

232 

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 

245 

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)) 

250 

251 return (sr_info, vdi_info) 

252 

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)) 

260 

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] 

269 

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)) 

276 

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) 

297 

298 try: 

299 objtype = update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] 

300 del update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] 

301 

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)) 

311 

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)) 

321 

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))) 

331 

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)) 

340 

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 

350 

351 md['vdi_info'][md['offset']][VDI_DELETED_TAG] = '1' 

352 self.updateVdi(md['vdi_info'][md['offset']]) 

353 

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))) 

366 

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) 

373 

374 for vdi_offset in vdi_info.keys(): 

375 if vdi_offset < lower: 

376 continue 

377 

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 

380 

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] 

386 

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) 

390 

391 return value 

392 

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) 

413 

414 file_write_wrapper(self.fd, md['lower'], min_block_size, \ 

415 value, len(value)) 

416 

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 

427 

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) 

449 

450 # Read in the metadata fil 

451 metadataxml = '' 

452 metadataxml = file_read_wrapper(self.fd, 0, length, min_blk_size) 

453 

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', '') 

459 

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) 

463 

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 

470 

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 

481 

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 

486 

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 

491 

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) 

498 

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) 

506 

507 offset += SECTOR_SIZE * self.VDI_INFO_SIZE_IN_SECTORS 

508 

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 

517 

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') 

522 

523 value = '' 

524 

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)}) 

533 

534 sr_info = md['sr_info'] 

535 vdi_info_by_offset = md['vdi_info'] 

536 

537 # update SR info with Dict 

538 for key in Dict.keys(): 

539 sr_info[key] = Dict[key] 

540 

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)) 

550 

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) 

557 

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) 

563 

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 

579 

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)) 

587 

588 # Go over the VDIs passed and for each 

589 for key in vdi_info.keys(): 

590 md += self.getVdiInfo(vdi_info[key]) 

591 

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)) 

596 

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 

601 

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 = {} 

613 

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)) 

619 

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 

634 

635 # specific functions, to be implement by the child classes 

636 def getVdiInfo(self, Dict, generateSector=0): 

637 return '' 

638 

639 def getSRInfoForSectors(self, sr_info, range): 

640 return '' 

641 

642 

643class LVMMetadataHandler(MetadataHandler): 

644 

645 VDI_INFO_SIZE_IN_SECTORS = 2 

646 

647 # constructor 

648 def __init__(self, path=None, write=True): 

649 lvutil.ensurePathExists(path) 

650 MetadataHandler.__init__(self, path, write) 

651 

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 } 

670 

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 

679 

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) 

702 

703 util.SMlog('warning: name-label truncated from ' + 

704 str(len(Dict[NAME_LABEL_TAG])) + ' to ' + 

705 str(length) + ' bytes') 

706 

707 Dict[NAME_LABEL_TAG] = Dict[NAME_LABEL_TAG][:length] 

708 

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) 

714 

715 util.SMlog('warning: description truncated from ' + 

716 str(len(Dict[NAME_DESCRIPTION_TAG])) + 

717 ' to ' + str(length) + ' bytes') 

718 

719 Dict[NAME_DESCRIPTION_TAG] = \ 

720 Dict[NAME_DESCRIPTION_TAG][:length] 

721 

722 # Fill the open struct and write it 

723 vdi_info += getSector(VDI_SECTOR_1 % (Dict[NAME_LABEL_TAG], 

724 Dict[NAME_DESCRIPTION_TAG])) 

725 

726 if generateSector == 2 or generateSector == 0: 

727 sector2 = '' 

728 

729 if VDI_DELETED_TAG not in Dict: 

730 Dict.update({VDI_DELETED_TAG: '0'}) 

731 

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] 

736 

737 sector2 += VDI_CLOSING_TAG 

738 vdi_info += getSector(sector2) 

739 return vdi_info 

740 

741 except Exception as e: 

742 util.SMlog("Exception generating vdi info: %s. Error: %s" % \ 

743 (Dict, str(e))) 

744 raise 

745 

746 def getSRInfoForSectors(self, sr_info, range): 

747 srinfo = '' 

748 

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)) 

755 

756 if 1 in range: 

757 uuid = getXMLTag(UUID_TAG) % sr_info[UUID_TAG] 

758 allocation = getXMLTag(ALLOCATION_TAG) % sr_info[ALLOCATION_TAG] 

759 

760 second = SECTOR2_FMT % (XML_HEADER, uuid, allocation) 

761 srinfo += getSector(second) 

762 

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]))) 

767 

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]))) 

772 

773 return srinfo 

774 

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