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 

45from sm_typing import List, Optional 

46 

47NO_LOGGING_STAMPFILE = '/etc/xensource/no_sm_log' 

48 

49IORETRY_MAX = 20 # retries 

50IORETRY_PERIOD = 1.0 # seconds 

51 

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

53_SM_SYSLOG_FACILITY = syslog.LOG_LOCAL2 

54LOG_EMERG = syslog.LOG_EMERG 

55LOG_ALERT = syslog.LOG_ALERT 

56LOG_CRIT = syslog.LOG_CRIT 

57LOG_ERR = syslog.LOG_ERR 

58LOG_WARNING = syslog.LOG_WARNING 

59LOG_NOTICE = syslog.LOG_NOTICE 

60LOG_INFO = syslog.LOG_INFO 

61LOG_DEBUG = syslog.LOG_DEBUG 

62 

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

64 

65CMD_DD = "/bin/dd" 

66CMD_KICKPIPE = '/opt/xensource/libexec/kickpipe' 

67 

68FIST_PAUSE_PERIOD = 30 # seconds 

69 

70 

71class SMException(Exception): 

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

73 XenError""" 

74 

75 

76class CommandException(SMException): 

77 def error_message(self, code): 

78 if code > 0: 

79 return os.strerror(code) 

80 elif code < 0: 

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

82 return "Success" 

83 

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

85 self.code = code 

86 self.cmd = cmd 

87 self.reason = reason 

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

89 

90 

91class SRBusyException(SMException): 

92 """The SR could not be locked""" 

93 pass 

94 

95 

96def logException(tag): 

97 info = sys.exc_info() 

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

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

100 sys.exit(0) 

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

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

103 SMlog(str) 

104 

105 

106def roundup(divisor, value): 

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

108 

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

110 value = 1 

111 if value % divisor != 0: 

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

113 return value 

114 

115 

116def to_plain_string(obj): 

117 if obj is None: 

118 return None 

119 if isinstance(obj, dict) and len(obj) == 0: 

120 SMlog(f"util.to_plain_string() corrected empty dict to empty str") 

121 return "" 

122 return str(obj) 

123 

124 

125def shellquote(arg): 

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

127 

128 

129def make_WWN(name): 

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

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

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

133 # inject dashes for each nibble 

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

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

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

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

138 return name 

139 

140 

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

142 syslog.openlog(ident, 0, facility) 

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

144 syslog.closelog() 

145 

146 

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

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

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

150 _logToSyslog(ident, _SM_SYSLOG_FACILITY, priority, message_line) 

151 

152 

153class LoggerCounter: 

154 def __init__(self, max_repeats): 

155 self.previous_message = None 

156 self.max_repeats = max_repeats 

157 self.repeat_counter = 0 

158 

159 def log(self, message): 

160 self.repeat_counter += 1 

161 if self.previous_message != message or self.repeat_counter == self.max_repeats: 

162 SMlog(message) 

163 self.previous_message = message 

164 self.repeat_counter = 0 

165 

166def _getDateString(): 

167 d = datetime.datetime.now() 

168 t = d.timetuple() 

169 return "%s-%s-%s:%s:%s:%s" % \ 

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

171 

172 

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

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

175 env = None 

176 if new_env: 

177 env = dict(os.environ) 

178 env.update(new_env) 

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

180 stdout=subprocess.PIPE, 

181 stderr=subprocess.PIPE, 

182 close_fds=True, env=env, 

183 universal_newlines=text) 

184 

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

186 inputtext = inputtext.encode() 

187 

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

189 

190 rc = proc.returncode 

191 return rc, stdout, stderr 

192 

193 

194def is_string(value): 

195 return isinstance(value, str) 

196 

197 

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

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

200# replace the original ones at some later date. 

201# 

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

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

204# written to the logs. 

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

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

207 cmdlist_for_exec = [] 

208 cmdlist_for_log = [] 

209 for item in cmdlist: 

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

211 cmdlist_for_exec.append(item) 

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

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

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

215 else: 

216 cmdlist_for_log.append(item) 

217 else: 

218 cmdlist_for_log.append(item) 

219 else: 

220 cmdlist_for_exec.append(item[0]) 

221 cmdlist_for_log.append(item[1]) 

222 

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

224 SMlog(cmdlist_for_log) 

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

226 if rc != expect_rc: 

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

228 (rc, stdout, stderr)) 

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

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

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

232 stderr = stdout 

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

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

235 SMlog(" pread SUCCESS") 

236 return stdout 

237 

238 

239# POSIX guaranteed atomic within the same file system. 

240# Supply directory to ensure tempfile is created 

241# in the same directory. 

242def atomicFileWrite(targetFile, directory, text): 

243 

244 file = None 

245 try: 

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

247 # our responsibility to clean it up. 

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

249 file = open(tempPath, 'w') 

250 file.write(text) 

251 

252 # Ensure flushed to disk. 

253 file.flush() 

254 os.fsync(file.fileno()) 

255 file.close() 

256 

257 os.rename(tempPath, targetFile) 

258 except OSError: 

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

260 

261 finally: 

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

263 file.close() 

264 

265 if os.path.isfile(tempPath): 

266 os.remove(tempPath) 

267 

268 

269#Read STDOUT from cmdlist and discard STDERR output 

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

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

272 

273 

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

275def pread3(cmdlist, text): 

276 SMlog(cmdlist) 

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

278 if rc: 

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

280 (rc, stdout, stderr)) 

281 if '' == stderr: 

282 stderr = stdout 

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

284 SMlog(" pread3 SUCCESS") 

285 return stdout 

286 

287 

288def listdir(path, quiet=False): 

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

290 try: 

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

292 if len(text) == 0: 

293 return [] 

294 return text.split('\n') 

295 except CommandException as inst: 

296 if inst.code == errno.ENOENT: 

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

298 else: 

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

300 

301 

302def gen_uuid(): 

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

304 return pread(cmd)[:-1] 

305 

306 

307def match_uuid(s): 

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

309 return regex.search(s, 0) 

310 

311 

312def findall_uuid(s): 

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

314 return regex.findall(s, 0) 

315 

316 

317def exactmatch_uuid(s): 

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

319 return regex.search(s, 0) 

320 

321 

322def start_log_entry(srpath, path, args): 

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

324 logstring += " log: " 

325 logstring += srpath 

326 logstring += " " + path 

327 for element in args: 

328 logstring += " " + element 

329 try: 

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

331 file.write(logstring) 

332 file.write("\n") 

333 file.close() 

334 except: 

335 pass 

336 

337 # failed to write log ... 

338 

339def end_log_entry(srpath, path, args): 

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

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

342 logstring += " end: " 

343 logstring += srpath 

344 logstring += " " + path 

345 for element in args: 

346 logstring += " " + element 

347 try: 

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

349 file.write(logstring) 

350 file.write("\n") 

351 file.close() 

352 except: 

353 pass 

354 

355 # failed to write log ... 

356 # for now print 

357 # print "%s" % logstring 

358 

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

360 retries = 0 

361 while True: 

362 try: 

363 return f() 

364 except OSError as ose: 

365 err = int(ose.errno) 

366 if not err in errlist: 

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

368 except CommandException as ce: 

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

370 raise 

371 

372 retries += 1 

373 if retries >= maxretry: 

374 break 

375 

376 time.sleep(period) 

377 

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

379 

380 

381def ioretry_stat(path, maxretry=IORETRY_MAX): 

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

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

384 retries = 0 

385 while retries < maxretry: 

386 stat = os.statvfs(path) 

387 if stat.f_blocks != -1: 

388 return stat 

389 time.sleep(1) 

390 retries += 1 

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

392 

393 

394def sr_get_capability(sr_uuid, session=None): 

395 result = [] 

396 local_session = None 

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

398 local_session = get_localAPI_session() 

399 session = local_session 

400 

401 try: 

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

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

404 sm_rec = session.xenapi.SM.get_all_records_where( 

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

406 

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

408 if len(sm_rec) > 0: 

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

410 

411 return result 

412 finally: 

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

414 local_session.xenapi.session.logout() 

415 

416def sr_get_driver_info(driver_info): 

417 results = {} 

418 # first add in the vanilla stuff 

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

420 'driver_version', 'required_api_version']: 

421 results[key] = driver_info[key] 

422 # add the capabilities (xmlrpc array) 

423 # enforcing activate/deactivate for blktap2 

424 caps = driver_info['capabilities'] 

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

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

427 if not cap in caps: 

428 caps.append(cap) 

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

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

431 

432 results['capabilities'] = caps 

433 # add in the configuration options 

434 options = [] 

435 for option in driver_info['configuration']: 

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

437 results['configuration'] = options 

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

439 

440 

441def return_nil(): 

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

443 

444 

445def SRtoXML(SRlist): 

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

447 driver = dom.createElement("SRlist") 

448 dom.appendChild(driver) 

449 

450 for key in SRlist.keys(): 

451 dict = SRlist[key] 

452 entry = dom.createElement("SR") 

453 driver.appendChild(entry) 

454 

455 e = dom.createElement("UUID") 

456 entry.appendChild(e) 

457 textnode = dom.createTextNode(key) 

458 e.appendChild(textnode) 

459 

460 if 'size' in dict: 

461 e = dom.createElement("Size") 

462 entry.appendChild(e) 

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

464 e.appendChild(textnode) 

465 

466 if 'storagepool' in dict: 

467 e = dom.createElement("StoragePool") 

468 entry.appendChild(e) 

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

470 e.appendChild(textnode) 

471 

472 if 'aggregate' in dict: 

473 e = dom.createElement("Aggregate") 

474 entry.appendChild(e) 

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

476 e.appendChild(textnode) 

477 

478 return dom.toprettyxml() 

479 

480 

481def pathexists(path): 

482 try: 

483 os.lstat(path) 

484 return True 

485 except OSError as inst: 

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

487 time.sleep(1) 

488 try: 

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

490 os.lstat(path) 

491 return True 

492 except: 

493 pass 

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

495 return False 

496 

497 

498def force_unlink(path): 

499 try: 

500 os.unlink(path) 

501 except OSError as e: 

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

503 raise 

504 

505 

506def create_secret(session, secret): 

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

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

509 

510 

511def get_secret(session, uuid): 

512 try: 

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

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

515 except: 

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

517 

518 

519def get_real_path(path): 

520 "Follow symlinks to the actual file" 

521 absPath = path 

522 directory = '' 

523 while os.path.islink(absPath): 

524 directory = os.path.dirname(absPath) 

525 absPath = os.readlink(absPath) 

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

527 return absPath 

528 

529 

530def wait_for_path(path, timeout): 

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

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

533 return True 

534 time.sleep(1) 

535 return False 

536 

537 

538def wait_for_nopath(path, timeout): 

539 for i in range(0, timeout): 

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

541 return True 

542 time.sleep(1) 

543 return False 

544 

545 

546def wait_for_path_multi(path, timeout): 

547 for i in range(0, timeout): 

548 paths = glob.glob(path) 

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

550 if len(paths): 

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

552 return paths[0] 

553 time.sleep(1) 

554 return "" 

555 

556 

557def isdir(path): 

558 try: 

559 st = os.stat(path) 

560 return stat.S_ISDIR(st.st_mode) 

561 except OSError as inst: 

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

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

564 return False 

565 

566 

567def get_single_entry(path): 

568 f = open(path, 'r') 

569 line = f.readline() 

570 f.close() 

571 return line.rstrip() 

572 

573 

574def get_fs_size(path): 

575 st = ioretry_stat(path) 

576 return st.f_blocks * st.f_frsize 

577 

578 

579def get_fs_utilisation(path): 

580 st = ioretry_stat(path) 

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

582 st.f_frsize 

583 

584 

585def ismount(path): 

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

587 try: 

588 s1 = os.stat(path) 

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

590 except OSError as inst: 

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

592 dev1 = s1.st_dev 

593 dev2 = s2.st_dev 

594 if dev1 != dev2: 

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

596 ino1 = s1.st_ino 

597 ino2 = s2.st_ino 

598 if ino1 == ino2: 

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

600 return False 

601 

602 

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

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

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

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

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

608 makedirs(head, mode) 

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

610 return 

611 try: 

612 os.mkdir(name, mode) 

613 except OSError as exc: 

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

615 if mode: 

616 os.chmod(name, mode) 

617 pass 

618 else: 

619 raise 

620 

621 

622def zeroOut(path, fromByte, bytes): 

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

624 blockSize = 4096 

625 

626 fromBlock = fromByte // blockSize 

627 if fromByte % blockSize: 

628 fromBlock += 1 

629 bytesBefore = fromBlock * blockSize - fromByte 

630 if bytesBefore > bytes: 

631 bytesBefore = bytes 

632 bytes -= bytesBefore 

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

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

635 try: 

636 pread2(cmd) 

637 except CommandException: 

638 return False 

639 

640 blocks = bytes // blockSize 

641 bytes -= blocks * blockSize 

642 fromByte = (fromBlock + blocks) * blockSize 

643 if blocks: 

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

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

646 try: 

647 pread2(cmd) 

648 except CommandException: 

649 return False 

650 

651 if bytes: 

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

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

654 try: 

655 pread2(cmd) 

656 except CommandException: 

657 return False 

658 

659 return True 

660 

661 

662def wipefs(blockdev): 

663 "Wipe filesystem signatures from `blockdev`" 

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

665 

666 

667def match_rootdev(s): 

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

669 return regex.search(s, 0) 

670 

671 

672def getrootdev(): 

673 filename = '/etc/xensource-inventory' 

674 try: 

675 f = open(filename, 'r') 

676 except: 

677 raise xs_errors.XenError('EIO', \ 

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

679 rootdev = '' 

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

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

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

683 raise xs_errors.XenError('NoRootDev') 

684 return rootdev 

685 

686 

687def getrootdevID(): 

688 rootdev = getrootdev() 

689 try: 

690 rootdevID = scsiutil.getSCSIid(rootdev) 

691 except: 

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

693 % rootdev) 

694 return '' 

695 

696 if not len(rootdevID): 

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

698 % rootdev) 

699 

700 return rootdevID 

701 

702 

703def get_localAPI_session(): 

704 # First acquire a valid session 

705 session = XenAPI.xapi_local() 

706 try: 

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

708 except: 

709 raise xs_errors.XenError('APISession') 

710 return session 

711 

712 

713def get_this_host(): 

714 uuid = None 

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

716 for line in f.readlines(): 

717 if line.startswith("INSTALLATION_UUID"): 

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

719 f.close() 

720 return uuid 

721 

722 

723def get_master_ref(session): 

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

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

726 

727 

728def is_master(session): 

729 return get_this_host_ref(session) == get_master_ref(session) 

730 

731 

732def get_localhost_ref(session): 

733 filename = '/etc/xensource-inventory' 

734 try: 

735 f = open(filename, 'r') 

736 except: 

737 raise xs_errors.XenError('EIO', \ 

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

739 domid = '' 

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

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

742 if not domid: 

743 raise xs_errors.XenError('APILocalhost') 

744 

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

746 for vm in vms: 

747 record = vms[vm] 

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

749 hostid = record["resident_on"] 

750 return hostid 

751 raise xs_errors.XenError('APILocalhost') 

752 

753 

754def match_domain_id(s): 

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

756 return regex.search(s, 0) 

757 

758 

759def get_hosts_attached_on(session, vdi_uuids): 

760 host_refs = {} 

761 for vdi_uuid in vdi_uuids: 

762 try: 

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

764 except XenAPI.Failure: 

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

766 continue 

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

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

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

770 return host_refs.keys() 

771 

772def get_hosts_attached_on_with_vdi_uuid(session, vdi_uuids): 

773 """ 

