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" 

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 type(obj) == str: 

119 return obj 

120 return str(obj) 

121 

122 

123def shellquote(arg): 

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

125 

126 

127def make_WWN(name): 

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

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

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

131 # inject dashes for each nibble 

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

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

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

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

136 return name 

137 

138 

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

140 syslog.openlog(ident, 0, facility) 

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

142 syslog.closelog() 

143 

144 

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

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

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

148 _logToSyslog(ident, _SM_SYSLOG_FACILITY, priority, message_line) 

149 

150 

151class LoggerCounter: 

152 def __init__(self, max_repeats): 

153 self.previous_message = None 

154 self.max_repeats = max_repeats 

155 self.repeat_counter = 0 

156 

157 def log(self, message): 

158 self.repeat_counter += 1 

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

160 SMlog(message) 

161 self.previous_message = message 

162 self.repeat_counter = 0 

163 

164def _getDateString(): 

165 d = datetime.datetime.now() 

166 t = d.timetuple() 

167 return "%s-%s-%s:%s:%s:%s" % \ 

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

169 

170 

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

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

173 env = None 

174 if new_env: 

175 env = dict(os.environ) 

176 env.update(new_env) 

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

178 stdout=subprocess.PIPE, 

179 stderr=subprocess.PIPE, 

180 close_fds=True, env=env, 

181 universal_newlines=text) 

182 

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

184 inputtext = inputtext.encode() 

185 

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

187 

188 rc = proc.returncode 

189 return rc, stdout, stderr 

190 

191 

192def is_string(value): 

193 return isinstance(value, str) 

194 

195 

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

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

198# replace the original ones at some later date. 

199# 

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

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

202# written to the logs. 

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

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

205 cmdlist_for_exec = [] 

206 cmdlist_for_log = [] 

207 for item in cmdlist: 

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

209 cmdlist_for_exec.append(item) 

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

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

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

213 else: 

214 cmdlist_for_log.append(item) 

215 else: 

216 cmdlist_for_log.append(item) 

217 else: 

218 cmdlist_for_exec.append(item[0]) 

219 cmdlist_for_log.append(item[1]) 

220 

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

222 SMlog(cmdlist_for_log) 

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

224 if rc != expect_rc: 

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

226 (rc, stdout, stderr)) 

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

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

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

230 stderr = stdout 

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

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

233 SMlog(" pread SUCCESS") 

234 return stdout 

235 

236 

237# POSIX guaranteed atomic within the same file system. 

238# Supply directory to ensure tempfile is created 

239# in the same directory. 

240def atomicFileWrite(targetFile, directory, text): 

241 

242 file = None 

243 try: 

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

245 # our responsibility to clean it up. 

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

247 file = open(tempPath, 'w') 

248 file.write(text) 

249 

250 # Ensure flushed to disk. 

251 file.flush() 

252 os.fsync(file.fileno()) 

253 file.close() 

254 

255 os.rename(tempPath, targetFile) 

256 except OSError: 

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

258 

259 finally: 

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

261 file.close() 

262 

263 if os.path.isfile(tempPath): 

264 os.remove(tempPath) 

265 

266 

267#Read STDOUT from cmdlist and discard STDERR output 

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

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

270 

271 

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

273def pread3(cmdlist, text): 

274 SMlog(cmdlist) 

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

276 if rc: 

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

278 (rc, stdout, stderr)) 

279 if '' == stderr: 

280 stderr = stdout 

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

282 SMlog(" pread3 SUCCESS") 

283 return stdout 

284 

285 

286def listdir(path, quiet=False): 

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

288 try: 

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

290 if len(text) == 0: 

291 return [] 

292 return text.split('\n') 

293 except CommandException as inst: 

294 if inst.code == errno.ENOENT: 

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

296 else: 

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

298 

299 

300def gen_uuid(): 

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

302 return pread(cmd)[:-1] 

303 

304 

305def match_uuid(s): 

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

307 return regex.search(s, 0) 

308 

309 

310def findall_uuid(s): 

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

312 return regex.findall(s, 0) 

313 

314 

315def exactmatch_uuid(s): 

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

317 return regex.search(s, 0) 

318 

319 

320def start_log_entry(srpath, path, args): 

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

322 logstring += " log: " 

323 logstring += srpath 

324 logstring += " " + path 

325 for element in args: 

326 logstring += " " + element 

327 try: 

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

329 file.write(logstring) 

330 file.write("\n") 

331 file.close() 

332 except: 

333 pass 

334 

335 # failed to write log ... 

336 

337def end_log_entry(srpath, path, args): 

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

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

340 logstring += " end: " 

341 logstring += srpath 

342 logstring += " " + path 

343 for element in args: 

344 logstring += " " + element 

345 try: 

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

347 file.write(logstring) 

348 file.write("\n") 

349 file.close() 

350 except: 

351 pass 

352 

353 # failed to write log ... 

354 # for now print 

355 # print "%s" % logstring 

356 

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

358 retries = 0 

359 while True: 

360 try: 

361 return f() 

362 except OSError as ose: 

363 err = int(ose.errno) 

364 if not err in errlist: 

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

366 except CommandException as ce: 

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

368 raise 

369 

370 retries += 1 

371 if retries >= maxretry: 

372 break 

373 

374 time.sleep(period) 

375 

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

377 

378 

379def ioretry_stat(path, maxretry=IORETRY_MAX): 

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

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

382 retries = 0 

383 while retries < maxretry: 

384 stat = os.statvfs(path) 

385 if stat.f_blocks != -1: 

386 return stat 

387 time.sleep(1) 

388 retries += 1 

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

390 

391 

392def sr_get_capability(sr_uuid, session=None): 

393 result = [] 

394 local_session = None 

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

396 local_session = get_localAPI_session() 

397 session = local_session 

398 

399 try: 

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

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

