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# Miscellaneous utility functions 

17# 

18 

19import os 

20import re 

21import sys 

22import subprocess 

23import shutil 

24import tempfile 

25import signal 

26import time 

27import datetime 

28import errno 

29import socket 

30import xml.dom.minidom 

31import scsiutil 

32import stat 

33import xs_errors 

34import XenAPI # pylint: disable=import-error 

35import xmlrpc.client 

36import base64 

37import syslog 

38import resource 

39import traceback 

40import glob 

41import copy 

42import tempfile 

43 

44from functools import reduce 

45 

46NO_LOGGING_STAMPFILE = '/etc/xensource/no_sm_log' 

47 

48IORETRY_MAX = 20 # retries 

49IORETRY_PERIOD = 1.0 # seconds 

50 

51LOGGING = not (os.path.exists(NO_LOGGING_STAMPFILE)) 

52_SM_SYSLOG_FACILITY = syslog.LOG_LOCAL2 

53LOG_EMERG = syslog.LOG_EMERG 

54LOG_ALERT = syslog.LOG_ALERT 

55LOG_CRIT = syslog.LOG_CRIT 

56LOG_ERR = syslog.LOG_ERR 

57LOG_WARNING = syslog.LOG_WARNING 

58LOG_NOTICE = syslog.LOG_NOTICE 

59LOG_INFO = syslog.LOG_INFO 

60LOG_DEBUG = syslog.LOG_DEBUG 

61 

62ISCSI_REFDIR = '/var/run/sr-ref' 

63 

64CMD_DD = "/bin/dd" 

65 

66FIST_PAUSE_PERIOD = 30 # seconds 

67 

68 

69class SMException(Exception): 

70 """Base class for all SM exceptions for easier catching & wrapping in 

71 XenError""" 

72 

73 

74class CommandException(SMException): 

75 def error_message(self, code): 

76 if code > 0: 

77 return os.strerror(code) 

78 elif code < 0: 

79 return "Signalled %s" % (abs(code)) 

80 return "Success" 

81 

82 def __init__(self, code, cmd="", reason='exec failed'): 

83 self.code = code 

84 self.cmd = cmd 

85 self.reason = reason 

86 Exception.__init__(self, self.error_message(code)) 

87 

88 

89class SRBusyException(SMException): 

90 """The SR could not be locked""" 

91 pass 

92 

93 

94def logException(tag): 

95 info = sys.exc_info() 

96 if info[0] == SystemExit: 96 ↛ 98line 96 didn't jump to line 98, because the condition on line 96 was never true

97 # this should not be happening when catching "Exception", but it is 

98 sys.exit(0) 

99 tb = reduce(lambda a, b: "%s%s" % (a, b), traceback.format_tb(info[2])) 

100 str = "***** %s: EXCEPTION %s, %s\n%s" % (tag, info[0], info[1], tb) 

101 SMlog(str) 

102 

103 

104def roundup(divisor, value): 

105 """Retruns the rounded up value so it is divisible by divisor.""" 

106 

107 if value == 0: 107 ↛ 108line 107 didn't jump to line 108, because the condition on line 107 was never true

108 value = 1 

109 if value % divisor != 0: 