774 Return a dict of {vdi_uuid: host OpaqueRef} 

775 """ 

776 host_refs = {} 

777 for vdi_uuid in vdi_uuids: 

778 try: 

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

780 except XenAPI.Failure: 

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

782 continue 

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

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

785 host_refs[vdi_uuid] = key[len('host_'):] 

786 return host_refs 

787 

788def get_this_host_address(session): 

789 host_uuid = get_this_host() 

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

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

792 

793def get_host_addresses(session): 

794 addresses = [] 

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

796 for record in hosts.values(): 

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

798 return addresses 

799 

800def get_this_host_ref(session): 

801 host_uuid = get_this_host() 

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

803 return host_ref 

804 

805 

806def get_slaves_attached_on(session, vdi_uuids): 

807 "assume this host is the SR master" 

808 host_refs = get_hosts_attached_on(session, vdi_uuids) 

809 master_ref = get_this_host_ref(session) 

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

811 

812def get_enabled_hosts(session): 

813 """ 

814 Returns a list of host refs that are enabled in the pool. 

815 """ 

816 return list(session.xenapi.host.get_all_records_where('field "enabled" = "true"').keys()) 

817 

818def get_online_hosts(session): 

819 online_hosts = [] 

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

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

822 metricsRef = host_rec["metrics"] 

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

824 if metrics["live"]: 

825 online_hosts.append(host_ref) 

826 return online_hosts 

827 

828 

829def get_all_slaves(session): 

830 "assume this host is the SR master" 

831 host_refs = get_online_hosts(session) 

832 master_ref = get_this_host_ref(session) 

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

834 

835 

836def is_attached_rw(sm_config): 

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

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

839 return True 

840 return False 

841 

842 

843def attached_as(sm_config): 

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

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

846 return val 

847 

848 

849def find_my_pbd_record(session, host_ref, sr_ref): 

850 try: 

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

852 for pbd_ref in pbds.keys(): 

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

854 return [pbd_ref, pbds[pbd_ref]] 

855 return None 

856 except Exception as e: 

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

858 return None 

859 

860 

861def find_my_pbd(session, host_ref, sr_ref): 

862 ret = find_my_pbd_record(session, host_ref, sr_ref) 

863 if ret is not None: 

864 return ret[0] 

865 else: 

866 return None 

867 

868 

869def test_hostPBD_devs(session, sr_uuid, devs): 

870 host = get_localhost_ref(session) 

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

872 try: 

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

874 except: 

875 raise xs_errors.XenError('APIPBDQuery') 

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

877 for pbd in pbds: 

878 record = pbds[pbd] 

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

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

881 break 

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

883 devconfig = record["device_config"] 

884 if 'device' in devconfig: 

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

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

887 return True 

888 return False 

889 

890 

891def test_hostPBD_lun(session, targetIQN, LUNid): 

892 host = get_localhost_ref(session) 

893 try: 

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

895 except: 

896 raise xs_errors.XenError('APIPBDQuery') 

897 for pbd in pbds: 

898 record = pbds[pbd] 

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

900 devconfig = record["device_config"] 

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

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

903 devconfig['LUNid'] == LUNid: 

904 return True 

905 return False 

906 

907 

908def test_SCSIid(session, sr_uuid, SCSIid): 

909 if sr_uuid is not None: 

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

911 try: 

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

913 except: 

914 raise xs_errors.XenError('APIPBDQuery') 

915 for pbd in pbds: 

916 record = pbds[pbd] 

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

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

919 if sr_uuid is not None: 

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

921 break 

922 devconfig = record["device_config"] 

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

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

925 return True 

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

927 return True 

928 elif 'scsi-' + SCSIid in sm_config: 

929 return True 

930 return False 

931 

932 

933class TimeoutException(SMException): 

934 pass 

935 

936 

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

938 def handler(signum, frame): 

939 raise TimeoutException() 

940 signal.signal(signal.SIGALRM, handler) 

941 signal.alarm(timeoutseconds) 

942 try: 

943 return function(*arguments) 

944 finally: 

945 signal.alarm(0) 

946 

947 

948def _incr_iscsiSR_refcount(targetIQN, uuid): 

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

950 os.mkdir(ISCSI_REFDIR) 

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

952 try: 

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

954 except: 

955 raise xs_errors.XenError('LVMRefCount', \ 

956 opterr='file %s' % filename) 

957 

958 f.seek(0) 

959 found = False 

960 refcount = 0 

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

962 refcount += 1 

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

964 found = True 

965 if not found: 

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

967 refcount += 1 

968 f.close() 

969 return refcount 

970 

971 

972def _decr_iscsiSR_refcount(targetIQN, uuid): 

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

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

975 return 0 

976 try: 

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

978 except: 

979 raise xs_errors.XenError('LVMRefCount', \ 

980 opterr='file %s' % filename) 

981 

982 f.seek(0) 

983 output = [] 

984 refcount = 0 

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

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

987 output.append(line.rstrip()) 

988 refcount += 1 

989 if not refcount: 

990 os.unlink(filename) 

991 return refcount 

992 

993 # Re-open file and truncate 

994 f.close() 

995 f = open(filename, 'w') 

996 for i in range(0, refcount): 

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

998 f.close() 

999 return refcount 

1000 

1001 

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

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

1004def test_activePoolPBDs(session, host, uuid): 

1005 try: 

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

1007 except: 

1008 raise xs_errors.XenError('APIPBDQuery') 

1009 for pbd in pbds: 

1010 record = pbds[pbd] 

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

1012 and record["currently_attached"]: 

1013 return True 

1014 return False 

1015 

1016 

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

1018 try: 

1019 pbdref = find_my_pbd(session, host_ref, sr_ref) 

1020 if pbdref is not None: 

1021 key = "mpath-" + SCSIid 

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

1023 except: 

1024 pass 

1025 

1026 

1027def kickpipe_mpathcount(): 

1028 """ 