402 sm_rec = session.xenapi.SM.get_all_records_where( 

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

404 

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

406 if len(sm_rec) > 0: 

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

408 

409 return result 

410 finally: 

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

412 local_session.xenapi.session.logout() 

413 

414def sr_get_driver_info(driver_info): 

415 results = {} 

416 # first add in the vanilla stuff 

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

418 'driver_version', 'required_api_version']: 

419 results[key] = driver_info[key] 

420 # add the capabilities (xmlrpc array) 

421 # enforcing activate/deactivate for blktap2 

422 caps = driver_info['capabilities'] 

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

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

425 if not cap in caps: 

426 caps.append(cap) 

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

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

429 

430 results['capabilities'] = caps 

431 # add in the configuration options 

432 options = [] 

433 for option in driver_info['configuration']: 

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

435 results['configuration'] = options 

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

437 

438 

439def return_nil(): 

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

441 

442 

443def SRtoXML(SRlist): 

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

445 driver = dom.createElement("SRlist") 

446 dom.appendChild(driver) 

447 

448 for key in SRlist.keys(): 

449 dict = SRlist[key] 

450 entry = dom.createElement("SR") 

451 driver.appendChild(entry) 

452 

453 e = dom.createElement("UUID") 

454 entry.appendChild(e) 

455 textnode = dom.createTextNode(key) 

456 e.appendChild(textnode) 

457 

458 if 'size' in dict: 

459 e = dom.createElement("Size") 

460 entry.appendChild(e) 

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

462 e.appendChild(textnode) 

463 

464 if 'storagepool' in dict: 

465 e = dom.createElement("StoragePool") 

466 entry.appendChild(e) 

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

468 e.appendChild(textnode) 

469 

470 if 'aggregate' in dict: 

471 e = dom.createElement("Aggregate") 

472 entry.appendChild(e) 

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

474 e.appendChild(textnode) 

475 

476 return dom.toprettyxml() 

477 

478 

479def pathexists(path): 

480 try: 

481 os.lstat(path) 

482 return True 

483 except OSError as inst: 

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

485 time.sleep(1) 

486 try: 

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

488 os.lstat(path) 

489 return True 

490 except: 

491 pass 

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

493 return False 

494 

495 

496def force_unlink(path): 

497 try: 

498 os.unlink(path) 

499 except OSError as e: 

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

501 raise 

502 

503 

504def create_secret(session, secret): 

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

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

507 

508 

509def get_secret(session, uuid): 

510 try: 

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

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

513 except: 

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

515 

516 

517def get_real_path(path): 

518 "Follow symlinks to the actual file" 

519 absPath = path 

520 directory = '' 

521 while os.path.islink(absPath): 

522 directory = os.path.dirname(absPath) 

523 absPath = os.readlink(absPath) 

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

525 return absPath 

526 

527 

528def wait_for_path(path, timeout): 

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

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

531 return True 

532 time.sleep(1) 

533 return False 

534 

535 

536def wait_for_nopath(path, timeout): 

537 for i in range(0, timeout): 

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

539 return True 

540 time.sleep(1) 

541 return False 

542 

543 

544def wait_for_path_multi(path, timeout): 

545 for i in range(0, timeout): 

546 paths = glob.glob(path) 

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

548 if len(paths): 

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

550 return paths[0] 

551 time.sleep(1) 

552 return "" 

553 

554 

555def isdir(path): 

556 try: 

557 st = os.stat(path) 

558 return stat.S_ISDIR(st.st_mode) 

559 except OSError as inst: 

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

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

562 return False 

563 

564 

565def get_single_entry(path): 

566 f = open(path, 'r') 

567 line = f.readline() 

568 f.close() 

569 return line.rstrip() 

570 

571 

572def get_fs_size(path): 

573 st = ioretry_stat(path) 

574 return st.f_blocks * st.f_frsize 

575 

576 

577def get_fs_utilisation(path): 

578 st = ioretry_stat(path) 

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

580 st.f_frsize 

581 

582 

583def ismount(path): 

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

585 try: 

586 s1 = os.stat(path) 

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

588 except OSError as inst: 

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

590 dev1 = s1.st_dev 

591 dev2 = s2.st_dev 

592 if dev1 != dev2: 

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

594 ino1 = s1.st_ino 

595 ino2 = s2.st_ino 

596 if ino1 == ino2: 

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

598 return False 

599 

600 

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

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

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

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

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

606 makedirs(head, mode) 

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

608 return 

609 try: 

610 os.mkdir(name, mode) 

611 except OSError as exc: 

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

613 if mode: 

614 os.chmod(name, mode) 

615 pass 

616 else: 

617 raise 

618 

619 

620def zeroOut(path, fromByte, bytes): 

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

622 blockSize = 4096 

623 

624 fromBlock = fromByte // blockSize 

625 if fromByte % blockSize: 

626 fromBlock += 1 

627 bytesBefore = fromBlock * blockSize - fromByte 

628 if bytesBefore > bytes: 

629 bytesBefore = bytes 

630 bytes -= bytesBefore 

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

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

633 try: 

634 pread2(cmd) 

635 except CommandException: 

636 return False 

637 

638 blocks = bytes // blockSize 

639 bytes -= blocks * blockSize 

640 fromByte = (fromBlock + blocks) * blockSize 

641 if blocks: 

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

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

644 try: 

645 pread2(cmd) 

646 except CommandException: 

647 return False 

648 

649 if bytes: 

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

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

652 try: 

653 pread2(cmd) 

654 except CommandException: 

655 return False 

656 

657 return True 

658 

659 

660def wipefs(blockdev): 

661 "Wipe filesystem signatures from `blockdev`" 

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

663 

664 

665def match_rootdev(s): 

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

667 return regex.search(s, 0) 

668 

669 

670def getrootdev(): 

671 filename = '/etc/xensource-inventory' 

672 try: 

673 f = open(filename, 'r') 

674 except: 