110 return ((int(value) // divisor) + 1) * divisor 

111 return value 

112 

113 

114def to_plain_string(obj): 

115 if obj is None: 

116 return None 

117 if type(obj) == str: 

118 return obj 

119 return str(obj) 

120 

121 

122def shellquote(arg): 

123 return '"%s"' % arg.replace('"', '\\"') 

124 

125 

126def make_WWN(name): 

127 hex_prefix = name.find("0x") 

128 if (hex_prefix >= 0): 128 ↛ 131line 128 didn't jump to line 131, because the condition on line 128 was never false

129 name = name[name.find("0x") + 2:len(name)] 

130 # inject dashes for each nibble 

131 if (len(name) == 16): # sanity check 131 ↛ 135line 131 didn't jump to line 135, because the condition on line 131 was never false

132 name = name[0:2] + "-" + name[2:4] + "-" + name[4:6] + "-" + \ 

133 name[6:8] + "-" + name[8:10] + "-" + name[10:12] + "-" + \ 

134 name[12:14] + "-" + name[14:16] 

135 return name 

136 

137 

138def _logToSyslog(ident, facility, priority, message): 

139 syslog.openlog(ident, 0, facility) 

140 syslog.syslog(priority, "[%d] %s" % (os.getpid(), message)) 

141 syslog.closelog() 

142 

143 

144def SMlog(message, ident="SM", priority=LOG_INFO): 

145 if LOGGING: 145 ↛ exitline 145 didn't return from function 'SMlog', because the condition on line 145 was never false

146 for message_line in str(message).split('\n'): 

147 _logToSyslog(ident, _SM_SYSLOG_FACILITY, priority, message_line) 

148 

149 

150def _getDateString(): 

151 d = datetime.datetime.now() 

152 t = d.timetuple() 

153 return "%s-%s-%s:%s:%s:%s" % \ 

154 (t[0], t[1], t[2], t[3], t[4], t[5]) 

155 

156 

157def doexec(args, inputtext=None, new_env=None, text=True): 

158 """Execute a subprocess, then return its return code, stdout and stderr""" 

159 env = None 

160 if new_env: 

161 env = dict(os.environ) 

162 env.update(new_env) 

163 proc = subprocess.Popen(args, stdin=subprocess.PIPE, 

164 stdout=subprocess.PIPE, 

165 stderr=subprocess.PIPE, 

166 close_fds=True, env=env, 

167 universal_newlines=text) 

168 

169 if not text and inputtext is not None: 169 ↛ 170line 169 didn't jump to line 170, because the condition on line 169 was never true

170 inputtext = inputtext.encode() 

171 

172 (stdout, stderr) = proc.communicate(inputtext) 

173 

174 rc = proc.returncode 

175 return rc, stdout, stderr 

176 

177 

178def is_string(value): 

179 return isinstance(value, str) 

180 

181 

182# These are partially tested functions that replicate the behaviour of 

183# the original pread,pread2 and pread3 functions. Potentially these can 

184# replace the original ones at some later date. 

185# 

186# cmdlist is a list of either single strings or pairs of strings. For 

187# each pair, the first component is passed to exec while the second is 

188# written to the logs. 

189def pread(cmdlist, close_stdin=False, scramble=None, expect_rc=0, 

190 quiet=False, new_env=None, text=True): 

191 cmdlist_for_exec = [] 

192 cmdlist_for_log = [] 

193 for item in cmdlist: 

194 if is_string(item): 194 ↛ 204line 194 didn't jump to line 204, because the condition on line 194 was never false

195 cmdlist_for_exec.append(item) 

196 if scramble: 196 ↛ 197line 196 didn't jump to line 197, because the condition on line 196 was never true

197 if item.find(scramble) != -1: 

198 cmdlist_for_log.append("<filtered out>") 

199 else: 

200 cmdlist_for_log.append(item) 

201 else: 

202 cmdlist_for_log.append(item) 

203 else: 

204 cmdlist_for_exec.append(item[0]) 

205 cmdlist_for_log.append(item[1]) 

206 

207 if not quiet: 207 ↛ 209line 207 didn't jump to line 209, because the condition on line 207 was never false

208 SMlog(cmdlist_for_log) 

209 (rc, stdout, stderr) = doexec(cmdlist_for_exec, new_env=new_env, text=text) 

210 if rc != expect_rc: 

211 SMlog("FAILED in util.pread: (rc %d) stdout: '%s', stderr: '%s'" % \ 

212 (rc, stdout, stderr)) 

213 if quiet: 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true

214 SMlog("Command was: %s" % cmdlist_for_log) 

215 if '' == stderr: 215 ↛ 216line 215 didn't jump to line 216, because the condition on line 215 was never true

216 stderr = stdout 

217 raise CommandException(rc, str(cmdlist), stderr.strip()) 

218 if not quiet: 218 ↛ 220line 218 didn't jump to line 220, because the condition on line 218 was never false

219 SMlog(" pread SUCCESS") 

220 return stdout 

221 

222 

223# POSIX guaranteed atomic within the same file system. 

224# Supply directory to ensure tempfile is created 

225# in the same directory. 

226def atomicFileWrite(targetFile, directory, text): 

227 

228 file = None 

229 try: 

230 # Create file only current pid can write/read to 

231 # our responsibility to clean it up. 

232 _, tempPath = tempfile.mkstemp(dir=directory) 

233 file = open(tempPath, 'w') 

234 file.write(text) 

235 

236 # Ensure flushed to disk. 

237 file.flush() 

238 os.fsync(file.fileno()) 

239 file.close() 

240 

241 os.rename(tempPath, targetFile) 

242 except OSError: 

243 SMlog("FAILED to atomic write to %s" % (targetFile)) 

244 

245 finally: 

246 if (file is not None) and (not file.closed): 

247 file.close() 

248 

249 if os.path.isfile(tempPath): 

250 os.remove(tempPath) 

251 

252 

253#Read STDOUT from cmdlist and discard STDERR output 

254def pread2(cmdlist, quiet=False, text=True): 

255 return pread(cmdlist, quiet=quiet, text=text) 

256 

257 

258#Read STDOUT from cmdlist, feeding 'text' to STDIN 

259def pread3(cmdlist, text): 

260 SMlog(cmdlist) 

261 (rc, stdout, stderr) = doexec(cmdlist, text) 

262 if rc: 

263 SMlog("FAILED in util.pread3: (errno %d) stdout: '%s', stderr: '%s'" % \ 

264 (rc, stdout, stderr)) 

265 if '' == stderr: 

266 stderr = stdout 

267 raise CommandException(rc, str(cmdlist), stderr.strip()) 

268 SMlog(" pread3 SUCCESS") 

269 return stdout 

270 

271 

272def listdir(path, quiet=False): 

273 cmd = ["ls", path, "-1", "--color=never"] 

274 try: 

275 text = pread2(cmd, quiet=quiet)[:-1] 

276 if len(text) == 0: 

277 return [] 

278 return text.split('\n') 

279 except CommandException as inst: 

280 if inst.code == errno.ENOENT: 

281 raise CommandException(errno.EIO, inst.cmd, inst.reason) 

282 else: 

283 raise CommandException(inst.code, inst.cmd, inst.reason) 

284 

285 

286def gen_uuid(): 

287 cmd = ["uuidgen", "-r"] 

288 return pread(cmd)[:-1] 

289 

290 

291def match_uuid(s): 

292 regex = re.compile("^[0-9a-f]{8}-(([0-9a-f]{4})-){3}[0-9a-f]{12}") 

293 return regex.search(s, 0) 

294 

295 

296def findall_uuid(s): 

297 regex = re.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") 

298 return regex.findall(s, 0) 

299 

300 

301def exactmatch_uuid(s): 

302 regex = re.compile("^[0-9a-f]{8}-(([0-9a-f]{4})-){3}[0-9a-f]{12}$") 

303 return regex.search(s, 0) 

304 

305 

306def start_log_entry(srpath, path, args): 

307 logstring = str(datetime.datetime.now()) 

308 logstring += " log: " 

309 logstring += srpath 

310 logstring += " " + path 

311 for element in args: 

312 logstring += " " + element 

313 try: 

314 file = open(srpath + "/filelog.txt", "a") 

315 file.write(logstring) 

316 file.write("\n") 

317 file.close() 

318 except: 

319 pass 

320 

321 # failed to write log ... 

322 

323def end_log_entry(srpath, path, args): 

324 # for teminating, use "error" or "done" 

325 logstring = str(datetime.datetime.now()) 

326 logstring += " end: " 

327 logstring += srpath 

328 logstring += " " + path 

329 for element in args: 

330 logstring += " " + element 

331 try: 

332 file = open(srpath + "/filelog.txt", "a") 

333 file.write(logstring) 

334 file.write("\n") 

335 file.close() 

336 except: 

337 pass 

338 

339 # failed to write log ... 

340 # for now print 

341 # print "%s" % logstring 

342 

343def ioretry(f, errlist=[errno.EIO], maxretry=IORETRY_MAX, period=IORETRY_PERIOD, **ignored): 

344 retries = 0 

345 while True: 

346 try: 

347 return f() 

348 except OSError as ose: 

349 err = int(ose.errno) 

350 if not err in errlist: 

351 raise CommandException(err, str(f), "OSError") 

352 except CommandException as ce: 

353 if not int(ce.code) in errlist: 

354 raise 

355 

356 retries += 1 

357 if retries >= maxretry: 

358 break 

359 

360 time.sleep(period) 

361 

362 raise CommandException(errno.ETIMEDOUT, str(f), "Timeout") 

363 

364 

365def ioretry_stat(path, maxretry=IORETRY_MAX): 

366 # this ioretry is similar to the previous method, but 

367 # stat does not raise an error -- so check its return 

368 retries = 0 

369 while retries < maxretry: 

370 stat = os.statvfs(path) 

371 if stat.f_blocks != -1: 

372 return stat 

373 time.sleep(1) 

374 retries += 1 

375 raise CommandException(errno.EIO, "os.statvfs") 

376 

377 

378def sr_get_capability(sr_uuid, session=None): 

379 result = [] 

380 local_session = None 

381 if session is None: 381 ↛ 385line 381 didn't jump to line 385, because the condition on line 381 was never false

382 local_session = get_localAPI_session() 

383 session = local_session 

384 

385 try: 

386 sr_ref = session.xenapi.SR.get_by_uuid(sr_uuid) 

387 sm_type = session.xenapi.SR.get_record(sr_ref)['type'] 

388 sm_rec = session.xenapi.SM.get_all_records_where( 

389 "field \"type\" = \"%s\"" % sm_type) 

390 

391 # SM expects at least one entry of any SR type 

392 if len(sm_rec) > 0: 

393 result = list(sm_rec.values())[0]['capabilities'] 

394 

395 return result 

396 finally: 

397 if local_session: 397 ↛ exitline 397 didn't return from function 'sr_get_capability', because the return on line 395 wasn't executed

398 local_session.xenapi.session.logout() 

399 

400def sr_get_driver_info(driver_info): 

401 results = {} 

402 # first add in the vanilla stuff 

403 for key in ['name', 'description', 'vendor', 'copyright', \ 

404 'driver_version', 'required_api_version']: 

405 results[key] = driver_info[key] 

406 # add the capabilities (xmlrpc array) 

407 # enforcing activate/deactivate for blktap2 

408 caps = driver_info['capabilities'] 

409 if "ATOMIC_PAUSE" in caps: 409 ↛ 410line 409 didn't jump to line 410, because the condition on line 409 was never true

410 for cap in ("VDI_ACTIVATE", "VDI_DEACTIVATE"): 

411 if not cap in caps: 

412 caps.append(cap) 

413 elif "VDI_ACTIVATE" in caps or "VDI_DEACTIVATE" in caps: 413 ↛ 414line 413 didn't jump to line 414, because the condition on line 413 was never true

414 SMlog("Warning: vdi_[de]activate present for %s" % driver_info["name"]) 

415 

416 results['capabilities'] = caps 

417 # add in the configuration options 

418 options = [] 

419 for option in driver_info['configuration']: 

420 options.append({'key': option[0], 'description': option[1]}) 

421 results['configuration'] = options 

422 return xmlrpc.client.dumps((results, ), "", True) 

423 

424 

425def return_nil(): 

426 return xmlrpc.client.dumps((None, ), "", True, allow_none=True) 

427 

428 

429def SRtoXML(SRlist): 

430 dom = xml.dom.minidom.Document() 

431 driver = dom.createElement("SRlist") 

432 dom.appendChild(driver) 

433 

434 for key in SRlist.keys(): 

435 dict = SRlist[key] 

436 entry = dom.createElement("SR") 

437 driver.appendChild(entry) 

438 

439 e = dom.createElement("UUID") 

440 entry.appendChild(e) 

441 textnode = dom.createTextNode(key) 

442 e.appendChild(textnode) 

443 

444 if 'size' in dict: 

445 e = dom.createElement("Size") 

446 entry.appendChild(e) 

447 textnode = dom.createTextNode(str(dict['size'])) 

448 e.appendChild(textnode) 

449 

450 if 'storagepool' in dict: 

451 e = dom.createElement("StoragePool") 

452 entry.appendChild(e) 

453 textnode = dom.createTextNode(str(dict['storagepool'])) 

454 e.appendChild(textnode) 

455 

456 if 'aggregate' in dict: 

457 e = dom.createElement("Aggregate") 

458 entry.appendChild(e) 

459 textnode = dom.createTextNode(str(dict['aggregate'])) 

460 e.appendChild(textnode) 

461 

462 return dom.toprettyxml() 

463 

464 

465def pathexists(path): 

466 try: 

467 os.lstat(path) 

468 return True 

469 except OSError as inst: 

470 if inst.errno == errno.EIO: 470 ↛ 471line 470 didn't jump to line 471, because the condition on line 470 was never true

471 time.sleep(1) 

472 try: 

473 listdir(os.path.realpath(os.path.dirname(path))) 

474 os.lstat(path) 

475 return True 

476 except: 

477 pass 

478 raise CommandException(errno.EIO, "os.lstat(%s)" % path, "failed") 

479 return False 

480 

481 

482def force_unlink(path): 

483 try: 

484 os.unlink(path) 

485 except OSError as e: 

486 if e.errno != errno.ENOENT: 486 ↛ 487line 486 didn't jump to line 487, because the condition on line 486 was never true

487 raise 

488 

489 

490def create_secret(session, secret): 

491 ref = session.xenapi.secret.create({'value': secret}) 

492 return session.xenapi.secret.get_uuid(ref) 

493 

494 

495def get_secret(session, uuid): 

496 try: 

497 ref = session.xenapi.secret.get_by_uuid(uuid) 

498 return session.xenapi.secret.get_value(ref) 

499 except: 

500 raise xs_errors.XenError('InvalidSecret', opterr='Unable to look up secret [%s]' % uuid) 

501 

502 

503def get_real_path(path): 

504 "Follow symlinks to the actual file" 

505 absPath = path 

506 directory = '' 

507 while os.path.islink(absPath): 

508 directory = os.path.dirname(absPath) 

509 absPath = os.readlink(absPath) 

510 absPath = os.path.join(directory, absPath) 

511 return absPath 

512 

513 

514def wait_for_path(path, timeout): 

515 for i in range(0, timeout): 515 ↛ 519line 515 didn't jump to line 519, because the loop on line 515 didn't complete

516 if len(glob.glob(path)): 516 ↛ 518line 516 didn't jump to line 518, because the condition on line 516 was never false

517 return True 

518 time.sleep(1) 

519 return False 

520 

521 

522def wait_for_nopath(path, timeout): 

523 for i in range(0, timeout): 

524 if not os.path.exists(path): 

525 return True 

526 time.sleep(1) 

527 return False 

528 

529 

530def wait_for_path_multi(path, timeout): 

531 for i in range(0, timeout): 

532 paths = glob.glob(path) 

533 SMlog("_wait_for_paths_multi: paths = %s" % paths) 

534 if len(paths): 

535 SMlog("_wait_for_paths_multi: return first path: %s" % paths[0]) 

536 return paths[0] 

537 time.sleep(1) 

538 return "" 

539 

540 

541def isdir(path): 

542 try: 

543 st = os.stat(path) 

544 return stat.S_ISDIR(st.st_mode) 

545 except OSError as inst: 

546 if inst.errno == errno.EIO: 546 ↛ 547line 546 didn't jump to line 547, because the condition on line 546 was never true

547 raise CommandException(errno.EIO, "os.stat(%s)" % path, "failed") 

548 return False 

549 

550 

551def get_single_entry(path): 

552 f = open(path, 'r') 

553 line = f.readline() 

554 f.close() 

555 return line.rstrip() 

556 

557 

558def get_fs_size(path): 

559 st = ioretry_stat(path) 

560 return st.f_blocks * st.f_frsize 

561 

562 

563def get_fs_utilisation(path): 

564 st = ioretry_stat(path) 

565 return (st.f_blocks - st.f_bfree) * \ 

566 st.f_frsize 

567 

568 

569def ismount(path): 

570 """Test whether a path is a mount point""" 

571 try: 

572 s1 = os.stat(path) 

573 s2 = os.stat(os.path.join(path, '..')) 

574 except OSError as inst: 

575 raise CommandException(inst.errno, "os.stat") 

576 dev1 = s1.st_dev 

577 dev2 = s2.st_dev 

578 if dev1 != dev2: 

579 return True # path/.. on a different device as path 

580 ino1 = s1.st_ino 

581 ino2 = s2.st_ino 

582 if ino1 == ino2: 

583 return True # path/.. is the same i-node as path 

584 return False 

585 

586 

587def makedirs(name, mode=0o777): 

588 head, tail = os.path.split(name) 

589 if not tail: 589 ↛ 590line 589 didn't jump to line 590, because the condition on line 589 was never true

590 head, tail = os.path.split(head) 

591 if head and tail and not pathexists(head): 

592 makedirs(head, mode) 

593 if tail == os.curdir: 593 ↛ 594line 593 didn't jump to line 594, because the condition on line 593 was never true

594 return 

595 try: 

596 os.mkdir(name, mode) 

597 except OSError as exc: 

598 if exc.errno == errno.EEXIST and os.path.isdir(name): 598 ↛ 599line 598 didn't jump to line 599, because the condition on line 598 was never true

599 if mode: 

600 os.chmod(name, mode) 

601 pass 

602 else: 

603 raise 

604 

605 

606def zeroOut(path, fromByte, bytes): 

607 """write 'bytes' zeros to 'path' starting from fromByte (inclusive)""" 

608 blockSize = 4096 

609 

610 fromBlock = fromByte // blockSize 

611 if fromByte % blockSize: 

612 fromBlock += 1 

613 bytesBefore = fromBlock * blockSize - fromByte 

614 if bytesBefore > bytes: 

615 bytesBefore = bytes 

616 bytes -= bytesBefore 

617 cmd = [CMD_DD, "if=/dev/zero", "of=%s" % path, "bs=1", 

618 "seek=%s" % fromByte, "count=%s" % bytesBefore] 

619 try: 

620 pread2(cmd) 

621 except CommandException: 

622 return False 

623 

624 blocks = bytes // blockSize 

625 bytes -= blocks * blockSize 

626 fromByte = (fromBlock + blocks) * blockSize 

627 if blocks: 

628 cmd = [CMD_DD, "if=/dev/zero", "of=%s" % path, "bs=%s" % blockSize, 

629 "seek=%s" % fromBlock, "count=%s" % blocks] 

630 try: 

631 pread2(cmd) 

632 except CommandException: 

633 return False 

634 

635 if bytes: 

636 cmd = [CMD_DD, "if=/dev/zero", "of=%s" % path, "bs=1", 

637 "seek=%s" % fromByte, "count=%s" % bytes] 

638 try: 

639 pread2(cmd) 

640 except CommandException: 

641 return False 

642 

643 return True 

644 

645 

646def wipefs(blockdev): 

647 "Wipe filesystem signatures from `blockdev`" 

648 pread2(["/usr/sbin/wipefs", "-a", blockdev]) 

649 

650 

651def match_rootdev(s): 

652 regex = re.compile("^PRIMARY_DISK") 

653 return regex.search(s, 0) 

654 

655 

656def getrootdev(): 

657 filename = '/etc/xensource-inventory' 

658 try: 

659 f = open(filename, 'r') 

660 except: 

661 raise xs_errors.XenError('EIO', \ 

662 opterr="Unable to open inventory file [%s]" % filename) 

663 rootdev = '' 

664 for line in filter(match_rootdev, f.readlines()): 

665 rootdev = line.split("'")[1] 

666 if not rootdev: 666 ↛ 667line 666 didn't jump to line 667, because the condition on line 666 was never true

667 raise xs_errors.XenError('NoRootDev') 

668 return rootdev 

669 

670 

671def getrootdevID(): 

672 rootdev = getrootdev() 

673 try: 

674 rootdevID = scsiutil.getSCSIid(rootdev) 

675 except: 

676 SMlog("util.getrootdevID: Unable to verify serial or SCSIid of device: %s" \ 

677 % rootdev) 

678 return '' 

679 

680 if not len(rootdevID): 

681 SMlog("util.getrootdevID: Unable to identify scsi device [%s] via scsiID" \ 

682 % rootdev) 

683 

684 return rootdevID 

685 

686 

687def get_localAPI_session(): 

688 # First acquire a valid session 

689 session = XenAPI.xapi_local() 

690 try: 

691 session.xenapi.login_with_password('root', '', '', 'SM') 

692 except: 

693 raise xs_errors.XenError('APISession') 

694 return session 

695 

696 

697def get_this_host(): 

698 uuid = None 

699 f = open("/etc/xensource-inventory", 'r') 

700 for line in f.readlines(): 

701 if line.startswith("INSTALLATION_UUID"): 

702 uuid = line.split("'")[1] 

703 f.close() 

704 return uuid 

705 

706 

707def get_master_ref(session): 

708 pools = session.xenapi.pool.get_all() 

709 return session.xenapi.pool.get_master(pools[0]) 

710 

711 

712def is_master(session): 

713 return get_this_host_ref(session) == get_master_ref(session) 

714 

715 

716def get_localhost_ref(session): 

717 filename = '/etc/xensource-inventory' 

718 try: 

719 f = open(filename, 'r') 

720 except: 

721 raise xs_errors.XenError('EIO', \ 

722 opterr="Unable to open inventory file [%s]" % filename) 

723 domid = '' 

724 for line in filter(match_domain_id, f.readlines()): 

725 domid = line.split("'")[1] 

726 if not domid: 

727 raise xs_errors.XenError('APILocalhost') 

728 

729 vms = session.xenapi.VM.get_all_records_where('field "uuid" = "%s"' % domid) 

730 for vm in vms: 

731 record = vms[vm] 

732 if record["uuid"] == domid: 

733 hostid = record["resident_on"] 

734 return hostid 

735 raise xs_errors.XenError('APILocalhost') 

736 

737 

738def match_domain_id(s): 

739 regex = re.compile("^CONTROL_DOMAIN_UUID") 

740 return regex.search(s, 0) 

741 

742 

743def get_hosts_attached_on(session, vdi_uuids): 

744 host_refs = {} 

745 for vdi_uuid in vdi_uuids: 

746 try: 

747 vdi_ref = session.xenapi.VDI.get_by_uuid(vdi_uuid) 

748 except XenAPI.Failure: 

749 SMlog("VDI %s not in db, ignoring" % vdi_uuid) 

750 continue 

751 sm_config = session.xenapi.VDI.get_sm_config(vdi_ref) 

752 for key in [x for x in sm_config.keys() if x.startswith('host_')]: 

753 host_refs[key[len('host_'):]] = True 

754 return host_refs.keys() 

755 

756def get_this_host_address(session): 

757 host_uuid = get_this_host() 

758 host_ref = session.xenapi.host.get_by_uuid(host_uuid) 

759 return session.xenapi.host.get_record(host_ref)['address'] 

760 

761def get_host_addresses(session): 

762 addresses = [] 

763 hosts = session.xenapi.host.get_all_records() 

764 for record in hosts.values(): 

765 addresses.append(record['address']) 

766 return addresses 

767 

768def get_this_host_ref(session): 

769 host_uuid = get_this_host() 

770 host_ref = session.xenapi.host.get_by_uuid(host_uuid) 

771 return host_ref 

772 

773 

774def get_slaves_attached_on(session, vdi_uuids): 

775 "assume this host is the SR master" 

776 host_refs = get_hosts_attached_on(session, vdi_uuids) 

777 master_ref = get_this_host_ref(session) 

778 return [x for x in host_refs if x != master_ref] 

779 

780 

781def get_online_hosts(session): 

782 online_hosts = [] 

783 hosts = session.xenapi.host.get_all_records() 

784 for host_ref, host_rec in hosts.items(): 

785 metricsRef = host_rec["metrics"] 

786 metrics = session.xenapi.host_metrics.get_record(metricsRef) 

787 if metrics["live"]: 

788 online_hosts.append(host_ref) 

789 return online_hosts 

790 

791 

792def get_all_slaves(session): 

793 "assume this host is the SR master" 

794 host_refs = get_online_hosts(session) 

795 master_ref = get_this_host_ref(session) 

796 return [x for x in host_refs if x != master_ref] 

797 

798 

799def is_attached_rw(sm_config): 

800 for key, val in sm_config.items(): 

801 if key.startswith("host_") and val == "RW": 

802 return True 

803 return False 

804 

805 

806def attached_as(sm_config): 

807 for key, val in sm_config.items(): 

808 if key.startswith("host_") and (val == "RW" or val == "RO"): 808 ↛ 809line 808 didn't jump to line 809, because the condition on line 808 was never true

809 return val 

810 

811 

812def find_my_pbd_record(session, host_ref, sr_ref): 

813 try: 

814 pbds = session.xenapi.PBD.get_all_records() 

815 for pbd_ref in pbds.keys(): 

816 if pbds[pbd_ref]['host'] == host_ref and pbds[pbd_ref]['SR'] == sr_ref: 

817 return [pbd_ref, pbds[pbd_ref]] 

818 return None 

819 except Exception as e: 

820 SMlog("Caught exception while looking up PBD for host %s SR %s: %s" % (str(host_ref), str(sr_ref), str(e))) 

821 return None 

822 

823 

824def find_my_pbd(session, host_ref, sr_ref): 

825 ret = find_my_pbd_record(session, host_ref, sr_ref) 

826 if ret is not None: 

827 return ret[0] 

828 else: 

829 return None 

830 

831 

832def test_hostPBD_devs(session, sr_uuid, devs): 

833 host = get_localhost_ref(session) 

834 sr = session.xenapi.SR.get_by_uuid(sr_uuid) 

835 try: 

836 pbds = session.xenapi.PBD.get_all_records() 

837 except: 

838 raise xs_errors.XenError('APIPBDQuery') 

839 for dev in devs.split(','): 

840 for pbd in pbds: 

841 record = pbds[pbd] 

842 # it's ok if it's *our* PBD 

843 if record["SR"] == sr: 

844 break 

845 if record["host"] == host: 

846 devconfig = record["device_config"] 

847 if 'device' in devconfig: 

848 for device in devconfig['device'].split(','): 

849 if os.path.realpath(device) == os.path.realpath(dev): 

850 return True 

851 return False 

852 

853 

854def test_hostPBD_lun(session, targetIQN, LUNid): 

855 host = get_localhost_ref(session) 

856 try: 

857 pbds = session.xenapi.PBD.get_all_records() 

858 except: 

859 raise xs_errors.XenError('APIPBDQuery') 

860 for pbd in pbds: 

861 record = pbds[pbd] 

862 if record["host"] == host: 

863 devconfig = record["device_config"] 

864 if 'targetIQN' in devconfig and 'LUNid' in devconfig: 

865 if devconfig['targetIQN'] == targetIQN and \ 

866 devconfig['LUNid'] == LUNid: 

867 return True 

868 return False 

869 

870 

871def test_SCSIid(session, sr_uuid, SCSIid): 

872 if sr_uuid is not None: 

873 sr = session.xenapi.SR.get_by_uuid(sr_uuid) 

874 try: 

875 pbds = session.xenapi.PBD.get_all_records() 

876 except: 

877 raise xs_errors.XenError('APIPBDQuery') 

878 for pbd in pbds: 

879 record = pbds[pbd] 

880 # it's ok if it's *our* PBD 

881 # During FC SR creation, devscan.py passes sr_uuid as None 

882 if sr_uuid is not None: 

883 if record["SR"] == sr: 

884 break 

885 devconfig = record["device_config"] 

886 sm_config = session.xenapi.SR.get_sm_config(record["SR"]) 

887 if 'SCSIid' in devconfig and devconfig['SCSIid'] == SCSIid: 

888 return True 

889 elif 'SCSIid' in sm_config and sm_config['SCSIid'] == SCSIid: 

890 return True 

891 elif 'scsi-' + SCSIid in sm_config: 

892 return True 

893 return False 

894 

895 

896class TimeoutException(SMException): 

897 pass 

898 

899 

900def timeout_call(timeoutseconds, function, *arguments): 

901 def handler(signum, frame): 

902 raise TimeoutException() 

903 signal.signal(signal.SIGALRM, handler) 

904 signal.alarm(timeoutseconds) 

905 try: 

906 return function(*arguments) 

907 finally: 

908 signal.alarm(0) 

909 

910 

911def _incr_iscsiSR_refcount(targetIQN, uuid): 

912 if not os.path.exists(ISCSI_REFDIR): 

913 os.mkdir(ISCSI_REFDIR) 

914 filename = os.path.join(ISCSI_REFDIR, targetIQN) 

915 try: 

916 f = open(filename, 'a+') 

917 except: 

918 raise xs_errors.XenError('LVMRefCount', \ 

919 opterr='file %s' % filename) 

920 

921 f.seek(0) 

922 found = False 

923 refcount = 0 

924 for line in filter(match_uuid, f.readlines()): 

925 refcount += 1 

926 if line.find(uuid) != -1: 

927 found = True 

928 if not found: 

929 f.write("%s\n" % uuid) 

930 refcount += 1 

931 f.close() 

932 return refcount 

933 

934 

935def _decr_iscsiSR_refcount(targetIQN, uuid): 

936 filename = os.path.join(ISCSI_REFDIR, targetIQN) 

937 if not os.path.exists(filename): 

938 return 0 

939 try: 

940 f = open(filename, 'a+') 

941 except: 

942 raise xs_errors.XenError('LVMRefCount', \ 

943 opterr='file %s' % filename) 

944 

945 f.seek(0) 

946 output = [] 

947 refcount = 0 

948 for line in filter(match_uuid, f.readlines()): 

949 if line.find(uuid) == -1: 

950 output.append(line.rstrip()) 

951 refcount += 1 

952 if not refcount: 

953 os.unlink(filename) 

954 return refcount 

955 

956 # Re-open file and truncate 

957 f.close() 

958 f = open(filename, 'w') 

959 for i in range(0, refcount): 

960 f.write("%s\n" % output[i]) 

961 f.close() 

962 return refcount 

963 

964 

965# The agent enforces 1 PBD per SR per host, so we 

966# check for active SR entries not attached to this host 

967def test_activePoolPBDs(session, host, uuid): 

968 try: 

969 pbds = session.xenapi.PBD.get_all_records() 

970 except: 

971 raise xs_errors.XenError('APIPBDQuery') 

972 for pbd in pbds: 

973 record = pbds[pbd] 

974 if record["host"] != host and record["SR"] == uuid \ 

975 and record["currently_attached"]: 

976 return True 

977 return False 

978 

979 

980def remove_mpathcount_field(session, host_ref, sr_ref, SCSIid): 

981 try: 

982 pbdref = find_my_pbd(session, host_ref, sr_ref) 

983 if pbdref is not None: 

984 key = "mpath-" + SCSIid 

985 session.xenapi.PBD.remove_from_other_config(pbdref, key) 

986 except: 

987 pass 

988 

989 

990 

991def _testHost(hostname, port, errstring): 

992 SMlog("_testHost: Testing host/port: %s,%d" % (hostname, port)) 

993 try: 

994 sockinfo = socket.getaddrinfo(hostname, int(port))[0] 

995 except: 

996 logException('Exception occured getting IP for %s' % hostname) 

997 raise xs_errors.XenError('DNSError') 

998 

999 timeout = 5 

1000 

1001 sock = socket.socket(sockinfo[0], socket.SOCK_STREAM) 

1002 # Only allow the connect to block for up to timeout seconds 

1003 sock.settimeout(timeout) 

1004 try: 

1005 sock.connect(sockinfo[4]) 

1006 # Fix for MS storage server bug 

1007 sock.send(b'\n') 

1008 sock.close() 

1009 except socket.error as reason: 

1010 SMlog("_testHost: Connect failed after %d seconds (%s) - %s" \ 

1011 % (timeout, hostname, reason)) 

1012 raise xs_errors.XenError(errstring) 

1013 

1014 

1015def match_scsiID(s, id): 

1016 regex = re.compile(id) 

1017 return regex.search(s, 0) 

1018 

1019 

1020def _isSCSIid(s): 

1021 regex = re.compile("^scsi-") 

1022 return regex.search(s, 0) 

1023 

1024 

1025def test_scsiserial(session, device): 

1026 device = os.path.realpath(device) 

1027 if not scsiutil._isSCSIdev(device): 

1028 SMlog("util.test_scsiserial: Not a serial device: %s" % device) 

1029 return False 

1030 serial = "" 

1031 try: 

1032 serial += scsiutil.getserial(device) 

1033 except: 

1034 # Error allowed, SCSIid is the important one 

1035 pass 

1036 

1037 try: 

1038 scsiID = scsiutil.getSCSIid(device) 

1039 except: 

1040 SMlog("util.test_scsiserial: Unable to verify serial or SCSIid of device: %s" \ 

1041 % device) 

1042 return False 

1043 if not len(scsiID): 

1044 SMlog("util.test_scsiserial: Unable to identify scsi device [%s] via scsiID" \ 

1045 % device) 

1046 return False 

1047 

1048 try: 

1049 SRs = session.xenapi.SR.get_all_records() 

1050 except: 

1051 raise xs_errors.XenError('APIFailure') 

1052 for SR in SRs: 

1053 record = SRs[SR] 

1054 conf = record["sm_config"] 

1055 if 'devserial' in conf: 

1056 for dev in conf['devserial'].split(','): 

1057 if _isSCSIid(dev): 

1058 if match_scsiID(dev, scsiID): 

1059 return True 

1060 elif len(serial) and dev == serial: 

1061 return True 

1062 return False 

1063 

1064 

1065def default(self, field, thunk): 

1066 try: 

1067 return getattr(self, field) 

1068 except: 

1069 return thunk() 

1070 

1071 

1072def list_VDI_records_in_sr(sr): 

1073 """Helper function which returns a list of all VDI records for this SR 

1074 stored in the XenAPI server, useful for implementing SR.scan""" 

1075 sr_ref = sr.session.xenapi.SR.get_by_uuid(sr.uuid) 

1076 vdis = sr.session.xenapi.VDI.get_all_records_where("field \"SR\" = \"%s\"" % sr_ref) 

1077 return vdis 

1078 

1079 

1080# Given a partition (e.g. sda1), get a disk name: 

1081def diskFromPartition(partition): 

1082 # check whether this is a device mapper device (e.g. /dev/dm-0) 

1083 m = re.match('(/dev/)?(dm-[0-9]+)(p[0-9]+)?$', partition) 

1084 if m is not None: 1084 ↛ 1085line 1084 didn't jump to line 1085, because the condition on line 1084 was never true

1085 return m.group(2) 

1086 

1087 numlen = 0 # number of digit characters 

1088 m = re.match(r"\D+(\d+)", partition) 

1089 if m is not None: 1089 ↛ 1090line 1089 didn't jump to line 1090, because the condition on line 1089 was never true

1090 numlen = len(m.group(1)) 

1091 

1092 # is it a cciss? 

1093 if True in [partition.startswith(x) for x in ['cciss', 'ida', 'rd']]: 1093 ↛ 1094line 1093 didn't jump to line 1094, because the condition on line 1093 was never true

1094 numlen += 1 # need to get rid of trailing 'p' 

1095 

1096 # is it a mapper path? 

1097 if partition.startswith("mapper"): 1097 ↛ 1098line 1097 didn't jump to line 1098, because the condition on line 1097 was never true

1098 if re.search("p[0-9]*$", partition): 

1099 numlen = len(re.match(r"\d+", partition[::-1]).group(0)) + 1 

1100 SMlog("Found mapper part, len %d" % numlen) 

1101 else: 

1102 numlen = 0 

1103 

1104 # is it /dev/disk/by-id/XYZ-part<k>? 

1105 if partition.startswith("disk/by-id"): 1105 ↛ 1106line 1105 didn't jump to line 1106, because the condition on line 1105 was never true

1106 return partition[:partition.rfind("-part")] 

1107 

1108 return partition[:len(partition) - numlen] 

1109 

1110 

1111def dom0_disks(): 

1112 """Disks carrying dom0, e.g. ['/dev/sda']""" 

1113 disks = [] 

1114 with open("/etc/mtab", 'r') as f: 

1115 for line in f: 

1116 (dev, mountpoint, fstype, opts, freq, passno) = line.split(' ') 

1117 if mountpoint == '/': 

1118 disk = diskFromPartition(dev) 

1119 if not (disk in disks): 

1120 disks.append(disk) 

1121 SMlog("Dom0 disks: %s" % disks) 

1122 return disks 

1123 

1124 

1125def set_scheduler_sysfs_node(node, scheds): 

1126 """ 

1127 Set the scheduler for a sysfs node (e.g. '/sys/block/sda') 

1128 according to prioritized list schedulers 

1129 Try to set the first item, then fall back to the next on failure 

1130 """ 

1131 

1132 path = os.path.join(node, "queue", "scheduler") 

1133 if not os.path.exists(path): 1133 ↛ 1137line 1133 didn't jump to line 1137, because the condition on line 1133 was never false

1134 SMlog("no path %s" % path) 

1135 return 

1136 

1137 stored_error = None 

1138 for sched in scheds: 

1139 try: 

1140 with open(path, 'w') as file: 

1141 file.write("%s\n" % sched) 

1142 SMlog("Set scheduler to [%s] on [%s]" % (sched, node)) 

1143 return 

1144 except (OSError, IOError) as err: 

1145 stored_error = err 

1146 

1147 SMlog("Error setting schedulers to [%s] on [%s], %s" % (scheds, node, str(stored_error))) 

1148 

1149 

1150def set_scheduler(dev, schedulers=None): 

1151 if schedulers is None: 1151 ↛ 1154line 1151 didn't jump to line 1154, because the condition on line 1151 was never false

1152 schedulers = ["none", "noop"] 

1153 

1154 devices = [] 

1155 if not scsiutil.match_dm(dev): 1155 ↛ 1159line 1155 didn't jump to line 1159, because the condition on line 1155 was never false

1156 # Remove partition numbers 

1157 devices.append(diskFromPartition(dev).replace('/', '!')) 

1158 else: 

1159 rawdev = diskFromPartition(dev) 

1160 devices = [os.path.realpath(x)[5:] for x in scsiutil._genReverseSCSIidmap(rawdev.split('/')[-1])] 

1161 

1162 for d in devices: 

1163 set_scheduler_sysfs_node("/sys/block/%s" % d, schedulers) 

1164 

1165 

1166# This function queries XAPI for the existing VDI records for this SR 

1167def _getVDIs(srobj): 

1168 VDIs = [] 

1169 try: 

1170 sr_ref = getattr(srobj, 'sr_ref') 

1171 except AttributeError: 

1172 return VDIs 

1173 

1174 refs = srobj.session.xenapi.SR.get_VDIs(sr_ref) 

1175 for vdi in refs: 

1176 ref = srobj.session.xenapi.VDI.get_record(vdi) 

1177 ref['vdi_ref'] = vdi 

1178 VDIs.append(ref) 

1179 return VDIs 

1180 

1181 

1182def _getVDI(srobj, vdi_uuid): 

1183 vdi = srobj.session.xenapi.VDI.get_by_uuid(vdi_uuid) 

1184 ref = srobj.session.xenapi.VDI.get_record(vdi) 

1185 ref['vdi_ref'] = vdi 

1186 return ref 

1187 

1188 

1189def _convertDNS(name): 

1190 addr = socket.getaddrinfo(name, None)[0][4][0] 

1191 return addr 

1192 

1193 

1194def _containsVDIinuse(srobj): 

1195 VDIs = _getVDIs(srobj) 

1196 for vdi in VDIs: 

1197 if not vdi['managed']: 

1198 continue 

1199 sm_config = vdi['sm_config'] 

1200 if 'SRRef' in sm_config: 

1201 try: 

1202 PBDs = srobj.session.xenapi.SR.get_PBDs(sm_config['SRRef']) 

1203 for pbd in PBDs: 

1204 record = PBDs[pbd] 

1205 if record["host"] == srobj.host_ref and \ 

1206 record["currently_attached"]: 

1207 return True 

1208 except: 

1209 pass 

1210 return False 

1211 

1212 

1213def isVDICommand(cmd): 

1214 if cmd is None or cmd in ["vdi_attach", "vdi_detach", 

1215 "vdi_activate", "vdi_deactivate", 

1216 "vdi_epoch_begin", "vdi_epoch_end"]: 

1217 return True 

1218 else: 

1219 return False 

1220 

1221 

1222######################### 

1223# Daemon helper functions 

1224def p_id_fork(): 

1225 try: 

1226 p_id = os.fork() 

1227 except OSError as e: 

1228 print("Fork failed: %s (%d)" % (e.strerror, e.errno)) 

1229 sys.exit(-1) 

1230 

1231 if (p_id == 0): 

1232 os.setsid() 

1233 try: 

1234 p_id = os.fork() 

1235 except OSError as e: 

1236 print("Fork failed: %s (%d)" % (e.strerror, e.errno)) 

1237 sys.exit(-1) 

1238 if (p_id == 0): 

1239 os.chdir('/opt/xensource/sm') 

1240 os.umask(0) 

1241 else: 

1242 os._exit(0) 

1243 else: 

1244 os._exit(0) 

1245 

1246 

1247def daemon(): 

1248 p_id_fork() 

1249 # Query the max file descriptor parameter for this process 

1250 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] 

1251 

1252 # Close any fds that are open 

1253 for fd in range(0, maxfd): 

1254 try: 

1255 os.close(fd) 

1256 except: 

1257 pass 

1258 

1259 # Redirect STDIN to STDOUT and STDERR 

1260 os.open('/dev/null', os.O_RDWR) 

1261 os.dup2(0, 1) 

1262 os.dup2(0, 2) 

1263 

1264################################################################################ 

1265# 

1266# Fist points 

1267# 

1268 

1269# * The global variable 'fistpoint' define the list of all possible fistpoints; 

1270# 

1271# * To activate a fistpoint called 'name', you need to create the file '/tmp/fist_name' 

1272# on the SR master; 

1273# 

1274# * At the moment, activating a fist point can lead to two possible behaviors: 

1275# - if '/tmp/fist_LVHDRT_exit' exists, then the function called during the fistpoint is _exit; 

1276# - otherwise, the function called is _pause. 

1277 

1278def _pause(secs, name): 

1279 SMlog("Executing fist point %s: sleeping %d seconds ..." % (name, secs)) 

1280 time.sleep(secs) 

1281 SMlog("Executing fist point %s: done" % name) 

1282 

1283 

1284def _exit(name): 

1285 SMlog("Executing fist point %s: exiting the current process ..." % name) 

1286 raise xs_errors.XenError('FistPoint', opterr='%s' % name) 

1287 

1288 

1289class FistPoint: 

1290 def __init__(self, points): 

1291 #SMlog("Fist points loaded") 

1292 self.points = points 

1293 

1294 def is_legal(self, name): 

1295 return (name in self.points) 

1296 

1297 def is_active(self, name): 

1298 return os.path.exists("/tmp/fist_%s" % name) 

1299 

1300 def mark_sr(self, name, sruuid, started): 

1301 session = get_localAPI_session() 

1302 try: 

1303 sr = session.xenapi.SR.get_by_uuid(sruuid) 

1304 

1305 if started: 

1306 session.xenapi.SR.add_to_other_config(sr, name, "active") 

1307 else: 

1308 session.xenapi.SR.remove_from_other_config(sr, name) 

1309 finally: 

1310 session.xenapi.session.logout() 

1311 

1312 def activate(self, name, sruuid): 

1313 if name in self.points: 

1314 if self.is_active(name): 

1315 self.mark_sr(name, sruuid, True) 

1316 if self.is_active("LVHDRT_exit"): 1316 ↛ 1317line 1316 didn't jump to line 1317, because the condition on line 1316 was never true

1317 self.mark_sr(name, sruuid, False) 

1318 _exit(name) 

1319 else: 

1320 _pause(FIST_PAUSE_PERIOD, name) 

1321 self.mark_sr(name, sruuid, False) 

1322 else: 

1323 SMlog("Unknown fist point: %s" % name) 

1324 

1325 def activate_custom_fn(self, name, fn): 

1326 if name in self.points: 1326 ↛ 1332line 1326 didn't jump to line 1332, because the condition on line 1326 was never false

1327 if self.is_active(name): 1327 ↛ 1328line 1327 didn't jump to line 1328, because the condition on line 1327 was never true

1328 SMlog("Executing fist point %s: starting ..." % name) 

1329 fn() 

1330 SMlog("Executing fist point %s: done" % name) 

1331 else: 

1332 SMlog("Unknown fist point: %s" % name) 

1333 

1334 

1335def list_find(f, seq): 

1336 for item in seq: 

1337 if f(item): 

1338 return item 

1339 

1340GCPAUSE_FISTPOINT = "GCLoop_no_pause" 

1341 

1342fistpoint = FistPoint(["LVHDRT_finding_a_suitable_pair", 

1343 "LVHDRT_inflating_the_parent", 

1344 "LVHDRT_resizing_while_vdis_are_paused", 

1345 "LVHDRT_coalescing_VHD_data", 

1346 "LVHDRT_coalescing_before_inflate_grandparent", 

1347 "LVHDRT_relinking_grandchildren", 

1348 "LVHDRT_before_create_relink_journal", 

1349 "LVHDRT_xapiSM_serialization_tests", 

1350 "LVHDRT_clone_vdi_after_create_journal", 

1351 "LVHDRT_clone_vdi_after_shrink_parent", 

1352 "LVHDRT_clone_vdi_after_first_snap", 

1353 "LVHDRT_clone_vdi_after_second_snap", 

1354 "LVHDRT_clone_vdi_after_parent_hidden", 

1355 "LVHDRT_clone_vdi_after_parent_ro", 

1356 "LVHDRT_clone_vdi_before_remove_journal", 

1357 "LVHDRT_clone_vdi_after_lvcreate", 

1358 "LVHDRT_clone_vdi_before_undo_clone", 

1359 "LVHDRT_clone_vdi_after_undo_clone", 

1360 "LVHDRT_inflate_after_create_journal", 

1361 "LVHDRT_inflate_after_setSize", 

1362 "LVHDRT_inflate_after_zeroOut", 

1363 "LVHDRT_inflate_after_setSizePhys", 

1364 "LVHDRT_inflate_after_setSizePhys", 

1365 "LVHDRT_coaleaf_before_coalesce", 

1366 "LVHDRT_coaleaf_after_coalesce", 

1367 "LVHDRT_coaleaf_one_renamed", 

1368 "LVHDRT_coaleaf_both_renamed", 

1369 "LVHDRT_coaleaf_after_vdirec", 

1370 "LVHDRT_coaleaf_before_delete", 

1371 "LVHDRT_coaleaf_after_delete", 

1372 "LVHDRT_coaleaf_before_remove_j", 

1373 "LVHDRT_coaleaf_undo_after_rename", 

1374 "LVHDRT_coaleaf_undo_after_rename2", 

1375 "LVHDRT_coaleaf_undo_after_refcount", 

1376 "LVHDRT_coaleaf_undo_after_deflate", 

1377 "LVHDRT_coaleaf_undo_end", 

1378 "LVHDRT_coaleaf_stop_after_recovery", 

1379 "LVHDRT_coaleaf_finish_after_inflate", 

1380 "LVHDRT_coaleaf_finish_end", 

1381 "LVHDRT_coaleaf_delay_1", 

1382 "LVHDRT_coaleaf_delay_2", 

1383 "LVHDRT_coaleaf_delay_3", 

1384 "testsm_clone_allow_raw", 

1385 "xenrt_default_vdi_type_legacy", 

1386 "blktap_activate_inject_failure", 

1387 "blktap_activate_error_handling", 

1388 GCPAUSE_FISTPOINT, 

1389 "cleanup_coalesceVHD_inject_failure", 

1390 "cleanup_tracker_no_progress", 

1391 "FileSR_fail_hardlink", 

1392 "FileSR_fail_snap1", 

1393 "FileSR_fail_snap2", 

1394 "LVM_journaler_exists", 

1395 "LVM_journaler_none", 

1396 "LVM_journaler_badname", 

1397 "LVM_journaler_readfail", 

1398 "LVM_journaler_writefail"]) 

1399 

1400 

1401def set_dirty(session, sr): 

1402 try: 

1403 session.xenapi.SR.add_to_other_config(sr, "dirty", "") 

1404 SMlog("set_dirty %s succeeded" % (repr(sr))) 

1405 except: 

1406 SMlog("set_dirty %s failed (flag already set?)" % (repr(sr))) 

1407 

1408 

1409def doesFileHaveOpenHandles(fileName): 

1410 SMlog("Entering doesFileHaveOpenHandles with file: %s" % fileName) 

1411 (retVal, processAndPidTuples) = \ 

1412 findRunningProcessOrOpenFile(fileName, False) 

1413 

1414 if not retVal: 

1415 SMlog("Failed to determine if file %s has open handles." % \ 

1416 fileName) 

1417 # err on the side of caution 

1418 return True 

1419 else: 

1420 if len(processAndPidTuples) > 0: 

1421 return True 

1422 else: 

1423 return False 

1424 

1425 

1426# extract SR uuid from the passed in devmapper entry and return 

1427# /dev/mapper/VG_XenStorage--c3d82e92--cb25--c99b--b83a--482eebab4a93-MGT 

1428def extractSRFromDevMapper(path): 

1429 try: 

1430 path = os.path.basename(path) 

1431 path = path[len('VG_XenStorage-') + 1:] 

1432 path = path.replace('--', '/') 

1433 path = path[0:path.rfind('-')] 

1434 return path.replace('/', '-') 

1435 except: 

1436 return '' 

1437 

1438 

1439def pid_is_alive(pid): 

1440 """ 

1441 Try to kill PID with signal 0. 

1442 If we succeed, the PID is alive, so return True. 

1443 If we get an EPERM error, the PID is alive but we are not allowed to 

1444 signal it. Still return true. 

1445 Any other error (e.g. ESRCH), return False 

1446 """ 

1447 try: 

1448 os.kill(pid, 0) 

1449 return True 

1450 except OSError as e: 

1451 if e.errno == errno.EPERM: 

1452 return True 

1453 return False 

1454 

1455 

1456# Looks at /proc and figures either 

1457# If a process is still running (default), returns open file names 

1458# If any running process has open handles to the given file (process = False) 

1459# returns process names and pids 

1460def findRunningProcessOrOpenFile(name, process=True): 

1461 retVal = True 

1462 links = [] 

1463 processandpids = [] 

1464 sockets = set() 

1465 try: 

1466 SMlog("Entering findRunningProcessOrOpenFile with params: %s" % \ 

1467 [name, process]) 

1468 

1469 # Look at all pids 

1470 pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] 

