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" 

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

66 

67FIST_PAUSE_PERIOD = 30 # seconds 

68 

69 

70class SMException(Exception): 

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

72 XenError""" 

73 

74 

75class CommandException(SMException): 

76 def error_message(self, code): 

77 if code > 0: 

78 return os.strerror(code) 

79 elif code < 0: 

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

81 return "Success" 

82 

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

84 self.code = code 

85 self.cmd = cmd 

86 self.reason = reason 

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

88 

89 

90class SRBusyException(SMException): 

91 """The SR could not be locked""" 

92 pass 

93 

94 

95def logException(tag): 

96 info = sys.exc_info() 

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

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

99 sys.exit(0) 

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

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

102 SMlog(str) 

103 

104 

105def roundup(divisor, value): 

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

107 

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

109 value = 1 

110 if value % divisor != 0: 

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

112 return value 

113 

114 

115def to_plain_string(obj): 

116 if obj is None: 

117 return None 

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

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

120 return "" 

121 return str(obj) 

122 

123 

124def shellquote(arg): 

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

126 

127 

128def make_WWN(name): 

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

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

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

132 # inject dashes for each nibble 

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

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

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

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

137 return name 

138 

139 

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

141 syslog.openlog(ident, 0, facility) 

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

143 syslog.closelog() 

144 

145 

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

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

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

149 _logToSyslog(ident, _SM_SYSLOG_FACILITY, priority, message_line) 

150 

151 

152def _getDateString(): 

153 d = datetime.datetime.now() 

154 t = d.timetuple() 

155 return "%s-%s-%s:%s:%s:%s" % \ 

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

157 

158 

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

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

161 env = None 

162 if new_env: 

163 env = dict(os.environ) 

164 env.update(new_env) 

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

166 stdout=subprocess.PIPE, 

167 stderr=subprocess.PIPE, 

168 close_fds=True, env=env, 

169 universal_newlines=text) 

170 

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

172 inputtext = inputtext.encode() 

173 

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

175 

176 rc = proc.returncode 

177 return rc, stdout, stderr 

178 

179 

180def is_string(value): 

181 return isinstance(value, str) 

182 

183 

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

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

186# replace the original ones at some later date. 

187# 

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

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

190# written to the logs. 

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

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

193 cmdlist_for_exec = [] 

194 cmdlist_for_log = [] 

195 for item in cmdlist: 

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

197 cmdlist_for_exec.append(item) 

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

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

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

201 else: 

202 cmdlist_for_log.append(item) 

203 else: 

204 cmdlist_for_log.append(item) 

205 else: 

206 cmdlist_for_exec.append(item[0]) 

207 cmdlist_for_log.append(item[1]) 

208 

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

210 SMlog(cmdlist_for_log) 

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

212 if rc != expect_rc: 

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

214 (rc, stdout, stderr)) 

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

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

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

218 stderr = stdout 

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

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

221 SMlog(" pread SUCCESS") 

222 return stdout 

223 

224 

225# POSIX guaranteed atomic within the same file system. 

226# Supply directory to ensure tempfile is created 

227# in the same directory. 

228def atomicFileWrite(targetFile, directory, text): 

229 

230 file = None 

231 try: 

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

233 # our responsibility to clean it up. 

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

235 file = open(tempPath, 'w') 

236 file.write(text) 

237 

238 # Ensure flushed to disk. 

239 file.flush() 

240 os.fsync(file.fileno()) 

241 file.close() 

242 

243 os.rename(tempPath, targetFile) 

244 except OSError: 

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

246 

247 finally: 

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

249 file.close() 

250 

251 if os.path.isfile(tempPath): 

252 os.remove(tempPath) 

253 

254 

255#Read STDOUT from cmdlist and discard STDERR output 

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

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

258 

259 

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

261def pread3(cmdlist, text): 

262 SMlog(cmdlist) 

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

264 if rc: 

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

266 (rc, stdout, stderr)) 

267 if '' == stderr: 

268 stderr = stdout 

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

270 SMlog(" pread3 SUCCESS") 

271 return stdout 

272 

273 

274def listdir(path, quiet=False): 

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

276 try: 

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

278 if len(text) == 0: 

279 return [] 

280 return text.split('\n') 

281 except CommandException as inst: 

282 if inst.code == errno.ENOENT: 

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

284 else: 

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

286 

287 

288def gen_uuid(): 

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

290 return pread(cmd)[:-1] 

291 

292 

293def match_uuid(s): 

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

295 return regex.search(s, 0) 

296 

297 

298def findall_uuid(s): 

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

300 return regex.findall(s, 0) 

301 

302 

303def exactmatch_uuid(s): 

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

305 return regex.search(s, 0) 

306 

307 

308def start_log_entry(srpath, path, args): 

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

310 logstring += " log: " 

311 logstring += srpath 

312 logstring += " " + path 

313 for element in args: 

314 logstring += " " + element 

315 try: 

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

317 file.write(logstring) 

318 file.write("\n") 

319 file.close() 

320 except: 

321 pass 

322 

323 # failed to write log ... 

324 

325def end_log_entry(srpath, path, args): 

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

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

328 logstring += " end: " 

329 logstring += srpath 

330 logstring += " " + path 

331 for element in args: 

332 logstring += " " + element 

333 try: 

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

335 file.write(logstring) 

336 file.write("\n") 

337 file.close() 

338 except: 

339 pass 

340 

341 # failed to write log ... 

342 # for now print 

343 # print "%s" % logstring 

344 

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

346 retries = 0 

347 while True: 

348 try: 

349 return f() 

350 except OSError as ose: 

351 err = int(ose.errno) 

352 if not err in errlist: 

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

354 except CommandException as ce: 

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

356 raise 

357 

358 retries += 1 

359 if retries >= maxretry: 

360 break 

361 

362 time.sleep(period) 

363 

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

365 

366 

367def ioretry_stat(path, maxretry=IORETRY_MAX): 

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

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

370 retries = 0 

371 while retries < maxretry: 

372 stat = os.statvfs(path) 

373 if stat.f_blocks != -1: 

374 return stat 

375 time.sleep(1) 

376 retries += 1 

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

378 

379 

380def sr_get_capability(sr_uuid, session=None): 

381 result = [] 

382 local_session = None 

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

384 local_session = get_localAPI_session() 

385 session = local_session 

386 

387 try: 

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

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

390 sm_rec = session.xenapi.SM.get_all_records_where( 

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

392 

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

394 if len(sm_rec) > 0: 

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

396 

397 return result 

398 finally: 

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

400 local_session.xenapi.session.logout() 

401 

402def sr_get_driver_info(driver_info): 

403 results = {} 

404 # first add in the vanilla stuff 

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

406 'driver_version', 'required_api_version']: 

407 results[key] = driver_info[key] 

408 # add the capabilities (xmlrpc array) 

409 # enforcing activate/deactivate for blktap2 

410 caps = driver_info['capabilities'] 

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

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

413 if not cap in caps: 

414 caps.append(cap) 

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

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

417 

418 results['capabilities'] = caps 

419 # add in the configuration options 

420 options = [] 

421 for option in driver_info['configuration']: 

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

423 results['configuration'] = options 

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

425 

426 

427def return_nil(): 

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

429 

430 

431def SRtoXML(SRlist): 

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

433 driver = dom.createElement("SRlist") 

434 dom.appendChild(driver) 

435 

436 for key in SRlist.keys(): 

437 dict = SRlist[key] 

438 entry = dom.createElement("SR") 

439 driver.appendChild(entry) 

440 

441 e = dom.createElement("UUID") 

442 entry.appendChild(e) 

443 textnode = dom.createTextNode(key) 

444 e.appendChild(textnode) 

445 

446 if 'size' in dict: 

447 e = dom.createElement("Size") 

448 entry.appendChild(e) 

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

450 e.appendChild(textnode) 

451 

452 if 'storagepool' in dict: 

453 e = dom.createElement("StoragePool") 

454 entry.appendChild(e) 

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

456 e.appendChild(textnode) 

457 

458 if 'aggregate' in dict: 

459 e = dom.createElement("Aggregate") 

460 entry.appendChild(e) 

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

462 e.appendChild(textnode) 

463 

464 return dom.toprettyxml() 

465 

466 

467def pathexists(path): 

468 try: 

469 os.lstat(path) 

470 return True 

471 except OSError as inst: 

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

473 time.sleep(1) 

474 try: 

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

476 os.lstat(path) 

477 return True 

478 except: 

479 pass 

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

481 return False 

482 

483 

484def force_unlink(path): 

485 try: 

486 os.unlink(path) 

487 except OSError as e: 

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

489 raise 

490 

491 

492def create_secret(session, secret): 

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

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

495 

496 

497def get_secret(session, uuid): 

498 try: 

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

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

501 except: 

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

503 

504 

505def get_real_path(path): 

506 "Follow symlinks to the actual file" 

507 absPath = path 

508 directory = '' 

509 while os.path.islink(absPath): 

510 directory = os.path.dirname(absPath) 

511 absPath = os.readlink(absPath) 

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

513 return absPath 

514 

515 

516def wait_for_path(path, timeout): 

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

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

519 return True 

520 time.sleep(1) 

521 return False 

522 

523 

524def wait_for_nopath(path, timeout): 

525 for i in range(0, timeout): 

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

527 return True 

528 time.sleep(1) 

529 return False 

530 

531 

532def wait_for_path_multi(path, timeout): 

533 for i in range(0, timeout): 

534 paths = glob.glob(path) 

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

536 if len(paths): 

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

538 return paths[0] 

539 time.sleep(1) 

540 return "" 

541 

542 

543def isdir(path): 

544 try: 

545 st = os.stat(path) 

546 return stat.S_ISDIR(st.st_mode) 

547 except OSError as inst: 

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

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

550 return False 

551 

552 

553def get_single_entry(path): 

554 f = open(path, 'r') 

555 line = f.readline() 

556 f.close() 

557 return line.rstrip() 

558 

559 

560def get_fs_size(path): 

561 st = ioretry_stat(path) 

562 return st.f_blocks * st.f_frsize 

563 

564 

565def get_fs_utilisation(path): 

566 st = ioretry_stat(path) 

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

568 st.f_frsize 

569 

570 

571def ismount(path): 

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

573 try: 

574 s1 = os.stat(path) 

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

576 except OSError as inst: 

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

578 dev1 = s1.st_dev 

579 dev2 = s2.st_dev 

580 if dev1 != dev2: 

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

582 ino1 = s1.st_ino 

583 ino2 = s2.st_ino 

584 if ino1 == ino2: 

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

586 return False 

587 

588 

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

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

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

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

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

594 makedirs(head, mode) 

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

596 return 

597 try: 

598 os.mkdir(name, mode) 

599 except OSError as exc: 

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

601 if mode: 

602 os.chmod(name, mode) 

603 pass 

604 else: 

605 raise 

606 

607 

608def zeroOut(path, fromByte, bytes): 

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

610 blockSize = 4096 

611 

612 fromBlock = fromByte // blockSize 

613 if fromByte % blockSize: 

614 fromBlock += 1 

615 bytesBefore = fromBlock * blockSize - fromByte 

616 if bytesBefore > bytes: 

617 bytesBefore = bytes 

618 bytes -= bytesBefore 

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

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

621 try: 

622 pread2(cmd) 

623 except CommandException: 

624 return False 

625 

626 blocks = bytes // blockSize 

627 bytes -= blocks * blockSize 

628 fromByte = (fromBlock + blocks) * blockSize 

629 if blocks: 

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

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

632 try: 

633 pread2(cmd) 

634 except CommandException: 

635 return False 

636 

637 if bytes: 

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

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

640 try: 

641 pread2(cmd) 

642 except CommandException: 

643 return False 

644 

645 return True 

646 

647 

648def wipefs(blockdev): 

649 "Wipe filesystem signatures from `blockdev`" 

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

651 

652 

653def match_rootdev(s): 

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

655 return regex.search(s, 0) 

656 

657 

658def getrootdev(): 

659 filename = '/etc/xensource-inventory' 

660 try: 

661 f = open(filename, 'r') 

662 except: 

663 raise xs_errors.XenError('EIO', \ 

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

665 rootdev = '' 

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

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

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

669 raise xs_errors.XenError('NoRootDev') 

670 return rootdev 

671 

672 

673def getrootdevID(): 

674 rootdev = getrootdev() 

675 try: 

676 rootdevID = scsiutil.getSCSIid(rootdev) 

677 except: 

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

679 % rootdev) 

680 return '' 

681 

682 if not len(rootdevID): 

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

684 % rootdev) 

685 

686 return rootdevID 

687 

688 

689def get_localAPI_session(): 

690 # First acquire a valid session 

691 session = XenAPI.xapi_local() 

692 try: 

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

694 except: 

695 raise xs_errors.XenError('APISession') 

696 return session 

697 

698 

699def get_this_host(): 

700 uuid = None 

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

702 for line in f.readlines(): 

703 if line.startswith("INSTALLATION_UUID"): 

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

705 f.close() 

706 return uuid 

707 

708 

709def get_master_ref(session): 

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

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

712 

713 

714def is_master(session): 

715 return get_this_host_ref(session) == get_master_ref(session) 

716 

717 

718def get_localhost_ref(session): 

719 filename = '/etc/xensource-inventory' 

720 try: 

721 f = open(filename, 'r') 

722 except: 

723 raise xs_errors.XenError('EIO', \ 

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

725 domid = '' 

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

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

728 if not domid: 

729 raise xs_errors.XenError('APILocalhost') 

730 

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

732 for vm in vms: 

733 record = vms[vm] 

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

735 hostid = record["resident_on"] 

736 return hostid 

737 raise xs_errors.XenError('APILocalhost') 

738 

739 

740def match_domain_id(s): 

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

742 return regex.search(s, 0) 

743 

744 

745def get_hosts_attached_on(session, vdi_uuids): 

746 host_refs = {} 

747 for vdi_uuid in vdi_uuids: 

748 try: 

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

750 except XenAPI.Failure: 

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

752 continue 

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

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

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

756 return host_refs.keys() 

757 

758def get_this_host_address(session): 

759 host_uuid = get_this_host() 

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

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

762 

763def get_host_addresses(session): 

764 addresses = [] 

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

766 for record in hosts.values(): 

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

768 return addresses 

769 

770def get_this_host_ref(session): 

771 host_uuid = get_this_host() 

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

773 return host_ref 

774 

775 

776def get_slaves_attached_on(session, vdi_uuids): 

777 "assume this host is the SR master" 

778 host_refs = get_hosts_attached_on(session, vdi_uuids) 

779 master_ref = get_this_host_ref(session) 

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

781 

782def get_enabled_hosts(session): 

783 """ 

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