675 raise xs_errors.XenError('EIO', \ 

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

677 rootdev = '' 

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

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

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

681 raise xs_errors.XenError('NoRootDev') 

682 return rootdev 

683 

684 

685def getrootdevID(): 

686 rootdev = getrootdev() 

687 try: 

688 rootdevID = scsiutil.getSCSIid(rootdev) 

689 except: 

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

691 % rootdev) 

692 return '' 

693 

694 if not len(rootdevID): 

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

696 % rootdev) 

697 

698 return rootdevID 

699 

700 

701def get_localAPI_session(): 

702 # First acquire a valid session 

703 session = XenAPI.xapi_local() 

704 try: 

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

706 except: 

707 raise xs_errors.XenError('APISession') 

708 return session 

709 

710 

711def get_this_host(): 

712 uuid = None 

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

714 for line in f.readlines(): 

715 if line.startswith("INSTALLATION_UUID"): 

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

717 f.close() 

718 return uuid 

719 

720 

721def get_master_ref(session): 

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

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

724 

725 

726def is_master(session): 

727 return get_this_host_ref(session) == get_master_ref(session) 

728 

729 

730def get_localhost_ref(session): 

731 filename = '/etc/xensource-inventory' 

732 try: 

733 f = open(filename, 'r') 

734 except: 

735 raise xs_errors.XenError('EIO', \ 

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

737 domid = '' 

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

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

740 if not domid: 

741 raise xs_errors.XenError('APILocalhost') 

742 

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

744 for vm in vms: 

745 record = vms[vm] 

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

747 hostid = record["resident_on"] 

748 return hostid 

749 raise xs_errors.XenError('APILocalhost') 

750 

751 

752def match_domain_id(s): 

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

754 return regex.search(s, 0) 

755 

756 

757def get_hosts_attached_on(session, vdi_uuids): 

758 host_refs = {} 

759 for vdi_uuid in vdi_uuids: 

760 try: 

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

762 except XenAPI.Failure: 

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

764 continue 

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

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

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

768 return host_refs.keys() 

769 

770def get_this_host_address(session): 

771 host_uuid = get_this_host() 

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

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

774 

775def get_host_addresses(session): 

776 addresses = [] 

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

778 for record in hosts.values(): 

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

780 return addresses 

781 

782def get_this_host_ref(session): 

783 host_uuid = get_this_host() 

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

785 return host_ref 

786 

787 

788def get_slaves_attached_on(session, vdi_uuids): 

789 "assume this host is the SR master" 

790 host_refs = get_hosts_attached_on(session, vdi_uuids) 

791 master_ref = get_this_host_ref(session) 

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

793 

794 

795def get_online_hosts(session): 

796 online_hosts = [] 

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

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

799 metricsRef = host_rec["metrics"] 

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

801 if metrics["live"]: 

802 online_hosts.append(host_ref) 

803 return online_hosts 

804 

805 

806def get_all_slaves(session): 

807 "assume this host is the SR master" 

808 host_refs = get_online_hosts(session) 

809 master_ref = get_this_host_ref(session) 

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

811 

812 

813def is_attached_rw(sm_config): 

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

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

816 return True 

817 return False 

818 

819 

820def attached_as(sm_config): 

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

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

823 return val 

824 

825 

826def find_my_pbd_record(session, host_ref, sr_ref): 

827 try: 

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

829 for pbd_ref in pbds.keys(): 

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

831 return [pbd_ref, pbds[pbd_ref]] 

832 return None 

833 except Exception as e: 

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

835 return None 

836 

837 

838def find_my_pbd(session, host_ref, sr_ref): 

839 ret = find_my_pbd_record(session, host_ref, sr_ref) 

840 if ret is not None: 

841 return ret[0] 

842 else: 

843 return None 

844 

845 

846def test_hostPBD_devs(session, sr_uuid, devs): 

847 host = get_localhost_ref(session) 

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

849 try: 

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

851 except: 

852 raise xs_errors.XenError('APIPBDQuery') 

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

854 for pbd in pbds: 

855 record = pbds[pbd] 

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

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

858 break 

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

860 devconfig = record["device_config"] 

861 if 'device' in devconfig: 

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

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

864 return True 

865 return False 

866 

867 

868def test_hostPBD_lun(session, targetIQN, LUNid): 

869 host = get_localhost_ref(session) 

870 try: 

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

872 except: 

873 raise xs_errors.XenError('APIPBDQuery') 

874 for pbd in pbds: 

875 record = pbds[pbd] 

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

877 devconfig = record["device_config"] 

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

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

880 devconfig['LUNid'] == LUNid: 

881 return True 

882 return False 

883 

884 

885def test_SCSIid(session, sr_uuid, SCSIid): 

886 if sr_uuid is not None: 

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

888 try: 

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

890 except: 

891 raise xs_errors.XenError('APIPBDQuery') 

892 for pbd in pbds: 

893 record = pbds[pbd] 

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

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

896 if sr_uuid is not None: 

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

898 break 

899 devconfig = record["device_config"] 

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

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

902 return True 

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

904 return True 

905 elif 'scsi-' + SCSIid in sm_config: 

906 return True 

907 return False 

908 

909 

910class TimeoutException(SMException): 

911 pass 

912 

913 

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

915 def handler(signum, frame): 

916 raise TimeoutException() 

917 signal.signal(signal.SIGALRM, handler) 

918 signal.alarm(timeoutseconds) 

919 try: 

920 return function(*arguments) 

921 finally: 

922 signal.alarm(0) 

923 

924 

925def _incr_iscsiSR_refcount(targetIQN, uuid): 

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

927 os.mkdir(ISCSI_REFDIR) 

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

929 try: 

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

931 except: 

932 raise xs_errors.XenError('LVMRefCount', \ 

933 opterr='file %s' % filename) 

934 

935 f.seek(0) 

936 found = False 

937 refcount = 0 

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

939 refcount += 1 

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

941 found = True 

942 if not found: 

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

944 refcount += 1 

945 f.close() 

946 return refcount 

947 

948 

949def _decr_iscsiSR_refcount(targetIQN, uuid): 

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

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

952 return 0 

953 try: 

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

955 except: 

956 raise xs_errors.XenError('LVMRefCount', \ 

957 opterr='file %s' % filename) 

958 

959 f.seek(0) 

960 output = [] 

961 refcount = 0 

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

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

964 output.append(line.rstrip()) 

965 refcount += 1 

966 if not refcount: 

967 os.unlink(filename) 

968 return refcount 

969 

970 # Re-open file and truncate 

971 f.close() 

972 f = open(filename, 'w') 

973 for i in range(0, refcount): 

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

975 f.close() 

976 return refcount 

977 

978 

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

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

981def test_activePoolPBDs(session, host, uuid): 

982 try: 

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

984 except: 

985 raise xs_errors.XenError('APIPBDQuery') 

986 for pbd in pbds: 

987 record = pbds[pbd] 

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

989 and record["currently_attached"]: 

990 return True 

991 return False 

992 

993 

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

995 try: 

996 pbdref = find_my_pbd(session, host_ref, sr_ref) 

997 if pbdref is not None: 

998 key = "mpath-" + SCSIid 

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

1000 except: 

1001 pass 

1002 

1003 

1004 

1005def _testHost(hostname, port, errstring): 

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

1007 try: 

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

1009 except: 

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

1011 raise xs_errors.XenError('DNSError') 

1012 

1013 timeout = 5 

1014 

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

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

1017 sock.settimeout(timeout) 

1018 try: 

1019 sock.connect(sockinfo[4]) 

1020 # Fix for MS storage server bug 

1021 sock.send(b'\n') 

1022 sock.close() 

1023 except socket.error as reason: 

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

1025 % (timeout, hostname, reason)) 