1471 for pid in sorted(pids): 

1472 try: 

1473 try: 

1474 f = None 

1475 f = open(os.path.join('/proc', pid, 'cmdline'), 'r') 

1476 prog = f.read()[:-1] 

1477 if prog: 1477 ↛ 1486line 1477 didn't jump to line 1486, because the condition on line 1477 was never false

1478 # Just want the process name 

1479 argv = prog.split('\x00') 

1480 prog = argv[0] 

1481 except IOError as e: 

1482 if e.errno in (errno.ENOENT, errno.ESRCH): 

1483 SMlog("ERROR %s reading %s, ignore" % (e.errno, pid)) 

1484 continue 

1485 finally: 

1486 if f is not None: 1486 ↛ 1471,   1486 ↛ 14892 missed branches: 1) line 1486 didn't jump to line 1471, because the continue on line 1484 wasn't executed, 2) line 1486 didn't jump to line 1489, because the condition on line 1486 was never false

1487 f.close() 1487 ↛ 1471line 1487 didn't jump to line 1471, because the continue on line 1484 wasn't executed

1488 

1489 try: 

1490 fd_dir = os.path.join('/proc', pid, 'fd') 

1491 files = os.listdir(fd_dir) 

1492 except OSError as e: 

1493 if e.errno in (errno.ENOENT, errno.ESRCH): 