785 """ 

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

787 

788def get_online_hosts(session): 

789 online_hosts = [] 

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

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

792 metricsRef = host_rec["metrics"] 

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

794 if metrics["live"]: 

795 online_hosts.append(host_ref) 

796 return online_hosts 

797 

798 

799def get_all_slaves(session): 

800 "assume this host is the SR master" 

801 host_refs = get_online_hosts(session) 

802 master_ref = get_this_host_ref(session) 

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

804 

805 

806def is_attached_rw(sm_config): 

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

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

809 return True 

810 return False 

811 

812 

813def attached_as(sm_config): 

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

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

816 return val 

817 

818 

819def find_my_pbd_record(session, host_ref, sr_ref): 

820 try: 

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

822 for pbd_ref in pbds.keys(): 

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

824 return [pbd_ref, pbds[pbd_ref]] 

825 return None 

826 except Exception as e: 

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

828 return None 

829 

830 

831def find_my_pbd(session, host_ref, sr_ref): 

832 ret = find_my_pbd_record(session, host_ref, sr_ref) 

833 if ret is not None: 

834 return ret[0] 

835 else: 

836 return None 

837 

838 

839def test_hostPBD_devs(session, sr_uuid, devs): 

840 host = get_localhost_ref(session) 

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

842 try: 

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

844 except: 

845 raise xs_errors.XenError('APIPBDQuery') 

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

847 for pbd in pbds: 

848 record = pbds[pbd] 

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

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

851 break 

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

853 devconfig = record["device_config"] 

854 if 'device' in devconfig: 

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

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

857 return True 

858 return False 

859 

860 

861def test_hostPBD_lun(session, targetIQN, LUNid): 

862 host = get_localhost_ref(session) 

863 try: 

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

865 except: 

866 raise xs_errors.XenError('APIPBDQuery') 

867 for pbd in pbds: 

868 record = pbds[pbd] 

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

870 devconfig = record["device_config"] 

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

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

873 devconfig['LUNid'] == LUNid: 

874 return True 

875 return False 

876 

877 

878def test_SCSIid(session, sr_uuid, SCSIid): 

879 if sr_uuid is not None: 

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

881 try: 

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

883 except: 

884 raise xs_errors.XenError('APIPBDQuery') 

885 for pbd in pbds: 

886 record = pbds[pbd] 

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

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

889 if sr_uuid is not None: 

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

891 break 

892 devconfig = record["device_config"] 

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

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

895 return True 

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

897 return True 

898 elif 'scsi-' + SCSIid in sm_config: 

899 return True 

900 return False 

901 

902 

903class TimeoutException(SMException): 

904 pass 

905 

906 

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

908 def handler(signum, frame): 

909 raise TimeoutException() 

910 signal.signal(signal.SIGALRM, handler) 

911 signal.alarm(timeoutseconds) 

912 try: 

913 return function(*arguments) 

914 finally: 

915 signal.alarm(0) 

916 

917 

918def _incr_iscsiSR_refcount(targetIQN, uuid): 

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

920 os.mkdir(ISCSI_REFDIR) 

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

922 try: 

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

924 except: 

925 raise xs_errors.XenError('LVMRefCount', \ 

926 opterr='file %s' % filename) 

927 

928 f.seek(0) 

929 found = False 

930 refcount = 0 

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

932 refcount += 1 

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

934 found = True 

935 if not found: 

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

937 refcount += 1 

938 f.close() 

939 return refcount 

940 

941 

942def _decr_iscsiSR_refcount(targetIQN, uuid): 

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

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

945 return 0 

946 try: 

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

948 except: 

949 raise xs_errors.XenError('LVMRefCount', \ 

950 opterr='file %s' % filename) 

951 

952 f.seek(0) 

953 output = [] 

954 refcount = 0 

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

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

957 output.append(line.rstrip()) 

958 refcount += 1 

959 if not refcount: 

960 os.unlink(filename) 

961 return refcount 

962 

963 # Re-open file and truncate 

964 f.close() 

965 f = open(filename, 'w') 

966 for i in range(0, refcount): 

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

968 f.close() 

969 return refcount 

970 

971 

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

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

974def test_activePoolPBDs(session, host, uuid): 

975 try: 

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

977 except: 

978 raise xs_errors.XenError('APIPBDQuery') 

979 for pbd in pbds: 

980 record = pbds[pbd] 

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

982 and record["currently_attached"]: 

983 return True 

984 return False 

985 

986 

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

988 try: 

989 pbdref = find_my_pbd(session, host_ref, sr_ref) 

990 if pbdref is not None: 

991 key = "mpath-" + SCSIid 

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

993 except: 

994 pass 

995 

996 

997def kickpipe_mpathcount(): 

998 """ 

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

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