1026 raise xs_errors.XenError(errstring) 

1027 

1028 

1029def match_scsiID(s, id): 

1030 regex = re.compile(id) 

1031 return regex.search(s, 0) 

1032 

1033 

1034def _isSCSIid(s): 

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

1036 return regex.search(s, 0) 

1037 

1038 

1039def test_scsiserial(session, device): 

1040 device = os.path.realpath(device) 

1041 if not scsiutil._isSCSIdev(device): 

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

1043 return False 

1044 serial = "" 

1045 try: 

1046 serial += scsiutil.getserial(device) 

1047 except: 

1048 # Error allowed, SCSIid is the important one 

1049 pass 

1050 

1051 try: 

1052 scsiID = scsiutil.getSCSIid(device) 

1053 except: 

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

1055 % device) 

1056 return False 

1057 if not len(scsiID): 

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

1059 % device) 

1060 return False 

1061 

1062 try: 

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

1064 except: 

1065 raise xs_errors.XenError('APIFailure') 

1066 for SR in SRs: 

1067 record = SRs[SR] 

1068 conf = record["sm_config"] 

1069 if 'devserial' in conf: 

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

1071 if _isSCSIid(dev): 

1072 if match_scsiID(dev, scsiID): 

1073 return True 

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

1075 return True 

1076 return False 

1077 

1078 

1079def default(self, field, thunk): 

1080 try: 

1081 return getattr(self, field) 

1082 except: 

1083 return thunk() 

1084 

1085 

1086def list_VDI_records_in_sr(sr): 

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

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

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

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

1091 return vdis 

1092 

1093 

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

1095def diskFromPartition(partition): 

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

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

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

1099 return m.group(2) 

1100 

1101 numlen = 0 # number of digit characters 

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

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

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

1105 

1106 # is it a cciss? 

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

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

1109 

1110 # is it a mapper path? 

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

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

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

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

1115 else: 

1116 numlen = 0 

1117 

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

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

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

1121 

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

1123 

1124 

1125def dom0_disks(): 

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

1127 disks = [] 

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

1129 for line in f: 

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

1131 if mountpoint == '/': 

1132 disk = diskFromPartition(dev) 

1133 if not (disk in disks): 

1134 disks.append(disk) 

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

1136 return disks 

1137 

1138 

1139def set_scheduler_sysfs_node(node, scheds): 

1140 """ 

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

1142 according to prioritized list schedulers 

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

1144 """ 

1145 

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

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

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

1149 return 

1150 

1151 stored_error = None 

1152 for sched in scheds: 

1153 try: 

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

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

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

1157 return 

1158 except (OSError, IOError) as err: 

1159 stored_error = err 

1160 

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

1162 

1163 

1164def set_scheduler(dev, schedulers=None): 

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

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

1167 

1168 devices = [] 

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

1170 # Remove partition numbers 

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

1172 else: 

1173 rawdev = diskFromPartition(dev) 

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

1175 

1176 for d in devices: 

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

1178 

1179 

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

1181def _getVDIs(srobj): 

1182 VDIs = [] 

1183 try: 

1184 sr_ref = getattr(srobj, 'sr_ref') 

1185 except AttributeError: 

1186 return VDIs 

1187 

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

1189 for vdi in refs: 

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

1191 ref['vdi_ref'] = vdi 

1192 VDIs.append(ref) 

1193 return VDIs 

1194 

1195 

1196def _getVDI(srobj, vdi_uuid): 

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

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

1199 ref['vdi_ref'] = vdi 

1200 return ref 

1201 

1202 

1203def _convertDNS(name): 

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

1205 return addr 

1206 

1207 

1208def _containsVDIinuse(srobj): 

1209 VDIs = _getVDIs(srobj) 

1210 for vdi in VDIs: 

1211 if not vdi['managed']: 

1212 continue 

1213 sm_config = vdi['sm_config'] 

1214 if 'SRRef' in sm_config: 

1215 try: 

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

1217 for pbd in PBDs: 

1218 record = PBDs[pbd] 

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

1220 record["currently_attached"]: 

1221 return True 

1222 except: 

1223 pass 