1494 SMlog("ERROR %s reading fds for %s, ignore" % (e.errno, pid)) 

1495 # Ignore pid that are no longer valid 

1496 continue 

1497 else: 

1498 raise 

1499 

1500 for file in files: 

1501 try: 

1502 link = os.readlink(os.path.join(fd_dir, file)) 

1503 except OSError: 

1504 continue 

1505 

1506 if process: 1506 ↛ 1511line 1506 didn't jump to line 1511, because the condition on line 1506 was never false

1507 if name == prog: 1507 ↛ 1500line 1507 didn't jump to line 1500, because the condition on line 1507 was never false

1508 links.append(link) 

1509 else: 

1510 # need to return process name and pid tuples 

1511 if link == name: 

1512 processandpids.append((prog, pid)) 

1513 

1514 # Get the connected sockets 

1515 if name == prog: 

1516 sockets.update(get_connected_sockets(pid)) 

1517 

1518 # We will only have a non-empty processandpids if some fd entries were found. 

1519 # Before returning them, verify that all the PIDs in question are properly alive. 

1520 # There is no specific guarantee of when a PID's /proc directory will disappear 

1521 # when it exits, particularly relative to filedescriptor cleanup, so we want to 

1522 # make sure we're not reporting a false positive. 

1523 processandpids = [x for x in processandpids if pid_is_alive(int(x[1]))] 