1001 by a UDEV event. 

1002 """ 

1003 cmd = [CMD_KICKPIPE, "mpathcount"] 

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

1005 return (rc == 0) 

1006 

1007 

1008def _testHost(hostname, port, errstring): 

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

1010 try: 

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

1012 except: 

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

1014 raise xs_errors.XenError('DNSError') 

1015 

1016 timeout = 5 

1017 

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

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

1020 sock.settimeout(timeout) 

1021 try: 

1022 sock.connect(sockinfo[4]) 

1023 # Fix for MS storage server bug 

1024 sock.send(b'\n') 

1025 sock.close() 

1026 except socket.error as reason: 

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

1028 % (timeout, hostname, reason)) 

1029 raise xs_errors.XenError(errstring) 

1030 

1031 

1032def match_scsiID(s, id): 

1033 regex = re.compile(id) 

1034 return regex.search(s, 0) 

1035 

1036 

1037def _isSCSIid(s): 

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

1039 return regex.search(s, 0) 

1040 

1041 

1042def is_usb_device(device): 

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

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

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

1046 

1047 

1048def test_scsiserial(session, device): 

1049 device = os.path.realpath(device) 

1050 if not scsiutil._isSCSIdev(device): 

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

1052 return False 

1053 serial = "" 

1054 try: 

1055 serial += scsiutil.getserial(device) 

1056 except: 

1057 # Error allowed, SCSIid is the important one 

1058 pass 

1059 

1060 try: 

1061 scsiID = scsiutil.getSCSIid(device) 

1062 except: 

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

1064 % device) 

1065 return False 

1066 if not len(scsiID): 

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

1068 % device) 

1069 return False 

1070 

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

1072 try: 

1073 usb_device_with_serial = serial and is_usb_device(device) 

1074 except: 

1075 usb_device_with_serial = False 

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

1077 SMlog(traceback.format_exc()) 

1078 

1079 try: 

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

1081 except: 

1082 raise xs_errors.XenError('APIFailure') 

1083 for SR in SRs: 

1084 record = SRs[SR] 

1085 conf = record["sm_config"] 

1086 if 'devserial' in conf: 

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

1088 if not usb_device_with_serial and _isSCSIid(dev): 

1089 if match_scsiID(dev, scsiID): 

1090 return True 

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

1092 return True 

1093 return False 

1094 

1095 

1096def default(self, field, thunk): 

1097 try: 

1098 return getattr(self, field) 

1099 except: 

1100 return thunk() 

1101 

1102 

1103def list_VDI_records_in_sr(sr): 

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

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

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

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

1108 return vdis 

1109 

1110 

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

1112def diskFromPartition(partition): 

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

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

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

1116 return m.group(2) 

1117 

1118 numlen = 0 # number of digit characters 

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

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

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

1122 

1123 # is it a cciss? 

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

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

1126 

1127 # is it a mapper path? 

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

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

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

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

1132 else: 

1133 numlen = 0 

1134 

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

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

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

1138 

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

1140 

1141 

1142def dom0_disks(): 

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

1144 disks = [] 

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

1146 for line in f: 

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

1148 if mountpoint == '/': 

1149 disk = diskFromPartition(dev) 

1150 if not (disk in disks): 

1151 disks.append(disk) 

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

1153 return disks 

1154 

1155 

1156def set_scheduler_sysfs_node(node, scheds): 

1157 """ 

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