1029 Issue a kick to the mpathcount service. This will ensure that mpathcount runs 

1030 shortly to update the multipath config records, if it was not already activated 

1031 by a UDEV event. 

1032 """ 

1033 cmd = [CMD_KICKPIPE, "mpathcount"] 

1034 (rc, stdout, stderr) = doexec(cmd) 

1035 return (rc == 0) 

1036 

1037 

1038def _testHost(hostname, port, errstring): 

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

1040 try: 

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

1042 except: 

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

1044 raise xs_errors.XenError('DNSError') 

1045 

1046 timeout = 5 

1047 

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

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

1050 sock.settimeout(timeout) 

1051 try: 

1052 sock.connect(sockinfo[4]) 

1053 # Fix for MS storage server bug 

1054 sock.send(b'\n') 

1055 sock.close() 

1056 except socket.error as reason: 

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

1058 % (timeout, hostname, reason)) 

1059 raise xs_errors.XenError(errstring) 

1060 

1061 

1062def match_scsiID(s, id): 

1063 regex = re.compile(id) 

1064 return regex.search(s, 0) 

1065 

1066 

1067def _isSCSIid(s): 

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

1069 return regex.search(s, 0) 

1070 

1071 

1072def is_usb_device(device): 

1073 cmd = ["udevadm", "info", "-q", "path", "-n", device] 

1074 result = pread2(cmd).split('/') 

1075 return len(result) >= 5 and result[4].startswith('usb') 

1076 

1077 

1078def test_scsiserial(session, device): 

1079 device = os.path.realpath(device) 

1080 if not scsiutil._isSCSIdev(device): 

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

1082 return False 

1083 serial = "" 

1084 try: 

1085 serial += scsiutil.getserial(device) 

1086 except: 

1087 # Error allowed, SCSIid is the important one 

1088 pass 

1089 

1090 try: 

1091 scsiID = scsiutil.getSCSIid(device) 

1092 except: 

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

1094 % device) 

1095 return False 

1096 if not len(scsiID): 

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

1098 % device) 

1099 return False 

1100 

1101 # USB devices can have identical SCSI IDs - prefer matching with serial number 

1102 try: 

1103 usb_device_with_serial = serial and is_usb_device(device) 

1104 except: 

1105 usb_device_with_serial = False 

1106 SMlog("Unable to check if device is USB:") 

1107 SMlog(traceback.format_exc()) 

1108 

1109 try: 

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

1111 except: 

1112 raise xs_errors.XenError('APIFailure') 

1113 for SR in SRs: 

1114 record = SRs[SR] 

1115 conf = record["sm_config"] 

1116 if 'devserial' in conf: 

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

1118 if not usb_device_with_serial and _isSCSIid(dev): 

1119 if match_scsiID(dev, scsiID): 

1120 return True 

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

1122 return True 

1123 return False 

1124 

1125 

1126def default(self, field, thunk): 

1127 try: 

1128 return getattr(self, field) 

1129 except: 

1130 return thunk() 

1131 

1132 

1133def list_VDI_records_in_sr(sr): 

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

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

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

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

1138 return vdis 

1139 

1140 

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

1142def diskFromPartition(partition): 

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

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

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

1146 return m.group(2) 

1147 

1148 numlen = 0 # number of digit characters 

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

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

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

1152 

1153 # is it a cciss? 

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

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

1156 

1157 # is it a mapper path? 

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

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

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

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

1162 else: 

1163 numlen = 0 

1164 

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

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

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

1168 

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

1170 

1171 

1172def dom0_disks(): 

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

1174 disks = [] 

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

1176 for line in f: 

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

1178 if mountpoint == '/': 

1179 disk = diskFromPartition(dev) 

1180 if not (disk in disks): 

1181 disks.append(disk) 

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

1183 return disks 

1184 

1185 

1186def set_scheduler_sysfs_node(node, scheds): 

1187 """ 

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