1524 for pp in processandpids: 1524 ↛ 1525line 1524 didn't jump to line 1525, because the loop on line 1524 never started

1525 SMlog(f"File {name} has an open handle with process {pp[0]} with pid {pp[1]}") 

1526 

1527 except Exception as e: 

1528 SMlog("Exception checking running process or open file handles. " \ 

1529 "Error: %s" % str(e)) 

1530 retVal = False 

1531 

1532 if process: 1532 ↛ 1535line 1532 didn't jump to line 1535, because the condition on line 1532 was never false

1533 return retVal, links, sockets 

1534 else: 

1535 return retVal, processandpids 

1536 

1537 

1538def get_connected_sockets(pid): 

1539 sockets = set() 

1540 try: 

1541 # Lines in /proc/<pid>/net/unix are formatted as follows 

1542 # (see Linux source net/unix/af_unix.c, unix_seq_show() ) 

1543 # - Pointer address to socket (hex) 

1544 # - Refcount (HEX) 

1545 # - 0 

1546 # - State (HEX, 0 or __SO_ACCEPTCON) 

1547 # - Type (HEX - but only 0001 of interest) 

1548 # - Connection state (HEX - but only 03, SS_CONNECTED of interest) 

1549 # - Inode number 

1550 # - Path (optional) 