1159 according to prioritized list schedulers 

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

1161 """ 

1162 

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

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

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

1166 return 

1167 

1168 stored_error = None 

1169 for sched in scheds: 

1170 try: 

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

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

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

1174 return 

1175 except (OSError, IOError) as err: 

1176 stored_error = err 

1177 

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

1179 

1180 

1181def set_scheduler(dev, schedulers=None): 

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

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

1184 

1185 devices = [] 

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

1187 # Remove partition numbers 

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

1189 else: 

1190 rawdev = diskFromPartition(dev) 

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

1192 

1193 for d in devices: 

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

1195 

1196 

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

1198def _getVDIs(srobj): 

1199 VDIs = [] 

1200 try: 

1201 sr_ref = getattr(srobj, 'sr_ref') 

1202 except AttributeError: 

1203 return VDIs 

1204 

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

1206 for vdi in refs: 

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

1208 ref['vdi_ref'] = vdi 

1209 VDIs.append(ref) 

1210 return VDIs 

1211 

1212 

1213def _getVDI(srobj, vdi_uuid): 

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

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

1216 ref['vdi_ref'] = vdi 

1217 return ref 

1218 

1219 

1220def _convertDNS(name): 

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

1222 return addr 

1223 

1224 

1225def _containsVDIinuse(srobj): 

1226 VDIs = _getVDIs(srobj) 

1227 for vdi in VDIs: 

1228 if not vdi['managed']: 

1229 continue 

1230 sm_config = vdi['sm_config'] 

1231 if 'SRRef' in sm_config: 

1232 try: 

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

1234 for pbd in PBDs: 

1235 record = PBDs[pbd] 

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

1237 record["currently_attached"]: 

1238 return True 

1239 except: 

1240 pass 

1241 return False 

1242 

1243 

1244def isVDICommand(cmd): 

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

1246 "vdi_activate", "vdi_deactivate", 

1247 "vdi_epoch_begin", "vdi_epoch_end"]: 

1248 return True 

1249 else: 

1250 return False 

1251 

1252 

1253######################### 

1254# Daemon helper functions 

1255def p_id_fork(): 

1256 try: 

1257 p_id = os.fork() 

1258 except OSError as e: 

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

1260 sys.exit(-1) 

1261 

1262 if (p_id == 0): 

1263 os.setsid() 

1264 try: 

1265 p_id = os.fork() 

1266 except OSError as e: 

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

1268 sys.exit(-1) 

1269 if (p_id == 0): 

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

1271 os.umask(0) 

1272 else: 

1273 os._exit(0) 

1274 else: 

1275 os._exit(0) 

1276 

1277 

1278def daemon(): 

1279 p_id_fork() 

1280 # Query the max file descriptor parameter for this process 

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

1282 

1283 # Close any fds that are open 

1284 for fd in range(0, maxfd): 

1285 try: 

1286 os.close(fd) 

1287 except: 

1288 pass 

1289 

1290 # Redirect STDIN to STDOUT and STDERR 

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

1292 os.dup2(0, 1) 

1293 os.dup2(0, 2) 

1294 

1295################################################################################ 

1296# 

1297# Fist points 

1298# 

1299 

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

1301# 

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

1303# on the SR master; 

1304# 

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

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

1307# - otherwise, the function called is _pause. 

1308 

1309def _pause(secs, name): 

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

1311 time.sleep(secs) 

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

1313 

1314 

1315def _exit(name): 

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

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

1318 

1319 

1320class FistPoint: 

1321 def __init__(self, points): 

1322 #SMlog("Fist points loaded") 

1323 self.points = points 

1324 

1325 def is_legal(self, name): 

1326 return (name in self.points) 

1327 

1328 def is_active(self, name): 

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

1330 

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

1332 session = get_localAPI_session() 

1333 try: 

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

1335 

1336 if started: 

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

1338 else: 

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

1340 finally: 

1341 session.xenapi.session.logout() 

1342 

1343 def activate(self, name, sruuid): 

1344 if name in self.points: 

1345 if self.is_active(name): 

1346 self.mark_sr(name, sruuid, True) 

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

1348 self.mark_sr(name, sruuid, False) 

1349 _exit(name) 

1350 else: 

1351 _pause(FIST_PAUSE_PERIOD, name) 

1352 self.mark_sr(name, sruuid, False) 

1353 else: 

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

1355 

1356 def activate_custom_fn(self, name, fn): 

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

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

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

1360 fn() 

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

1362 else: 

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

1364 

1365 

1366def list_find(f, seq): 

1367 for item in seq: 

1368 if f(item): 

1369 return item 

1370 

1371GCPAUSE_FISTPOINT = "GCLoop_no_pause" 

1372 

1373fistpoint = FistPoint(["LVHDRT_finding_a_suitable_pair", 

1374 "LVHDRT_inflating_the_parent", 

1375 "LVHDRT_resizing_while_vdis_are_paused", 

1376 "LVHDRT_coalescing_VHD_data", 

1377 "LVHDRT_coalescing_before_inflate_grandparent", 

1378 "LVHDRT_relinking_grandchildren", 

1379 "LVHDRT_before_create_relink_journal", 

1380 "LVHDRT_xapiSM_serialization_tests", 

1381 "LVHDRT_clone_vdi_after_create_journal", 

1382 "LVHDRT_clone_vdi_after_shrink_parent", 

1383 "LVHDRT_clone_vdi_after_first_snap", 

1384 "LVHDRT_clone_vdi_after_second_snap", 

1385 "LVHDRT_clone_vdi_after_parent_hidden", 

1386 "LVHDRT_clone_vdi_after_parent_ro", 

1387 "LVHDRT_clone_vdi_before_remove_journal", 

1388 "LVHDRT_clone_vdi_after_lvcreate", 

1389 "LVHDRT_clone_vdi_before_undo_clone", 

1390 "LVHDRT_clone_vdi_after_undo_clone", 

1391 "LVHDRT_inflate_after_create_journal", 

1392 "LVHDRT_inflate_after_setSize", 

1393 "LVHDRT_inflate_after_zeroOut", 

1394 "LVHDRT_inflate_after_setSizePhys", 

1395 "LVHDRT_inflate_after_setSizePhys", 

1396 "LVHDRT_coaleaf_before_coalesce", 

1397 "LVHDRT_coaleaf_after_coalesce", 

1398 "LVHDRT_coaleaf_one_renamed", 

1399 "LVHDRT_coaleaf_both_renamed", 

1400 "LVHDRT_coaleaf_after_vdirec", 

1401 "LVHDRT_coaleaf_before_delete", 

1402 "LVHDRT_coaleaf_after_delete", 

1403 "LVHDRT_coaleaf_before_remove_j", 

1404 "LVHDRT_coaleaf_undo_after_rename", 

1405 "LVHDRT_coaleaf_undo_after_rename2", 

1406 "LVHDRT_coaleaf_undo_after_refcount", 

1407 "LVHDRT_coaleaf_undo_after_deflate", 

1408 "LVHDRT_coaleaf_undo_end", 

1409 "LVHDRT_coaleaf_stop_after_recovery", 

1410 "LVHDRT_coaleaf_finish_after_inflate", 

1411 "LVHDRT_coaleaf_finish_end", 

1412 "LVHDRT_coaleaf_delay_1", 

1413 "LVHDRT_coaleaf_delay_2", 

1414 "LVHDRT_coaleaf_delay_3", 

1415 "testsm_clone_allow_raw", 

1416 "xenrt_default_vdi_type_legacy", 

1417 "blktap_activate_inject_failure", 

1418 "blktap_activate_error_handling", 

1419 GCPAUSE_FISTPOINT, 

1420 "cleanup_coalesceVHD_inject_failure", 

1421 "cleanup_tracker_no_progress", 

1422 "FileSR_fail_hardlink", 

1423 "FileSR_fail_snap1", 

1424 "FileSR_fail_snap2", 

1425 "LVM_journaler_exists", 

1426 "LVM_journaler_none", 

1427 "LVM_journaler_badname", 

1428 "LVM_journaler_readfail", 

1429 "LVM_journaler_writefail"]) 

1430 

1431 

1432def set_dirty(session, sr): 

1433 try: 

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

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

1436 except: 

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

1438 

1439 

1440def doesFileHaveOpenHandles(fileName): 

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

1442 (retVal, processAndPidTuples) = \ 

1443 findRunningProcessOrOpenFile(fileName, False) 

1444 

1445 if not retVal: 

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

1447 fileName) 

1448 # err on the side of caution 

1449 return True 

1450 else: 

1451 if len(processAndPidTuples) > 0: 

1452 return True 

1453 else: 

1454 return False 

1455 

1456 

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

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

1459def extractSRFromDevMapper(path): 

1460 try: 

1461 path = os.path.basename(path) 

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

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

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

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

1466 except: 

1467 return '' 

1468 

1469 

1470def pid_is_alive(pid): 

1471 """ 