1189 according to prioritized list schedulers 

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

1191 """ 

1192 

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

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

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

1196 return 

1197 

1198 stored_error = None 

1199 for sched in scheds: 

1200 try: 

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

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

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

1204 return 

1205 except (OSError, IOError) as err: 

1206 stored_error = err 

1207 

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

1209 

1210 

1211def set_scheduler(dev, schedulers=None): 

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

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

1214 

1215 devices = [] 

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

1217 # Remove partition numbers 

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

1219 else: 

1220 rawdev = diskFromPartition(dev) 

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

1222 

1223 for d in devices: 

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

1225 

1226 

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

1228def _getVDIs(srobj): 

1229 VDIs = [] 

1230 try: 

1231 sr_ref = getattr(srobj, 'sr_ref') 

1232 except AttributeError: 

1233 return VDIs 

1234 

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

1236 for vdi in refs: 

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

1238 ref['vdi_ref'] = vdi 

1239 VDIs.append(ref) 

1240 return VDIs 

1241 

1242 

1243def _getVDI(srobj, vdi_uuid): 

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

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

1246 ref['vdi_ref'] = vdi 

1247 return ref 

1248 

1249 

1250def _convertDNS(name): 

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

1252 return addr 

1253 

1254 

1255def _containsVDIinuse(srobj): 

1256 VDIs = _getVDIs(srobj) 

1257 for vdi in VDIs: 

1258 if not vdi['managed']: 

1259 continue 

1260 sm_config = vdi['sm_config'] 

1261 if 'SRRef' in sm_config: 

1262 try: 

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

1264 for pbd in PBDs: 

1265 record = PBDs[pbd] 

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

1267 record["currently_attached"]: 

1268 return True 

1269 except: 

1270 pass 

1271 return False 

1272 

1273 

1274def isVDICommand(cmd): 

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

1276 "vdi_activate", "vdi_deactivate", 

1277 "vdi_epoch_begin", "vdi_epoch_end"]: 

1278 return True 

1279 else: 

1280 return False 

1281 

1282 

1283######################### 

1284# Daemon helper functions 

1285def p_id_fork(): 

1286 try: 

1287 p_id = os.fork() 

1288 except OSError as e: 

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

1290 sys.exit(-1) 

1291 

1292 if (p_id == 0): 

1293 os.setsid() 

1294 try: 

1295 p_id = os.fork() 

1296 except OSError as e: 

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

1298 sys.exit(-1) 

1299 if (p_id == 0): 

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

1301 os.umask(0) 

1302 else: 

1303 os._exit(0) 

1304 else: 

1305 os._exit(0) 

1306 

1307 

1308def daemon(): 

1309 p_id_fork() 

1310 # Query the max file descriptor parameter for this process 

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

1312 

1313 # Close any fds that are open 

1314 for fd in range(0, maxfd): 

1315 try: 

1316 os.close(fd) 

1317 except: 

1318 pass 

1319 

1320 # Redirect STDIN to STDOUT and STDERR 

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

1322 os.dup2(0, 1) 

1323 os.dup2(0, 2) 

1324 

1325################################################################################ 

1326# 

1327# Fist points 

1328# 

1329 

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

1331# 

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

1333# on the SR master; 

1334# 

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

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

1337# - otherwise, the function called is _pause. 

1338 

1339def _pause(secs, name): 

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

1341 time.sleep(secs) 

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

1343 

1344 

1345def _exit(name): 

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

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

1348 

1349 

1350class FistPoint: 

1351 def __init__(self, points): 

1352 #SMlog("Fist points loaded") 

1353 self.points = points 

1354 

1355 def is_legal(self, name): 

1356 return (name in self.points) 

1357 

1358 def is_active(self, name): 

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

1360 

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

1362 session = get_localAPI_session() 

1363 try: 

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

1365 

1366 if started: 

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

1368 else: 

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

1370 finally: 

1371 session.xenapi.session.logout() 

1372 

1373 def activate(self, name, sruuid): 

1374 if name in self.points: 

1375 if self.is_active(name): 

1376 self.mark_sr(name, sruuid, True) 

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

1378 self.mark_sr(name, sruuid, False) 

1379 _exit(name) 

1380 else: 

1381 _pause(FIST_PAUSE_PERIOD, name) 

1382 self.mark_sr(name, sruuid, False) 

1383 else: 

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

1385 

1386 def activate_custom_fn(self, name, fn): 

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

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

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

1390 fn() 

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

1392 else: 

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

1394 

1395 

1396def list_find(f, seq): 

1397 for item in seq: 

1398 if f(item): 

1399 return item 

1400 

1401GCPAUSE_FISTPOINT = "GCLoop_no_pause" 

1402 

1403fistpoint = FistPoint(["LVHDRT_finding_a_suitable_pair", 

1404 "LVHDRT_inflating_the_parent", 

1405 "LVHDRT_resizing_while_vdis_are_paused", 

1406 "LVHDRT_coalescing_VHD_data", 

1407 "LVHDRT_coalescing_before_inflate_grandparent", 

1408 "LVHDRT_relinking_grandchildren", 

1409 "LVHDRT_before_create_relink_journal", 

1410 "LVHDRT_xapiSM_serialization_tests", 

1411 "LVHDRT_clone_vdi_after_create_journal", 

1412 "LVHDRT_clone_vdi_after_shrink_parent", 

1413 "LVHDRT_clone_vdi_after_first_snap", 

1414 "LVHDRT_clone_vdi_after_second_snap", 

1415 "LVHDRT_clone_vdi_after_parent_hidden", 

1416 "LVHDRT_clone_vdi_after_parent_ro", 

1417 "LVHDRT_clone_vdi_before_remove_journal", 

1418 "LVHDRT_clone_vdi_after_lvcreate", 

1419 "LVHDRT_clone_vdi_before_undo_clone", 

1420 "LVHDRT_clone_vdi_after_undo_clone", 

1421 "LVHDRT_inflate_after_create_journal", 

1422 "LVHDRT_inflate_after_setSize", 

1423 "LVHDRT_inflate_after_zeroOut", 

1424 "LVHDRT_inflate_after_setSizePhys", 

1425 "LVHDRT_inflate_after_setSizePhys", 

1426 "LVHDRT_coaleaf_before_coalesce", 

1427 "LVHDRT_coaleaf_after_coalesce", 

1428 "LVHDRT_coaleaf_one_renamed", 

1429 "LVHDRT_coaleaf_both_renamed", 

1430 "LVHDRT_coaleaf_after_vdirec", 

1431 "LVHDRT_coaleaf_before_delete", 

1432 "LVHDRT_coaleaf_after_delete", 

1433 "LVHDRT_coaleaf_before_remove_j", 

1434 "LVHDRT_coaleaf_undo_after_rename", 

1435 "LVHDRT_coaleaf_undo_after_rename2", 

1436 "LVHDRT_coaleaf_undo_after_refcount", 

1437 "LVHDRT_coaleaf_undo_after_deflate", 

1438 "LVHDRT_coaleaf_undo_end", 

1439 "LVHDRT_coaleaf_stop_after_recovery", 

1440 "LVHDRT_coaleaf_finish_after_inflate", 

1441 "LVHDRT_coaleaf_finish_end", 

1442 "LVHDRT_coaleaf_delay_1", 

1443 "LVHDRT_coaleaf_delay_2", 

1444 "LVHDRT_coaleaf_delay_3", 

1445 "testsm_clone_allow_raw", 

1446 "xenrt_default_vdi_type_legacy", 

1447 "blktap_activate_inject_failure", 

1448 "blktap_activate_error_handling", 

1449 GCPAUSE_FISTPOINT, 

1450 "cleanup_coalesceVHD_inject_failure", 

1451 "cleanup_tracker_no_progress", 

1452 "FileSR_fail_hardlink", 

1453 "FileSR_fail_snap1", 

1454 "FileSR_fail_snap2", 

1455 "LVM_journaler_exists", 

1456 "LVM_journaler_none", 

1457 "LVM_journaler_badname", 

1458 "LVM_journaler_readfail", 

1459 "LVM_journaler_writefail"]) 

1460 

1461 

1462def set_dirty(session, sr): 

1463 try: 

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

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

1466 except: 

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

1468 

1469 

1470def doesFileHaveOpenHandles(fileName): 

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

1472 (retVal, processAndPidTuples) = \ 

1473 findRunningProcessOrOpenFile(fileName, False) 

1474 

1475 if not retVal: 

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

1477 fileName) 

1478 # err on the side of caution 

1479 return True 

1480 else: 

1481 if len(processAndPidTuples) > 0: 

1482 return True 

1483 else: 

1484 return False 

1485 

1486 

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

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

1489def extractSRFromDevMapper(path): 

1490 try: 

1491 path = os.path.basename(path) 

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

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

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

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

1496 except: 

1497 return '' 

1498 

1499 

1500def pid_is_alive(pid): 

1501 """ 