1224 return False 

1225 

1226 

1227def isVDICommand(cmd): 

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

1229 "vdi_activate", "vdi_deactivate", 

1230 "vdi_epoch_begin", "vdi_epoch_end"]: 

1231 return True 

1232 else: 

1233 return False 

1234 

1235 

1236######################### 

1237# Daemon helper functions 

1238def p_id_fork(): 

1239 try: 

1240 p_id = os.fork() 

1241 except OSError as e: 

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

1243 sys.exit(-1) 

1244 

1245 if (p_id == 0): 

1246 os.setsid() 

1247 try: 

1248 p_id = os.fork() 

1249 except OSError as e: 

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

1251 sys.exit(-1) 

1252 if (p_id == 0): 

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

1254 os.umask(0) 

1255 else: 

1256 os._exit(0) 

1257 else: 

1258 os._exit(0) 

1259 

1260 

1261def daemon(): 

1262 p_id_fork() 

1263 # Query the max file descriptor parameter for this process 

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

1265 

1266 # Close any fds that are open 

1267 for fd in range(0, maxfd): 

1268 try: 

1269 os.close(fd) 

1270 except: 

1271 pass 

1272 

1273 # Redirect STDIN to STDOUT and STDERR 

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

1275 os.dup2(0, 1) 

1276 os.dup2(0, 2) 

1277 

1278################################################################################ 

1279# 

1280# Fist points 

1281# 

1282 

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

1284# 

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

1286# on the SR master; 

1287# 

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

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

1290# - otherwise, the function called is _pause. 

1291 

1292def _pause(secs, name): 

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

1294 time.sleep(secs) 

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

1296 

1297 

1298def _exit(name): 

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

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

1301 

1302 

1303class FistPoint: 

1304 def __init__(self, points): 

1305 #SMlog("Fist points loaded") 

1306 self.points = points 

1307 

1308 def is_legal(self, name): 

1309 return (name in self.points) 

1310 

1311 def is_active(self, name): 

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

1313 

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

1315 session = get_localAPI_session() 

1316 try: 

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

1318 

1319 if started: 

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

1321 else: 

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

1323 finally: 

1324 session.xenapi.session.logout() 

1325 

1326 def activate(self, name, sruuid): 

1327 if name in self.points: 

1328 if self.is_active(name): 

1329 self.mark_sr(name, sruuid, True) 

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

1331 self.mark_sr(name, sruuid, False) 

1332 _exit(name) 

1333 else: 

1334 _pause(FIST_PAUSE_PERIOD, name) 

1335 self.mark_sr(name, sruuid, False) 

1336 else: 

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

1338 

1339 def activate_custom_fn(self, name, fn): 

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

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

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

1343 fn() 

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

1345 else: 

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

1347 

1348 

1349def list_find(f, seq): 

1350 for item in seq: 

1351 if f(item): 

1352 return item 

1353 

1354GCPAUSE_FISTPOINT = "GCLoop_no_pause" 

1355 

1356fistpoint = FistPoint(["LVHDRT_finding_a_suitable_pair", 

1357 "LVHDRT_inflating_the_parent", 

1358 "LVHDRT_resizing_while_vdis_are_paused", 

1359 "LVHDRT_coalescing_VHD_data", 

1360 "LVHDRT_coalescing_before_inflate_grandparent", 

1361 "LVHDRT_relinking_grandchildren", 

1362 "LVHDRT_before_create_relink_journal", 

1363 "LVHDRT_xapiSM_serialization_tests", 

1364 "LVHDRT_clone_vdi_after_create_journal", 

1365 "LVHDRT_clone_vdi_after_shrink_parent", 

1366 "LVHDRT_clone_vdi_after_first_snap", 

1367 "LVHDRT_clone_vdi_after_second_snap", 

1368 "LVHDRT_clone_vdi_after_parent_hidden", 

1369 "LVHDRT_clone_vdi_after_parent_ro", 

1370 "LVHDRT_clone_vdi_before_remove_journal", 

1371 "LVHDRT_clone_vdi_after_lvcreate", 

1372 "LVHDRT_clone_vdi_before_undo_clone", 

1373 "LVHDRT_clone_vdi_after_undo_clone", 

1374 "LVHDRT_inflate_after_create_journal", 

1375 "LVHDRT_inflate_after_setSize", 

1376 "LVHDRT_inflate_after_zeroOut", 

1377 "LVHDRT_inflate_after_setSizePhys", 

1378 "LVHDRT_inflate_after_setSizePhys", 

1379 "LVHDRT_coaleaf_before_coalesce", 

1380 "LVHDRT_coaleaf_after_coalesce", 

1381 "LVHDRT_coaleaf_one_renamed", 

1382 "LVHDRT_coaleaf_both_renamed", 

1383 "LVHDRT_coaleaf_after_vdirec", 

1384 "LVHDRT_coaleaf_before_delete", 

1385 "LVHDRT_coaleaf_after_delete", 

1386 "LVHDRT_coaleaf_before_remove_j", 

1387 "LVHDRT_coaleaf_undo_after_rename", 

1388 "LVHDRT_coaleaf_undo_after_rename2", 

1389 "LVHDRT_coaleaf_undo_after_refcount", 

1390 "LVHDRT_coaleaf_undo_after_deflate", 

1391 "LVHDRT_coaleaf_undo_end", 

1392 "LVHDRT_coaleaf_stop_after_recovery", 

1393 "LVHDRT_coaleaf_finish_after_inflate", 

1394 "LVHDRT_coaleaf_finish_end", 

1395 "LVHDRT_coaleaf_delay_1", 

1396 "LVHDRT_coaleaf_delay_2", 

1397 "LVHDRT_coaleaf_delay_3", 

1398 "testsm_clone_allow_raw", 

1399 "xenrt_default_vdi_type_legacy", 

1400 "blktap_activate_inject_failure", 

1401 "blktap_activate_error_handling", 

1402 GCPAUSE_FISTPOINT, 

1403 "cleanup_coalesceVHD_inject_failure", 

1404 "cleanup_tracker_no_progress", 

1405 "FileSR_fail_hardlink", 

1406 "FileSR_fail_snap1", 

1407 "FileSR_fail_snap2", 

1408 "LVM_journaler_exists", 

1409 "LVM_journaler_none", 

1410 "LVM_journaler_badname", 

1411 "LVM_journaler_readfail", 

1412 "LVM_journaler_writefail"]) 