1472 Try to kill PID with signal 0. 

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

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

1475 signal it. Still return true. 

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

1477 """ 

1478 try: 

1479 os.kill(pid, 0) 

1480 return True 

1481 except OSError as e: 

1482 if e.errno == errno.EPERM: 

1483 return True 

1484 return False 

1485 

1486 

1487# Looks at /proc and figures either 

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

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

1490# returns process names and pids 

1491def findRunningProcessOrOpenFile(name, process=True): 

1492 retVal = True 

1493 links = [] 

1494 processandpids = [] 

1495 sockets = set() 

1496 try: 

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

1498 [name, process]) 

1499 

1500 # Look at all pids 

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

1502 for pid in sorted(pids): 

1503 try: 

1504 try: 

1505 f = None 

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

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

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

1509 # Just want the process name 

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

1511 prog = argv[0] 

1512 except IOError as e: 

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

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

1515 continue 

1516 finally: 

1517 if f is not None: 1517 ↛ 1502,   1517 ↛ 15202 missed branches: 1) line 1517 didn't jump to line 1502, because the continue on line 1515 wasn't executed, 2) line 1517 didn't jump to line 1520, because the condition on line 1517 was never false

1518 f.close() 1518 ↛ 1502line 1518 didn't jump to line 1502, because the continue on line 1515 wasn't executed

1519 

1520 try: 

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

1522 files = os.listdir(fd_dir) 

1523 except OSError as e: 

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

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

1526 # Ignore pid that are no longer valid 

1527 continue 

1528 else: 

1529 raise 

1530 

1531 for file in files: 

1532 try: 

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

1534 except OSError: 

1535 continue 

1536 

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

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

1539 links.append(link) 

1540 else: 

1541 # need to return process name and pid tuples 

1542 if link == name: 

1543 processandpids.append((prog, pid)) 

1544 

1545 # Get the connected sockets 

1546 if name == prog: 

1547 sockets.update(get_connected_sockets(pid)) 

1548 

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

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

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

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

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

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

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

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

1557 

1558 except Exception as e: 

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

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

1561 retVal = False 

1562 

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

1564 return retVal, links, sockets 

1565 else: 

1566 return retVal, processandpids 

1567 

1568 

1569def get_connected_sockets(pid): 

1570 sockets = set() 

1571 try: 

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

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

1574 # - Pointer address to socket (hex) 

1575 # - Refcount (HEX) 

1576 # - 0 

1577 # - State (HEX, 0 or __SO_ACCEPTCON) 

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

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

1580 # - Inode number 

1581 # - Path (optional) 

1582 open_sock_matcher = re.compile( 

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

1584 with open( 

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

1586 lines = f.readlines() 

1587 for line in lines: 

1588 match = open_sock_matcher.match(line) 

1589 if match: 

1590 sockets.add(match[1]) 

1591 except OSError as e: 

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

1593 # Ignore pid that are no longer valid 

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

1595 (e.errno, pid)) 

1596 else: 

1597 raise 

1598 return sockets 

1599 

1600 

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

1602 retries = 0 

1603 while True: 

1604 try: 

1605 return f() 

1606 except Exception as e: 

1607 for exception in exceptions: 

1608 if isinstance(e, exception): 

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

1610 str(e), retries 

1611 )) 

1612 break 

1613 else: 

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

1615 raise e 

1616 

1617 retries += 1 

1618 if retries >= maxretry: 

1619 break 

1620 

1621 time.sleep(period) 

1622 

1623 return f() 

1624 

1625 

1626def getCslDevPath(svid): 

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

1628 if svid.startswith("NETAPP_"): 

1629 # special attention for NETAPP SVIDs 

1630 svid_parts = svid.split("__") 

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

1632 else: 

1633 globstr = basepath + svid + "*" 

1634 

1635 return globstr 

1636 

1637 

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

1639def get_scsiid_from_svid(md_svid): 

1640 cslg_path = getCslDevPath(md_svid) 

1641 abs_path = glob.glob(cslg_path) 

1642 if abs_path: 

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

1644 return scsiutil.getSCSIid(real_path) 

1645 else: 

1646 return None 

1647 

1648 

1649def get_isl_scsiids(session): 

1650 # Get cslg type SRs 

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

1652 

1653 # Iterate through the SR to get the scsi ids 

1654 scsi_id_ret = [] 

1655 for SR in SRs: 

1656 sr_rec = SRs[SR] 

1657 # Use the md_svid to get the scsi id 

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

1659 if scsi_id: 

1660 scsi_id_ret.append(scsi_id) 

1661 

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

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

1664 for vdi_rec in vdi_recs: 

1665 vdi_rec = vdi_recs[vdi_rec] 

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

1667 if scsi_id: 

1668 scsi_id_ret.append(scsi_id) 

1669 

1670 return scsi_id_ret 

1671 

1672 

1673class extractXVA: 

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

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

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

1677 HDR_SIZE = 512 

1678 BLOCK_SIZE = 512 

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

1680 SIZE_OFFSET = 124 

1681 ZERO_FILLED_REC = 2 

1682 NULL_IDEN = '\x00' 

1683 DIR_IDEN = '/' 

1684 CHECKSUM_IDEN = '.checksum' 

1685 OVA_FILE = 'ova.xml' 

1686 

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

1688 # as and when needed 

1689 def __init__(self, filename): 

1690 self.__extract_path = '' 

1691 self.__filename = filename 

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

1693 try: 

1694 self.spawn_p = subprocess.Popen( 

1695 cmd, shell=True, \ 

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

1697 stderr=subprocess.PIPE, close_fds=True) 

1698 except Exception as e: 

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

1700 raise Exception(str(e)) 

1701 

1702 # Create dir to extract the files 

1703 self.__extract_path = tempfile.mkdtemp() 

1704 

1705 def __del__(self): 

1706 shutil.rmtree(self.__extract_path) 

1707 

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

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

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

1711 def getTuple(self): 

1712 zerod_record = 0 

1713 ret_f_name = '' 

1714 ret_base_f_name = '' 

1715 

1716 try: 

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

1718 while True: 

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

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

1721 

1722 # Break out in case of end of file 

1723 if f_hdr == '': 

1724 if zerod_record == extractXVA.ZERO_FILLED_REC: 

1725 break 

1726 else: 

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

1728 extractXVA.ZERO_FILLED_REC) 

1729 raise Exception('Unrecognized end of file') 

1730 

1731 # Watch out for zero records, two zero records 

1732 # denote end of file. 

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

1734 zerod_record += 1 

1735 continue 

1736 

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

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

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

1740 f_size_octal = f_hdr[extractXVA.SIZE_OFFSET: \ 

1741 extractXVA.SIZE_OFFSET + extractXVA.SIZE_LEN] 

1742 f_size = int(f_size_octal, 8) 

1743 if f_name.endswith(extractXVA.CHECKSUM_IDEN): 

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

1745 ret_base_f_name: 

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

1747 yield(ret_f_name, checksum) 

1748 else: 

1749 # Expects file followed by its checksum 

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

1751 ret_f_name) 

1752 raise Exception( \ 

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

1754 ret_f_name) 

1755 else: 

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

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

1758 # read the contents into a file, it will 

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

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

1761 ret_base_f_name = f_name 

1762 

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

1764 # else create it. 

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

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

1767 os.mkdir(folder_path) 

1768 

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

1770 f = open(ret_f_name, 'w') 

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

1772 f.close() 

1773 if f_name == extractXVA.OVA_FILE: 

1774 yield(ret_f_name, '') 

1775 

1776 # Skip zero'd portion of data block 

1777 round_off = f_size % extractXVA.BLOCK_SIZE 

1778 if round_off != 0: 

1779 zeros = self.spawn_p.stdout.read( 

1780 extractXVA.BLOCK_SIZE - round_off) 

1781 except Exception as e: 

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

1783 self.__filename)) 

1784 

1785 # Kill and Drain stdout of the gunzip process, 

1786 # else gunzip might block on stdout 

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

1788 self.spawn_p.communicate() 

1789 raise Exception(str(e)) 

1790 

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

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

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

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

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

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

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

1798 (0x10FFFE, 0x10FFFF)] 

1799 

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

1801 for (low, high) in illegal_xml_chars 

1802 if low < sys.maxunicode] 

1803 

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

1805 

1806 

1807def isLegalXMLString(s): 

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

1809 illegal XML characters specified in 

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

1811 """ 