1502 Try to kill PID with signal 0. 

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

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

1505 signal it. Still return true. 

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

1507 """ 

1508 try: 

1509 os.kill(pid, 0) 

1510 return True 

1511 except OSError as e: 

1512 if e.errno == errno.EPERM: 

1513 return True 

1514 return False 

1515 

1516 

1517# Looks at /proc and figures either 

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

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

1520# returns process names and pids 

1521def findRunningProcessOrOpenFile(name, process=True): 

1522 retVal = True 

1523 links = [] 

1524 processandpids = [] 

1525 sockets = set() 

1526 try: 

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

1528 [name, process]) 

1529 

1530 # Look at all pids 

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

1532 for pid in sorted(pids): 

1533 try: 

1534 try: 

1535 f = None 

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

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

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

1539 # Just want the process name 

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

1541 prog = argv[0] 

1542 except IOError as e: 

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

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

1545 continue 

1546 finally: 

1547 if f is not None: 1547 ↛ 1532,   1547 ↛ 15502 missed branches: 1) line 1547 didn't jump to line 1532, because the continue on line 1545 wasn't executed, 2) line 1547 didn't jump to line 1550, because the condition on line 1547 was never false

1548 f.close() 1548 ↛ 1532line 1548 didn't jump to line 1532, because the continue on line 1545 wasn't executed

1549 

1550 try: 

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

1552 files = os.listdir(fd_dir) 

1553 except OSError as e: 

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

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

1556 # Ignore pid that are no longer valid 

1557 continue 

1558 else: 

1559 raise 

1560 

1561 for file in files: 

1562 try: 

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

1564 except OSError: 

1565 continue 

1566 

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

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

1569 links.append(link) 

1570 else: 

1571 # need to return process name and pid tuples 

1572 if link == name: 

1573 processandpids.append((prog, pid)) 

1574 

1575 # Get the connected sockets 

1576 if name == prog: 

1577 sockets.update(get_connected_sockets(pid)) 

1578 

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

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

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

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

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

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

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

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

1587 

1588 except Exception as e: 

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

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

1591 retVal = False 

1592 

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

1594 return retVal, links, sockets 

1595 else: 

1596 return retVal, processandpids 

1597 

1598 

1599def get_connected_sockets(pid): 

1600 sockets = set() 

1601 try: 

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

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

1604 # - Pointer address to socket (hex) 

1605 # - Refcount (HEX) 

1606 # - 0 

1607 # - State (HEX, 0 or __SO_ACCEPTCON) 

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

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

1610 # - Inode number 

1611 # - Path (optional) 

1612 open_sock_matcher = re.compile( 

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

1614 with open( 

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

1616 lines = f.readlines() 

1617 for line in lines: 

1618 match = open_sock_matcher.match(line) 

1619 if match: 

1620 sockets.add(match[1]) 

1621 except OSError as e: 

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

1623 # Ignore pid that are no longer valid 

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

1625 (e.errno, pid)) 

1626 else: 

1627 raise 

1628 return sockets 

1629 

1630 

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

1632 retries = 0 

1633 while True: 

1634 try: 

1635 return f() 

1636 except Exception as e: 

1637 for exception in exceptions: 

1638 if isinstance(e, exception): 

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

1640 str(e), retries 

1641 )) 

1642 break 

1643 else: 

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

1645 raise e 

1646 

1647 retries += 1 

1648 if retries >= maxretry: 

1649 break 

1650 

1651 time.sleep(period) 

1652 

1653 return f() 

1654 

1655 

1656def getCslDevPath(svid): 

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

1658 if svid.startswith("NETAPP_"): 

1659 # special attention for NETAPP SVIDs 

1660 svid_parts = svid.split("__") 

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

1662 else: 

1663 globstr = basepath + svid + "*" 

1664 

1665 return globstr 

1666 

1667 

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

1669def get_scsiid_from_svid(md_svid): 

1670 cslg_path = getCslDevPath(md_svid) 

1671 abs_path = glob.glob(cslg_path) 

1672 if abs_path: 

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

1674 return scsiutil.getSCSIid(real_path) 

1675 else: 

1676 return None 

1677 

1678 

1679def get_isl_scsiids(session): 

1680 # Get cslg type SRs 

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

1682 

1683 # Iterate through the SR to get the scsi ids 

1684 scsi_id_ret = [] 

1685 for SR in SRs: 

1686 sr_rec = SRs[SR] 

1687 # Use the md_svid to get the scsi id 

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

1689 if scsi_id: 

1690 scsi_id_ret.append(scsi_id) 

1691 

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

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

1694 for vdi_rec in vdi_recs: 

1695 vdi_rec = vdi_recs[vdi_rec] 

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

1697 if scsi_id: 

1698 scsi_id_ret.append(scsi_id) 

1699 

1700 return scsi_id_ret 

1701 

1702 

1703class extractXVA: 

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

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

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

1707 HDR_SIZE = 512 

1708 BLOCK_SIZE = 512 

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

1710 SIZE_OFFSET = 124 

1711 ZERO_FILLED_REC = 2 

1712 NULL_IDEN = '\x00' 

1713 DIR_IDEN = '/' 

1714 CHECKSUM_IDEN = '.checksum' 

1715 OVA_FILE = 'ova.xml' 

1716 

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

1718 # as and when needed 

1719 def __init__(self, filename): 

1720 self.__extract_path = '' 

1721 self.__filename = filename 

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

1723 try: 

1724 self.spawn_p = subprocess.Popen( 

1725 cmd, shell=True, \ 

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

1727 stderr=subprocess.PIPE, close_fds=True) 

1728 except Exception as e: 

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

1730 raise Exception(str(e)) 

1731 

1732 # Create dir to extract the files 

1733 self.__extract_path = tempfile.mkdtemp() 

1734 

1735 def __del__(self): 

1736 shutil.rmtree(self.__extract_path) 

1737 

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

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

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

1741 def getTuple(self): 

1742 zerod_record = 0 

1743 ret_f_name = '' 

1744 ret_base_f_name = '' 

1745 

1746 try: 

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

1748 while True: 

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

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

1751 

1752 # Break out in case of end of file 

1753 if f_hdr == '': 

1754 if zerod_record == extractXVA.ZERO_FILLED_REC: 

1755 break 

1756 else: 

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

1758 extractXVA.ZERO_FILLED_REC) 

1759 raise Exception('Unrecognized end of file') 

1760 

1761 # Watch out for zero records, two zero records 

1762 # denote end of file. 

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

1764 zerod_record += 1 

1765 continue 

1766 

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

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

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

1770 f_size_octal = f_hdr[extractXVA.SIZE_OFFSET: \ 

1771 extractXVA.SIZE_OFFSET + extractXVA.SIZE_LEN] 

1772 f_size = int(f_size_octal, 8) 

1773 if f_name.endswith(extractXVA.CHECKSUM_IDEN): 

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

1775 ret_base_f_name: 

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

1777 yield(ret_f_name, checksum) 

1778 else: 

1779 # Expects file followed by its checksum 

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

1781 ret_f_name) 

1782 raise Exception( \ 

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

1784 ret_f_name) 

1785 else: 

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

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

1788 # read the contents into a file, it will 

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

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

1791 ret_base_f_name = f_name 

1792 

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

1794 # else create it. 

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

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

1797 os.mkdir(folder_path) 

1798 

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

1800 f = open(ret_f_name, 'w') 

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

1802 f.close() 

1803 if f_name == extractXVA.OVA_FILE: 

1804 yield(ret_f_name, '') 

1805 

1806 # Skip zero'd portion of data block 

1807 round_off = f_size % extractXVA.BLOCK_SIZE 

1808 if round_off != 0: 

1809 zeros = self.spawn_p.stdout.read( 

1810 extractXVA.BLOCK_SIZE - round_off) 

1811 except Exception as e: 

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

1813 self.__filename)) 

1814 

1815 # Kill and Drain stdout of the gunzip process, 

1816 # else gunzip might block on stdout 

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

1818 self.spawn_p.communicate() 

1819 raise Exception(str(e)) 

1820 

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

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

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

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

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

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

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

1828 (0x10FFFE, 0x10FFFF)] 

1829 

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

1831 for (low, high) in illegal_xml_chars 

1832 if low < sys.maxunicode] 

1833 

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

1835 

1836 

1837def isLegalXMLString(s): 

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

1839 illegal XML characters specified in 

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

1841 """ 