1413 

1414 

1415def set_dirty(session, sr): 

1416 try: 

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

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

1419 except: 

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

1421 

1422 

1423def doesFileHaveOpenHandles(fileName): 

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

1425 (retVal, processAndPidTuples) = \ 

1426 findRunningProcessOrOpenFile(fileName, False) 

1427 

1428 if not retVal: 

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

1430 fileName) 

1431 # err on the side of caution 

1432 return True 

1433 else: 

1434 if len(processAndPidTuples) > 0: 

1435 return True 

1436 else: 

1437 return False 

1438 

1439 

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

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

1442def extractSRFromDevMapper(path): 

1443 try: 

1444 path = os.path.basename(path) 

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

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

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

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

1449 except: 

1450 return '' 

1451 

1452 

1453def pid_is_alive(pid): 

1454 """ 

1455 Try to kill PID with signal 0. 

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

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

1458 signal it. Still return true. 

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

1460 """ 

1461 try: 

1462 os.kill(pid, 0) 

1463 return True 

1464 except OSError as e: 

1465 if e.errno == errno.EPERM: 

1466 return True 

1467 return False 

1468 

1469 

1470# Looks at /proc and figures either 

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

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

1473# returns process names and pids 

1474def findRunningProcessOrOpenFile(name, process=True): 

1475 retVal = True 

1476 links = [] 

1477 processandpids = [] 

1478 sockets = set() 

1479 try: 

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

1481 [name, process]) 

1482 

1483 # Look at all pids 

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

1485 for pid in sorted(pids): 

1486 try: 

1487 try: 

1488 f = None 

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

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

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

1492 # Just want the process name 

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

1494 prog = argv[0] 

1495 except IOError as e: 

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

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

1498 continue 

1499 finally: 

1500 if f is not None: 1500 ↛ 1485,   1500 ↛ 15032 missed branches: 1) line 1500 didn't jump to line 1485, because the continue on line 1498 wasn't executed, 2) line 1500 didn't jump to line 1503, because the condition on line 1500 was never false

1501 f.close() 1501 ↛ 1485line 1501 didn't jump to line 1485, because the continue on line 1498 wasn't executed

1502 

1503 try: 

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

1505 files = os.listdir(fd_dir) 

1506 except OSError as e: 

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

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

1509 # Ignore pid that are no longer valid 

1510 continue 

1511 else: 

1512 raise 

1513 

1514 for file in files: 

1515 try: 

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

1517 except OSError: 

1518 continue 

1519 

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

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

1522 links.append(link) 

1523 else: 

1524 # need to return process name and pid tuples 

1525 if link == name: 

1526 processandpids.append((prog, pid)) 

1527 

1528 # Get the connected sockets 

1529 if name == prog: 

1530 sockets.update(get_connected_sockets(pid)) 

1531 

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

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

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

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

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

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

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

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

1540 

1541 except Exception as e: 

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

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

1544 retVal = False 

1545 

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

1547 return retVal, links, sockets 

1548 else: 

1549 return retVal, processandpids 

1550 

1551 

1552def get_connected_sockets(pid): 

1553 sockets = set() 

1554 try: 

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

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

1557 # - Pointer address to socket (hex) 

1558 # - Refcount (HEX) 

1559 # - 0 

1560 # - State (HEX, 0 or __SO_ACCEPTCON) 

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

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

1563 # - Inode number 

1564 # - Path (optional) 

1565 open_sock_matcher = re.compile( 

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

1567 with open( 

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

1569 lines = f.readlines() 

1570 for line in lines: 

1571 match = open_sock_matcher.match(line) 

1572 if match: 

1573 sockets.add(match[1]) 

1574 except OSError as e: 

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

1576 # Ignore pid that are no longer valid 

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

1578 (e.errno, pid)) 

1579 else: 

1580 raise 

1581 return sockets 

1582 

1583 

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

1585 retries = 0 

1586 while True: 

1587 try: 

1588 return f() 

1589 except Exception as e: 

1590 for exception in exceptions: 

1591 if isinstance(e, exception): 

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

1593 str(e), retries 

1594 )) 

1595 break 

1596 else: 

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

1598 raise e 

1599 

1600 retries += 1 

1601 if retries >= maxretry: 

1602 break 

1603 

1604 time.sleep(period) 

1605 

1606 return f() 

1607 

1608 

1609def getCslDevPath(svid): 

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

1611 if svid.startswith("NETAPP_"): 

1612 # special attention for NETAPP SVIDs 

1613 svid_parts = svid.split("__") 

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

1615 else: 

1616 globstr = basepath + svid + "*" 

1617 

1618 return globstr 

1619 

1620 

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

1622def get_scsiid_from_svid(md_svid): 

1623 cslg_path = getCslDevPath(md_svid) 

1624 abs_path = glob.glob(cslg_path) 

1625 if abs_path: 

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

1627 return scsiutil.getSCSIid(real_path) 

1628 else: 

1629 return None 

1630 

1631 

1632def get_isl_scsiids(session): 

1633 # Get cslg type SRs 

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

1635 

1636 # Iterate through the SR to get the scsi ids 

1637 scsi_id_ret = [] 

1638 for SR in SRs: 

1639 sr_rec = SRs[SR] 

1640 # Use the md_svid to get the scsi id 

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

1642 if scsi_id: 

1643 scsi_id_ret.append(scsi_id) 

1644 

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

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