1812 

1813 if len(s) > 0: 

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

1815 else: 

1816 return True 

1817 

1818 

1819def unictrunc(string, max_bytes): 

1820 """ 

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

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

1823 more than the given number of bytes. 

1824 

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

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

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

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

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

1830 

1831 string: the string to truncate 

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

1833 """ 

1834 if isinstance(string, str): 

1835 return_chars = True 

1836 else: 

1837 return_chars = False 

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

1839 

1840 cur_chars = 0 

1841 cur_bytes = 0 

1842 for char in string: 

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

1844 if cur_bytes + charsize > max_bytes: 

1845 break 

1846 else: 

1847 cur_chars += 1 

1848 cur_bytes += charsize 

1849 return cur_chars if return_chars else cur_bytes 

1850 

1851 

1852def hideValuesInPropMap(propmap, propnames): 

1853 """ 

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

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

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

1857 value and return the altered map. 

1858 If none found, return the original map 

1859 """ 

1860 matches = [] 

1861 for propname in propnames: 

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

1863 matches.append(propname) 

1864 

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

1866 deepCopyRec = copy.deepcopy(propmap) 

1867 for match in matches: 

1868 deepCopyRec[match] = '******' 

1869 return deepCopyRec 

1870 

1871 return propmap 

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

1873 

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