1842 

1843 if len(s) > 0: 

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

1845 else: 

1846 return True 

1847 

1848 

1849def unictrunc(string, max_bytes): 

1850 """ 

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

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

1853 more than the given number of bytes. 

1854 

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

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

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

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

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

1860 

1861 string: the string to truncate 

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

1863 """ 

1864 if isinstance(string, str): 

1865 return_chars = True 

1866 else: 

1867 return_chars = False 

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

1869 

1870 cur_chars = 0 

1871 cur_bytes = 0 

1872 for char in string: 

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

1874 if cur_bytes + charsize > max_bytes: 

1875 break 

1876 else: 

1877 cur_chars += 1 

1878 cur_bytes += charsize 

1879 return cur_chars if return_chars else cur_bytes 

1880 

1881 

1882def hideValuesInPropMap(propmap, propnames): 

1883 """ 

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

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

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

1887 value and return the altered map. 

1888 If none found, return the original map 

1889 """ 

1890 matches = [] 

1891 for propname in propnames: 

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

1893 matches.append(propname) 

1894 

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

1896 deepCopyRec = copy.deepcopy(propmap) 

1897 for match in matches: 

1898 deepCopyRec[match] = '******' 

1899 return deepCopyRec 

1900 

1901 return propmap 

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

1903 

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