1647 for vdi_rec in vdi_recs: 

1648 vdi_rec = vdi_recs[vdi_rec] 

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

1650 if scsi_id: 

1651 scsi_id_ret.append(scsi_id) 

1652 

1653 return scsi_id_ret 

1654 

1655 

1656class extractXVA: 

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

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

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

1660 HDR_SIZE = 512 

1661 BLOCK_SIZE = 512 

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

1663 SIZE_OFFSET = 124 

1664 ZERO_FILLED_REC = 2 

1665 NULL_IDEN = '\x00' 

1666 DIR_IDEN = '/' 

1667 CHECKSUM_IDEN = '.checksum' 

1668 OVA_FILE = 'ova.xml' 

1669 

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

1671 # as and when needed 

1672 def __init__(self, filename): 

1673 self.__extract_path = '' 

1674 self.__filename = filename 

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

1676 try: 

1677 self.spawn_p = subprocess.Popen( 

1678 cmd, shell=True, \ 

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

1680 stderr=subprocess.PIPE, close_fds=True) 

1681 except Exception as e: 

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

1683 raise Exception(str(e)) 

1684 

1685 # Create dir to extract the files 

1686 self.__extract_path = tempfile.mkdtemp() 

1687 

1688 def __del__(self): 

1689 shutil.rmtree(self.__extract_path) 

1690 

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

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

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

1694 def getTuple(self): 

1695 zerod_record = 0 

1696 ret_f_name = '' 

1697 ret_base_f_name = '' 

1698 

1699 try: 

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

1701 while True: 

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

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

1704 

1705 # Break out in case of end of file 

1706 if f_hdr == '': 

1707 if zerod_record == extractXVA.ZERO_FILLED_REC: 

1708 break 

1709 else: 

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

1711 extractXVA.ZERO_FILLED_REC) 

1712 raise Exception('Unrecognized end of file') 

1713 

1714 # Watch out for zero records, two zero records 

1715 # denote end of file. 

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

1717 zerod_record += 1 

1718 continue 

1719 

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

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

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

1723 f_size_octal = f_hdr[extractXVA.SIZE_OFFSET: \ 

1724 extractXVA.SIZE_OFFSET + extractXVA.SIZE_LEN] 

1725 f_size = int(f_size_octal, 8) 

1726 if f_name.endswith(extractXVA.CHECKSUM_IDEN): 

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

1728 ret_base_f_name: 

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

1730 yield(ret_f_name, checksum) 

1731 else: 

1732 # Expects file followed by its checksum 

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

1734 ret_f_name) 

1735 raise Exception( \ 

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

1737 ret_f_name) 

1738 else: 

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

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

1741 # read the contents into a file, it will 

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

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

1744 ret_base_f_name = f_name 

1745 

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

1747 # else create it. 

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

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

1750 os.mkdir(folder_path) 

1751 

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

1753 f = open(ret_f_name, 'w') 

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

1755 f.close() 

1756 if f_name == extractXVA.OVA_FILE: 

1757 yield(ret_f_name, '') 

1758 

1759 # Skip zero'd portion of data block 

1760 round_off = f_size % extractXVA.BLOCK_SIZE 

1761 if round_off != 0: 

1762 zeros = self.spawn_p.stdout.read( 

1763 extractXVA.BLOCK_SIZE - round_off) 

1764 except Exception as e: 

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

1766 self.__filename)) 

1767 

1768 # Kill and Drain stdout of the gunzip process, 

1769 # else gunzip might block on stdout 

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

1771 self.spawn_p.communicate() 

1772 raise Exception(str(e)) 

1773 

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

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

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

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

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

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

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

1781 (0x10FFFE, 0x10FFFF)] 

1782 

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

1784 for (low, high) in illegal_xml_chars 

1785 if low < sys.maxunicode] 

1786 

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

1788 

1789 

1790def isLegalXMLString(s): 

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

1792 illegal XML characters specified in 

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

1794 """ 

1795 

1796 if len(s) > 0: 

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

1798 else: 

1799 return True 

1800 

1801 

1802def unictrunc(string, max_bytes): 

1803 """ 

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

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

1806 more than the given number of bytes. 

1807 

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

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

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

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

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

1813 

1814 string: the string to truncate 

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

1816 """ 

1817 if isinstance(string, str): 

1818 return_chars = True 

1819 else: 

1820 return_chars = False 

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

1822 

1823 cur_chars = 0 

1824 cur_bytes = 0 

1825 for char in string: 

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

1827 if cur_bytes + charsize > max_bytes: 

1828 break 

1829 else: 

1830 cur_chars += 1 

1831 cur_bytes += charsize 

1832 return cur_chars if return_chars else cur_bytes 

1833 

1834 

1835def hideValuesInPropMap(propmap, propnames): 

1836 """ 

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

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

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

1840 value and return the altered map. 

1841 If none found, return the original map 

1842 """ 

1843 matches = [] 

1844 for propname in propnames: 

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

1846 matches.append(propname) 

1847 

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

1849 deepCopyRec = copy.deepcopy(propmap) 

1850 for match in matches: 

1851 deepCopyRec[match] = '******' 

1852 return deepCopyRec 

1853 

1854 return propmap 

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

1856 

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

1858DEFAULT_SEGMENT_LEN = 950 

1859 

1860 

1861def hidePasswdInConfig(config): 

1862 """ 

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

1864 for example "device_config" 

1865 """ 

1866 return hideValuesInPropMap(config, PASSWD_PROP_KEYS) 

1867 

1868 

1869def hidePasswdInParams(params, configProp): 

1870 """ 

1871 Function to hide password values in a specified property which 

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

1873 in a larger property map. 

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

1875 "sm_config", etc 

1876 """ 

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

1878 return params 

1879 

1880 

1881def hideMemberValuesInXmlParams(xmlParams, propnames=PASSWD_PROP_KEYS): 

1882 """ 

1883 Function to hide password values in XML params, specifically 

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

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