1875DEFAULT_SEGMENT_LEN = 950 

1876 

1877 

1878def hidePasswdInConfig(config): 

1879 """ 

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

1881 for example "device_config" 

1882 """ 

1883 return hideValuesInPropMap(config, PASSWD_PROP_KEYS) 

1884 

1885 

1886def hidePasswdInParams(params, configProp): 

1887 """ 

1888 Function to hide password values in a specified property which 

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

1890 in a larger property map. 

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

1892 "sm_config", etc 

1893 """ 

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

1895 return params 

1896 

1897 

1898def hideMemberValuesInXmlParams(xmlParams, propnames=PASSWD_PROP_KEYS): 

1899 """ 

1900 Function to hide password values in XML params, specifically 

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

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

1903 whose values we want to hide, and: 

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

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

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

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

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

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

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

1911 """ 

1912 findStrPrefixHead = "<member><name>" 

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

1914 findStrSuffix = "</value>" 

1915 strlen = len(xmlParams) 

1916 

1917 for propname in propnames: 

1918 findStrPrefix = findStrPrefixHead + propname + findStrPrefixTail 

1919 idx = xmlParams.find(findStrPrefix) 

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

1921 idx += len(findStrPrefix) 

1922 idx2 = xmlParams.find(findStrSuffix, idx) 