1551 open_sock_matcher = re.compile( 

1552 r'^[0-9a-f]+: [0-9A-Fa-f]+ [0-9A-Fa-f]+ [0-9A-Fa-f]+ 0001 03 \d+ (.*)$') 

1553 with open( 

1554 os.path.join('/proc', str(pid), 'net', 'unix'), 'r') as f: 

1555 lines = f.readlines() 

1556 for line in lines: 

1557 match = open_sock_matcher.match(line) 

1558 if match: 

1559 sockets.add(match[1]) 

1560 except OSError as e: 

1561 if e.errno in (errno.ENOENT, errno.ESRCH): 

1562 # Ignore pid that are no longer valid 

1563 SMlog("ERROR %s reading sockets for %s, ignore" % 

1564 (e.errno, pid)) 

1565 else: 

1566 raise 

1567 return sockets 

1568 

1569 

1570def retry(f, maxretry=20, period=3, exceptions=[Exception]): 

1571 retries = 0 

1572 while True: 

1573 try: 

1574 return f() 

1575 except Exception as e: 

1576 for exception in exceptions: 

1577 if isinstance(e, exception): 

1578 SMlog('Got exception: {}. Retry number: {}'.format( 

1579 str(e), retries 

1580 )) 

1581 break 

1582 else: 

1583 SMlog('Got bad exception: {}. Raising...'.format(e)) 

1584 raise e 

1585 

1586 retries += 1 

1587 if retries >= maxretry: 

1588 break 

1589 

1590 time.sleep(period) 

1591 

1592 return f() 

1593 

1594 

1595def getCslDevPath(svid): 

1596 basepath = "/dev/disk/by-csldev/" 

1597 if svid.startswith("NETAPP_"): 

1598 # special attention for NETAPP SVIDs 

1599 svid_parts = svid.split("__") 

1600 globstr = basepath + "NETAPP__LUN__" + "*" + svid_parts[2] + "*" + svid_parts[-1] + "*" 

1601 else: 

1602 globstr = basepath + svid + "*" 

1603 

1604 return globstr 

1605 

1606 

1607# Use device in /dev pointed to by cslg path which consists of svid 

1608def get_scsiid_from_svid(md_svid): 

1609 cslg_path = getCslDevPath(md_svid) 

1610 abs_path = glob.glob(cslg_path) 

1611 if abs_path: 

1612 real_path = os.path.realpath(abs_path[0]) 

1613 return scsiutil.getSCSIid(real_path) 

1614 else: 

1615 return None 

1616 

1617 

1618def get_isl_scsiids(session): 