1886 whose values we want to hide, and: 

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

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

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

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

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

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

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

1894 """ 

1895 findStrPrefixHead = "<member><name>" 

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

1897 findStrSuffix = "</value>" 

1898 strlen = len(xmlParams) 

1899 

1900 for propname in propnames: 

1901 findStrPrefix = findStrPrefixHead + propname + findStrPrefixTail 

1902 idx = xmlParams.find(findStrPrefix) 

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

1904 idx += len(findStrPrefix) 

1905 idx2 = xmlParams.find(findStrSuffix, idx) 

1906 if idx2 != -1: 

1907 retStr = xmlParams[0:idx] 

1908 retStr += "******" 

1909 retStr += xmlParams[idx2:strlen] 

1910 return retStr 

1911 else: 

1912 return xmlParams 

1913 return xmlParams 

1914 

1915 

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

1917 """ 

1918 Split xml string data into substrings small enough for the 

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

1920 Usage: 

1921 strList = [] 

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

1923 """ 

1924 remainingData = str(xmlData) 

1925 

1926 # "Un-pretty-print" 

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

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

1929 

1930 remainingChars = len(remainingData) 

1931 returnData = '' 

1932 

1933 thisLineNum = 0 

1934 while remainingChars > segmentLen: 

1935 thisLineNum = thisLineNum + 1 

1936 index = segmentLen 

1937 tmpStr = remainingData[:segmentLen] 

1938 tmpIndex = tmpStr.rfind('>') 

1939 if tmpIndex != -1: 

1940 index = tmpIndex + 1 

1941 

1942 tmpStr = tmpStr[:index] 

1943 remainingData = remainingData[index:] 

1944 remainingChars = len(remainingData) 

1945 

1946 if showContd: 

1947 if thisLineNum != 1: 

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

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

1950 

1951 returnData += tmpStr + '\n' 

1952 

1953 if showContd and thisLineNum > 0: 

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

1955 returnData += remainingData 

1956 

1957 return returnData 

1958 

1959 

1960def inject_failure(): 

1961 raise Exception('injected failure') 

1962 

1963 

1964def open_atomic(path, mode=None): 

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

1966 Leaves the file open and returns the file object. 

1967 

1968 path: the path to atomically open 

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

1970 returns: an open file object""" 

1971 

1972 assert path 

1973 

1974 flags = os.O_CREAT | os.O_EXCL 

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

1976 if mode: 

1977 if mode not in modes: 

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

1979 flags |= modes[mode] 

1980 fd = os.open(path, flags) 

1981 try: 

1982 if mode: 

1983 return os.fdopen(fd, mode) 

1984 else: 

1985 return os.fdopen(fd) 

1986 except: 

1987 os.close(fd) 

1988 raise 

1989 

1990 

1991def isInvalidVDI(exception): 

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

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

1994 

1995 

1996def get_pool_restrictions(session): 

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

1998 established.""" 

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

2000 

2001 

2002def read_caching_is_restricted(session): 

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

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

2005 return True 

2006 restrictions = get_pool_restrictions(session) 

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

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

2009 return True 

2010 return False 

2011 

2012 

2013def sessions_less_than_targets(other_config, device_config): 

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

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

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

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

2018 return (sessions < targets) 

2019 else: 

2020 return False 

2021 

2022 

2023def enable_and_start_service(name, start): 

2024 attempt = 0 

2025 while True: 

2026 attempt += 1 

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

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

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

2030 if ret == 0: 

2031 return 

2032 elif attempt >= 3: 

2033 raise Exception( 

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

2035 ) 

2036 time.sleep(1) 

2037 

2038 

2039def stop_service(name): 

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

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

2042 if ret == 0: 

2043 return 

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

2045 

2046 

2047def restart_service(name): 

2048 attempt = 0 

2049 while True: 

2050 attempt += 1 

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

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

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

2054 if ret == 0: 

2055 return 

2056 elif attempt >= 3: 

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

2058 raise Exception( 

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

2060 ) 

2061 time.sleep(1) 

2062 

2063 

2064def check_pid_exists(pid): 

2065 try: 

2066 os.kill(pid, 0) 

2067 except OSError: 

2068 return False 

2069 else: 

2070 return True 

2071 

2072 

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

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

2075 

2076 try: 

2077 list = [] 

2078 ret = pread2(cmd) 

2079 for line in ret.splitlines(): 

2080 list.append(int(line)) 

2081 return list 

2082 except CommandException as e: 

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

2084 return None 

2085 else: 

2086 raise e 

2087 

2088 

2089def make_profile(name, function): 

2090 """ 

2091 Helper to execute cProfile using unique log file. 

2092 """ 

2093 

2094 import cProfile 

2095 import itertools 

2096 import os.path 

2097 import time 

2098 

2099 assert name 

2100 assert function 

2101 

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

2103 makedirs(FOLDER) 

2104 

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

2106 

2107 def gen_path(path): 

2108 yield path 

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

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

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

2112 

2113 for profile_path in gen_path(FOLDER + filename): 

2114 try: 

2115 file = open_atomic(profile_path, 'w') 

2116 file.close() 

2117 break 

2118 except OSError as e: 

2119 if e.errno == errno.EEXIST: 

2120 pass 

2121 else: 

2122 raise 

2123 

2124 try: 

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

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

2127 finally: 

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

2129 

2130 

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

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

2133 # There is not alternative for strtobool. 

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

2135 # So this is a custom implementation with differences: 

2136 # - A boolean is returned instead of integer 

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

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

2139 return False 

2140 str = str.lower() 

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

2142 return True 

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

2144 return False 

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

2146 

2147 

2148def find_executable(name): 

2149 return shutil.which(name) 

2150 

2151 

2152def conditional_decorator(decorator, condition): 

2153 def wrapper(func): 

2154 if not condition: 

2155 return func 

2156 return decorator(func) 

2157 return wrapper