1905DEFAULT_SEGMENT_LEN = 950 

1906 

1907 

1908def hidePasswdInConfig(config): 

1909 """ 

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

1911 for example "device_config" 

1912 """ 

1913 return hideValuesInPropMap(config, PASSWD_PROP_KEYS) 

1914 

1915 

1916def hidePasswdInParams(params, configProp): 

1917 """ 

1918 Function to hide password values in a specified property which 

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

1920 in a larger property map. 

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

1922 "sm_config", etc 

1923 """ 

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

1925 return params 

1926 

1927 

1928def hideMemberValuesInXmlParams(xmlParams, propnames=PASSWD_PROP_KEYS): 

1929 """ 

1930 Function to hide password values in XML params, specifically 

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

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

1933 whose values we want to hide, and: 

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

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

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

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

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

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

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

1941 """ 

1942 findStrPrefixHead = "<member><name>" 

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

1944 findStrSuffix = "</value>" 

1945 strlen = len(xmlParams) 

1946 

1947 for propname in propnames: 

1948 findStrPrefix = findStrPrefixHead + propname + findStrPrefixTail 

1949 idx = xmlParams.find(findStrPrefix) 

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

1951 idx += len(findStrPrefix) 

1952 idx2 = xmlParams.find(findStrSuffix, idx) 