1619 # Get cslg type SRs 

1620 SRs = session.xenapi.SR.get_all_records_where('field "type" = "cslg"') 

1621 

1622 # Iterate through the SR to get the scsi ids 

1623 scsi_id_ret = [] 

1624 for SR in SRs: 

1625 sr_rec = SRs[SR] 

1626 # Use the md_svid to get the scsi id 

1627 scsi_id = get_scsiid_from_svid(sr_rec['sm_config']['md_svid']) 

1628 if scsi_id: 

1629 scsi_id_ret.append(scsi_id) 

1630 

1631 # Get the vdis in the SR and do the same procedure 

1632 vdi_recs = session.xenapi.VDI.get_all_records_where('field "SR" = "%s"' % SR) 

1633 for vdi_rec in vdi_recs: 

1634 vdi_rec = vdi_recs[vdi_rec] 

1635 scsi_id = get_scsiid_from_svid(vdi_rec['sm_config']['SVID']) 

1636 if scsi_id: 

1637 scsi_id_ret.append(scsi_id) 

1638 

1639 return scsi_id_ret 

1640 

1641 

1642class extractXVA: 

1643 # streams files as a set of file and checksum, caller should remove 

1644 # the files, if not needed. The entire directory (Where the files 

1645 # and checksum) will only be deleted as part of class cleanup. 

1646 HDR_SIZE = 512 

1647 BLOCK_SIZE = 512 

1648 SIZE_LEN = 12 - 1 # To remove \0 from tail 

1649 SIZE_OFFSET = 124 

1650 ZERO_FILLED_REC = 2 

1651 NULL_IDEN = '\x00' 

1652 DIR_IDEN = '/' 

1653 CHECKSUM_IDEN = '.checksum' 

1654 OVA_FILE = 'ova.xml' 

1655 

1656 # Init gunzips the file using a subprocess, and reads stdout later 

1657 # as and when needed 

1658 def __init__(self, filename): 

1659 self.__extract_path = '' 

1660 self.__filename = filename 

1661 cmd = 'gunzip -cd %s' % filename 

1662 try: 

1663 self.spawn_p = subprocess.Popen( 

1664 cmd, shell=True, \ 

1665 stdin=subprocess.PIPE, stdout=subprocess.PIPE, \ 

1666 stderr=subprocess.PIPE, close_fds=True) 

1667 except Exception as e: 

1668 SMlog("Error: %s. Uncompress failed for %s" % (str(e), filename)) 

1669 raise Exception(str(e)) 

1670 

1671 # Create dir to extract the files 

1672 self.__extract_path = tempfile.mkdtemp() 

1673 

1674 def __del__(self): 

1675 shutil.rmtree(self.__extract_path) 

1676 

1677 # Class supports Generator expression. 'for f_name, checksum in getTuple()' 

1678 # returns filename, checksum content. Returns filename, '' in case 

1679 # of checksum file missing. e.g. ova.xml 

1680 def getTuple(self): 

1681 zerod_record = 0 

1682 ret_f_name = '' 

1683 ret_base_f_name = '' 

1684 

1685 try: 

1686 # Read tar file as sets of file and checksum. 

1687 while True: 

1688 # Read the output of spawned process, or output of gunzip 

1689 f_hdr = self.spawn_p.stdout.read(self.HDR_SIZE) 

1690 

1691 # Break out in case of end of file 

1692 if f_hdr == '': 

1693 if zerod_record == extractXVA.ZERO_FILLED_REC: 

1694 break 

1695 else: 

1696 SMlog('Error. Expects %d zero records', \ 

1697 extractXVA.ZERO_FILLED_REC) 

1698 raise Exception('Unrecognized end of file') 

1699 

1700 # Watch out for zero records, two zero records 

1701 # denote end of file. 

1702 if f_hdr == extractXVA.NULL_IDEN * extractXVA.HDR_SIZE: 

1703 zerod_record += 1 

1704 continue 

1705 

1706 f_name = f_hdr[:f_hdr.index(extractXVA.NULL_IDEN)] 

1707 # File header may be for a folder, if so ignore the header 

1708 if not f_name.endswith(extractXVA.DIR_IDEN): 

1709 f_size_octal = f_hdr[extractXVA.SIZE_OFFSET: \ 

1710 extractXVA.SIZE_OFFSET + extractXVA.SIZE_LEN] 

1711 f_size = int(f_size_octal, 8) 

1712 if f_name.endswith(extractXVA.CHECKSUM_IDEN): 

1713 if f_name.rstrip(extractXVA.CHECKSUM_IDEN) == \ 

1714 ret_base_f_name: 

1715 checksum = self.spawn_p.stdout.read(f_size) 

1716 yield(ret_f_name, checksum) 

1717 else: 

1718 # Expects file followed by its checksum 

1719 SMlog('Error. Sequence mismatch starting with %s', \ 

1720 ret_f_name) 

1721 raise Exception( \ 

1722 'Files out of sequence starting with %s', \ 

1723 ret_f_name) 

1724 else: 

1725 # In case of ova.xml, read the contents into a file and 

1726 # return the file name to the caller. For other files, 

1727 # read the contents into a file, it will 

1728 # be used when a .checksum file is encountered. 

1729 ret_f_name = '%s/%s' % (self.__extract_path, f_name) 

1730 ret_base_f_name = f_name 

1731 

1732 # Check if the folder exists on the target location, 

1733 # else create it. 

1734 folder_path = ret_f_name[:ret_f_name.rfind('/')] 

1735 if not os.path.exists(folder_path): 

1736 os.mkdir(folder_path) 

1737 

1738 # Store the file to the tmp folder, strip the tail \0 

1739 f = open(ret_f_name, 'w') 

1740 f.write(self.spawn_p.stdout.read(f_size)) 

1741 f.close() 

1742 if f_name == extractXVA.OVA_FILE: 

1743 yield(ret_f_name, '') 

1744 

1745 # Skip zero'd portion of data block 

1746 round_off = f_size % extractXVA.BLOCK_SIZE 

1747 if round_off != 0: 

1748 zeros = self.spawn_p.stdout.read( 

1749 extractXVA.BLOCK_SIZE - round_off) 

1750 except Exception as e: 

1751 SMlog("Error: %s. File set extraction failed %s" % (str(e), \ 

1752 self.__filename)) 

1753 

1754 # Kill and Drain stdout of the gunzip process, 

1755 # else gunzip might block on stdout 

1756 os.kill(self.spawn_p.pid, signal.SIGTERM) 

1757 self.spawn_p.communicate() 

1758 raise Exception(str(e)) 

1759 

1760illegal_xml_chars = [(0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84), (0x86, 0x9F), 

1761 (0xD800, 0xDFFF), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF), 

1762 (0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), (0x3FFFE, 0x3FFFF), 

1763 (0x4FFFE, 0x4FFFF), (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF), 

1764 (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), (0x9FFFE, 0x9FFFF), 

1765 (0xAFFFE, 0xAFFFF), (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF), 

1766 (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF), 

1767 (0x10FFFE, 0x10FFFF)] 

1768 

1769illegal_ranges = ["%s-%s" % (chr(low), chr(high)) 

1770 for (low, high) in illegal_xml_chars 

1771 if low < sys.maxunicode] 

1772 

1773illegal_xml_re = re.compile(u'[%s]' % u''.join(illegal_ranges)) 

1774 

1775 

1776def isLegalXMLString(s): 

1777 """Tells whether this is a valid XML string (i.e. it does not contain 

1778 illegal XML characters specified in 

1779 http://www.w3.org/TR/2004/REC-xml-20040204/#charsets). 

1780 """ 

1781 

1782 if len(s) > 0: 

1783 return re.search(illegal_xml_re, s) is None 

1784 else: 

1785 return True 

1786 

1787 

1788def unictrunc(string, max_bytes): 

1789 """ 

1790 Given a string, returns the largest number of elements for a prefix 

1791 substring of it, such that the UTF-8 encoding of this substring takes no 

1792 more than the given number of bytes. 

1793 

1794 The string may be given as a unicode string or a UTF-8 encoded byte 

1795 string, and the number returned will be in characters or bytes 

1796 accordingly. Note that in the latter case, the substring will still be a 

1797 valid UTF-8 encoded string (which is to say, it won't have been truncated 

1798 part way through a multibyte sequence for a unicode character). 

1799 

1800 string: the string to truncate 

1801 max_bytes: the maximum number of bytes the truncated string can be 

1802 """ 

1803 if isinstance(string, str): 

1804 return_chars = True 

1805 else: 

1806 return_chars = False 

1807 string = string.decode('UTF-8') 

1808 

1809 cur_chars = 0 

1810 cur_bytes = 0 

1811 for char in string: 

1812 charsize = len(char.encode('UTF-8')) 

1813 if cur_bytes + charsize > max_bytes: 

1814 break 

1815 else: 

1816 cur_chars += 1 

1817 cur_bytes += charsize 

1818 return cur_chars if return_chars else cur_bytes 

1819 

1820 

1821def hideValuesInPropMap(propmap, propnames): 

1822 """ 

1823 Worker function: input simple map of prop name/value pairs, and 

1824 a list of specific propnames whose values we want to hide. 

1825 Loop through the "hide" list, and if any are found, hide the 

1826 value and return the altered map. 

1827 If none found, return the original map 

1828 """ 

1829 matches = [] 

1830 for propname in propnames: 

1831 if propname in propmap: 1831 ↛ 1832line 1831 didn't jump to line 1832, because the condition on line 1831 was never true

1832 matches.append(propname) 

1833 

1834 if matches: 1834 ↛ 1835line 1834 didn't jump to line 1835, because the condition on line 1834 was never true

1835 deepCopyRec = copy.deepcopy(propmap) 

1836 for match in matches: 

1837 deepCopyRec[match] = '******' 

1838 return deepCopyRec 

1839 

1840 return propmap 

1841# define the list of propnames whose value we want to hide 

1842 

1843PASSWD_PROP_KEYS = ['password', 'cifspassword', 'chappassword', 'incoming_chappassword'] 

1844DEFAULT_SEGMENT_LEN = 950 

1845 

1846 

1847def hidePasswdInConfig(config): 

1848 """ 

1849 Function to hide passwd values in a simple prop map, 

1850 for example "device_config" 

1851 """ 

1852 return hideValuesInPropMap(config, PASSWD_PROP_KEYS) 