1923 if idx2 != -1: 

1924 retStr = xmlParams[0:idx] 

1925 retStr += "******" 

1926 retStr += xmlParams[idx2:strlen] 

1927 return retStr 

1928 else: 

1929 return xmlParams 

1930 return xmlParams 

1931 

1932 

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

1934 """ 

1935 Split xml string data into substrings small enough for the 

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

1937 Usage: 

1938 strList = [] 

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

1940 """ 

1941 remainingData = str(xmlData) 

1942 

1943 # "Un-pretty-print" 

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

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

1946 

1947 remainingChars = len(remainingData) 

1948 returnData = '' 

1949 

1950 thisLineNum = 0 

1951 while remainingChars > segmentLen: 

1952 thisLineNum = thisLineNum + 1 

1953 index = segmentLen 

1954 tmpStr = remainingData[:segmentLen] 

1955 tmpIndex = tmpStr.rfind('>') 

1956 if tmpIndex != -1: 

1957 index = tmpIndex + 1 

1958 

1959 tmpStr = tmpStr[:index] 

1960 remainingData = remainingData[index:] 

1961 remainingChars = len(remainingData) 

1962 

1963 if showContd: 

1964 if thisLineNum != 1: 

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

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

1967 

1968 returnData += tmpStr + '\n' 

1969 

1970 if showContd and thisLineNum > 0: 

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

1972 returnData += remainingData 

1973 

1974 return returnData 

1975 

1976 

1977def inject_failure(): 

1978 raise Exception('injected failure') 

1979 

1980 

1981def open_atomic(path, mode=None): 

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

1983 Leaves the file open and returns the file object. 

1984 

1985 path: the path to atomically open 

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

1987 returns: an open file object""" 

1988 

1989 assert path 

1990 

1991 flags = os.O_CREAT | os.O_EXCL 

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

1993 if mode: 

1994 if mode not in modes: 

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

1996 flags |= modes[mode] 

1997 fd = os.open(path, flags) 

1998 try: 

1999 if mode: 

2000 return os.fdopen(fd, mode) 

2001 else: 

2002 return os.fdopen(fd) 

2003 except: 

2004 os.close(fd) 

2005 raise 

2006 

2007 

2008def isInvalidVDI(exception): 

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

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

2011 

2012 

2013def get_pool_restrictions(session): 

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

2015 established.""" 

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

2017 

2018 

2019def read_caching_is_restricted(session): 

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

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

2022 return True 

2023 restrictions = get_pool_restrictions(session) 

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

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

2026 return True 

2027 return False 

2028 

2029 

2030def sessions_less_than_targets(other_config, device_config): 

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

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

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

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

2035 return (sessions < targets) 

2036 else: 

2037 return False 

2038 

2039 

2040def enable_and_start_service(name, start): 

2041 attempt = 0 

2042 while True: 

2043 attempt += 1 

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

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

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

2047 if ret == 0: 

2048 return 

2049 elif attempt >= 3: 

2050 raise Exception( 

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

2052 ) 

2053 time.sleep(1) 

2054 

2055 

2056def stop_service(name): 

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

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

2059 if ret == 0: 

2060 return 

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

2062 

2063 

2064def restart_service(name): 

2065 attempt = 0 

2066 while True: 

2067 attempt += 1 

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

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

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

2071 if ret == 0: 

2072 return 

2073 elif attempt >= 3: 

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

2075 raise Exception( 

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

2077 ) 

2078 time.sleep(1) 

2079 

2080 

2081def check_pid_exists(pid): 

2082 try: 

2083 os.kill(pid, 0) 

2084 except OSError: 

2085 return False 

2086 else: 

2087 return True 

2088 

2089 

2090def make_profile(name, function): 

2091 """ 

2092 Helper to execute cProfile using unique log file. 

2093 """ 

2094 

2095 import cProfile 

2096 import itertools 

2097 import os.path 

2098 import time 

2099 

2100 assert name 

2101 assert function 

2102 

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

2104 makedirs(FOLDER) 

2105 

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

2107 

2108 def gen_path(path): 

2109 yield path 

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

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

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

2113 

2114 for profile_path in gen_path(FOLDER + filename): 

2115 try: 

2116 file = open_atomic(profile_path, 'w') 

2117 file.close() 

2118 break 

2119 except OSError as e: 

2120 if e.errno == errno.EEXIST: 

2121 pass 

2122 else: 

2123 raise 

2124 

2125 try: 

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

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

2128 finally: 

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

2130 

2131 

2132def strtobool(str): 

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

2134 # There is not alternative for strtobool. 

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

2136 # So this is a custom implementation with differences: 

2137 # - A boolean is returned instead of integer 

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

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

2140 return False 

2141 str = str.lower() 

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

2143 return True 

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

2145 return False 

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

2147 

2148 

2149def find_executable(name): 

2150 return shutil.which(name) 

2151 

2152 

2153def conditional_decorator(decorator, condition): 

2154 def wrapper(func): 

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

2156 return func 

2157 return decorator(func) 

2158 return wrapper