1953 if idx2 != -1: 

1954 retStr = xmlParams[0:idx] 

1955 retStr += "******" 

1956 retStr += xmlParams[idx2:strlen] 

1957 return retStr 

1958 else: 

1959 return xmlParams 

1960 return xmlParams 

1961 

1962 

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

1964 """ 

1965 Split xml string data into substrings small enough for the 

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

1967 Usage: 

1968 strList = [] 

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

1970 """ 

1971 remainingData = str(xmlData) 

1972 

1973 # "Un-pretty-print" 

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

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

1976 

1977 remainingChars = len(remainingData) 

1978 returnData = '' 

1979 

1980 thisLineNum = 0 

1981 while remainingChars > segmentLen: 

1982 thisLineNum = thisLineNum + 1 

1983 index = segmentLen 

1984 tmpStr = remainingData[:segmentLen] 

1985 tmpIndex = tmpStr.rfind('>') 

1986 if tmpIndex != -1: 

1987 index = tmpIndex + 1 

1988 

1989 tmpStr = tmpStr[:index] 

1990 remainingData = remainingData[index:] 

1991 remainingChars = len(remainingData) 

1992 

1993 if showContd: 

1994 if thisLineNum != 1: 

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

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

1997 

1998 returnData += tmpStr + '\n' 

1999 

2000 if showContd and thisLineNum > 0: 

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

2002 returnData += remainingData 

2003 

2004 return returnData 

2005 

2006 

2007def inject_failure(): 

2008 raise Exception('injected failure') 

2009 

2010 

2011def open_atomic(path, mode=None): 

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

2013 Leaves the file open and returns the file object. 

2014 

2015 path: the path to atomically open 

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

2017 returns: an open file object""" 

2018 

2019 assert path 

2020 

2021 flags = os.O_CREAT | os.O_EXCL 

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

2023 if mode: 

2024 if mode not in modes: 

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

2026 flags |= modes[mode] 

2027 fd = os.open(path, flags) 

2028 try: 

2029 if mode: 

2030 return os.fdopen(fd, mode) 

2031 else: 

2032 return os.fdopen(fd) 

2033 except: 

2034 os.close(fd) 

2035 raise 

2036 

2037 

2038def isInvalidVDI(exception): 

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

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

2041 

2042 

2043def get_pool_restrictions(session): 

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

2045 established.""" 

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

2047 

2048 

2049def read_caching_is_restricted(session): 

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

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

2052 return True 

2053 restrictions = get_pool_restrictions(session) 

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

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

2056 return True 

2057 return False 

2058 

2059 

2060def sessions_less_than_targets(other_config, device_config): 

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

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

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

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

2065 return (sessions < targets) 

2066 else: 

2067 return False 

2068 

2069 

2070def enable_and_start_service(name, start): 

2071 attempt = 0 

2072 while True: 

2073 attempt += 1 

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

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

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

2077 if ret == 0: 

2078 return 

2079 elif attempt >= 3: 

2080 raise Exception( 

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

2082 ) 

2083 time.sleep(1) 

2084 

2085 

2086def stop_service(name): 

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

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

2089 if ret == 0: 

2090 return 

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

2092 

2093 

2094def restart_service(name): 

2095 attempt = 0 

2096 while True: 

2097 attempt += 1 

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

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

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

2101 if ret == 0: 

2102 return 

2103 elif attempt >= 3: 

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

2105 raise Exception( 

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

2107 ) 

2108 time.sleep(1) 

2109 

2110 

2111def check_pid_exists(pid): 

2112 try: 

2113 os.kill(pid, 0) 

2114 except OSError: 

2115 return False 

2116 else: 

2117 return True 

2118 

2119 

2120def get_openers_pid(path: str) -> Optional[List[int]]: 

2121 cmd = ["lsof", "-t", path] 

2122 

2123 try: 

2124 list = [] 

2125 ret = pread2(cmd) 

2126 for line in ret.splitlines(): 

2127 list.append(int(line)) 

2128 return list 

2129 except CommandException as e: 

2130 if e.code == 1: # `lsof` return 1 if there is no openers 

2131 return None 

2132 else: 

2133 raise e 

2134 

2135 

2136def make_profile(name, function): 

2137 """ 

2138 Helper to execute cProfile using unique log file. 

2139 """ 

2140 

2141 import cProfile 

2142 import itertools 

2143 import os.path 

2144 import time 

2145 

2146 assert name 

2147 assert function 

2148 

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

2150 makedirs(FOLDER) 

2151 

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

2153 

2154 def gen_path(path): 

2155 yield path 

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

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

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

2159 

2160 for profile_path in gen_path(FOLDER + filename): 

2161 try: 

2162 file = open_atomic(profile_path, 'w') 

2163 file.close() 

2164 break 

2165 except OSError as e: 

2166 if e.errno == errno.EEXIST: 

2167 pass 

2168 else: 

2169 raise 

2170 

2171 try: 

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

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

2174 finally: 

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

2176 

2177 

2178def strtobool(str: str) -> bool: 

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

2180 # There is not alternative for strtobool. 

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

2182 # So this is a custom implementation with differences: 

2183 # - A boolean is returned instead of integer 

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

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

2186 return False 

2187 str = str.lower() 

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

2189 return True 

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

2191 return False 

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

2193 

2194 

2195def find_executable(name): 

2196 return shutil.which(name) 

2197 

2198 

2199def conditional_decorator(decorator, condition): 

2200 def wrapper(func): 

2201 if not condition: 

2202 return func 

2203 return decorator(func) 

2204 return wrapper