1853 

1854 

1855def hidePasswdInParams(params, configProp): 

1856 """ 

1857 Function to hide password values in a specified property which 

1858 is a simple map of prop name/values, and is itself an prop entry 

1859 in a larger property map. 

1860 For example, param maps containing "device_config", or 

1861 "sm_config", etc 

1862 """ 

1863 params[configProp] = hideValuesInPropMap(params[configProp], PASSWD_PROP_KEYS) 

1864 return params 

1865 

1866 

1867def hideMemberValuesInXmlParams(xmlParams, propnames=PASSWD_PROP_KEYS): 

1868 """ 

1869 Function to hide password values in XML params, specifically 

1870 for the XML format of incoming params to SR modules. 

1871 Uses text parsing: loop through the list of specific propnames 

1872 whose values we want to hide, and: 

1873 - Assemble a full "prefix" containing each property name, e.g., 

1874 "<member><name>password</name><value>" 

1875 - Test the XML if it contains that string, save the index. 

1876 - If found, get the index of the ending tag 

1877 - Truncate the return string starting with the password value. 

1878 - Append the substitute "*******" value string. 

1879 - Restore the rest of the original string starting with the end tag. 

1880 """ 

1881 findStrPrefixHead = "<member><name>" 

1882 findStrPrefixTail = "</name><value>" 

1883 findStrSuffix = "</value>" 

1884 strlen = len(xmlParams) 

1885 

1886 for propname in propnames: 

1887 findStrPrefix = findStrPrefixHead + propname + findStrPrefixTail 

1888 idx = xmlParams.find(findStrPrefix) 

1889 if idx != -1: # if found any of them 

1890 idx += len(findStrPrefix) 

1891 idx2 = xmlParams.find(findStrSuffix, idx) 

1892 if idx2 != -1: 

1893 retStr = xmlParams[0:idx] 

1894 retStr += "******" 

1895 retStr += xmlParams[idx2:strlen] 

1896 return retStr 

1897 else: 

1898 return xmlParams 

1899 return xmlParams 

1900 

1901 

1902def splitXmlText(xmlData, segmentLen=DEFAULT_SEGMENT_LEN, showContd=False): 

1903 """ 

1904 Split xml string data into substrings small enough for the 

1905 syslog line length limit. Split at tag end markers ( ">" ). 

1906 Usage: 

1907 strList = [] 

1908 strList = splitXmlText( longXmlText, maxLineLen ) # maxLineLen is optional 

1909 """ 

1910 remainingData = str(xmlData) 

1911 

1912 # "Un-pretty-print" 

1913 remainingData = remainingData.replace('\n', '') 

1914 remainingData = remainingData.replace('\t', '') 

1915 

1916 remainingChars = len(remainingData) 

1917 returnData = '' 

1918 

1919 thisLineNum = 0 

1920 while remainingChars > segmentLen: 

1921 thisLineNum = thisLineNum + 1 

1922 index = segmentLen 

1923 tmpStr = remainingData[:segmentLen] 

1924 tmpIndex = tmpStr.rfind('>') 

1925 if tmpIndex != -1: 

1926 index = tmpIndex + 1 

1927 

1928 tmpStr = tmpStr[:index] 

1929 remainingData = remainingData[index:] 

1930 remainingChars = len(remainingData) 

1931 

1932 if showContd: 

1933 if thisLineNum != 1: 

1934 tmpStr = '(Cont\'d): ' + tmpStr 

1935 tmpStr = tmpStr + ' (Cont\'d):' 

1936 

1937 returnData += tmpStr + '\n' 

1938 

1939 if showContd and thisLineNum > 0: 

1940 remainingData = '(Cont\'d): ' + remainingData 

1941 returnData += remainingData 

1942 

1943 return returnData 

1944 

1945 

1946def inject_failure(): 

1947 raise Exception('injected failure') 

1948 

1949 

1950def open_atomic(path, mode=None): 

1951 """Atomically creates a file if, and only if it does not already exist. 

1952 Leaves the file open and returns the file object. 

1953 

1954 path: the path to atomically open 

1955 mode: "r" (read), "w" (write), or "rw" (read/write) 

1956 returns: an open file object""" 

1957 

1958 assert path 

1959 

1960 flags = os.O_CREAT | os.O_EXCL 

1961 modes = {'r': os.O_RDONLY, 'w': os.O_WRONLY, 'rw': os.O_RDWR} 

1962 if mode: 

1963 if mode not in modes: 

1964 raise Exception('invalid access mode ' + mode) 

1965 flags |= modes[mode] 

1966 fd = os.open(path, flags) 

1967 try: 

1968 if mode: 

1969 return os.fdopen(fd, mode) 

1970 else: 

1971 return os.fdopen(fd) 

1972 except: 

1973 os.close(fd) 

1974 raise 

1975 

1976 

1977def isInvalidVDI(exception): 

1978 return exception.details[0] == "HANDLE_INVALID" or \ 

1979 exception.details[0] == "UUID_INVALID" 

1980 

1981 

1982def get_pool_restrictions(session): 

1983 """Returns pool restrictions as a map, @session must be already 

1984 established.""" 

1985 return list(session.xenapi.pool.get_all_records().values())[0]['restrictions'] 

1986 

1987 

1988def read_caching_is_restricted(session): 

1989 """Tells whether read caching is restricted.""" 

1990 if session is None: 1990 ↛ 1991line 1990 didn't jump to line 1991, because the condition on line 1990 was never true

1991 return True 

1992 restrictions = get_pool_restrictions(session) 

1993 if 'restrict_read_caching' in restrictions and \ 1993 ↛ 1995line 1993 didn't jump to line 1995, because the condition on line 1993 was never true

1994 restrictions['restrict_read_caching'] == "true": 

1995 return True 

1996 return False 

1997 

1998 

1999def sessions_less_than_targets(other_config, device_config): 

2000 if 'multihomelist' in device_config and 'iscsi_sessions' in other_config: 

2001 sessions = int(other_config['iscsi_sessions']) 

2002 targets = len(device_config['multihomelist'].split(',')) 

2003 SMlog("Targets %d and iscsi_sessions %d" % (targets, sessions)) 

2004 return (sessions < targets) 

2005 else: 

2006 return False 

2007 

2008 

2009def enable_and_start_service(name, start): 

2010 attempt = 0 

2011 while True: 

2012 attempt += 1 

2013 fn = 'enable' if start else 'disable' 

2014 args = ('systemctl', fn, '--now', name) 

2015 (ret, out, err) = doexec(args) 

2016 if ret == 0: 

2017 return 

2018 elif attempt >= 3: 

2019 raise Exception( 

2020 'Failed to {} {}: {} {}'.format(fn, name, out, err) 

2021 ) 

2022 time.sleep(1) 

2023 

2024 

2025def stop_service(name): 

2026 args = ('systemctl', 'stop', name) 

2027 (ret, out, err) = doexec(args) 

2028 if ret == 0: 

2029 return 

2030 raise Exception('Failed to stop {}: {} {}'.format(name, out, err)) 

2031 

2032 

2033def restart_service(name): 

2034 attempt = 0 

2035 while True: 

2036 attempt += 1 

2037 SMlog('Restarting service {} {}...'.format(name, attempt)) 

2038 args = ('systemctl', 'restart', name) 

2039 (ret, out, err) = doexec(args) 

2040 if ret == 0: 

2041 return 

2042 elif attempt >= 3: 

2043 SMlog('Restart service FAILED {} {}'.format(name, attempt)) 

2044 raise Exception( 

2045 'Failed to restart {}: {} {}'.format(name, out, err) 

2046 ) 

2047 time.sleep(1) 

2048 

2049 

2050def check_pid_exists(pid): 

2051 try: 

2052 os.kill(pid, 0) 

2053 except OSError: 

2054 return False 

2055 else: 

2056 return True 

2057 

2058 

2059def make_profile(name, function): 

2060 """ 

2061 Helper to execute cProfile using unique log file. 

2062 """ 

2063 

2064 import cProfile 

2065 import itertools 

2066 import os.path 

2067 import time 

2068 

2069 assert name 

2070 assert function 

2071 

2072 FOLDER = '/tmp/sm-perfs/' 

2073 makedirs(FOLDER) 

2074 

2075 filename = time.strftime('{}_%Y%m%d_%H%M%S.prof'.format(name)) 

2076 

2077 def gen_path(path): 

2078 yield path 

2079 root, ext = os.path.splitext(path) 

2080 for i in itertools.count(start=1, step=1): 

2081 yield root + '.{}.'.format(i) + ext 

2082 

2083 for profile_path in gen_path(FOLDER + filename): 

2084 try: 

2085 file = open_atomic(profile_path, 'w') 

2086 file.close() 

2087 break 

2088 except OSError as e: 

2089 if e.errno == errno.EEXIST: 

2090 pass 

2091 else: 

2092 raise 

2093 

2094 try: 

2095 SMlog('* Start profiling of {} ({}) *'.format(name, filename)) 

2096 cProfile.runctx('function()', None, locals(), profile_path) 

2097 finally: 

2098 SMlog('* End profiling of {} ({}) *'.format(name, filename)) 

2099 

2100 

2101def strtobool(str): 

2102 # Note: `distutils` package is deprecated and slated for removal in Python 3.12. 

2103 # There is not alternative for strtobool. 

2104 # See: https://peps.python.org/pep-0632/#migration-advice 

2105 # So this is a custom implementation with differences: 

2106 # - A boolean is returned instead of integer 

2107 # - Empty string and None are supported (False is returned in this case) 

2108 if not str: 2108 ↛ 2110line 2108 didn't jump to line 2110, because the condition on line 2108 was never false

2109 return False 

2110 str = str.lower() 

2111 if str in ('y', 'yes', 't', 'true', 'on', '1'): 

2112 return True 

2113 if str in ('n', 'no', 'f', 'false', 'off', '0'): 

2114 return False 

2115 raise ValueError("invalid truth value '{}'".format(str)) 

2116 

2117 

2118def find_executable(name): 

2119 return shutil.which(name) 

2120 

2121 

2122def conditional_decorator(decorator, condition): 

2123 def wrapper(func): 

2124 if not condition: 2124 ↛ 2126line 2124 didn't jump to line 2126, because the condition on line 2124 was never false

2125 return func 

2126 return decorator(func) 

2127 return wrapper