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#!/usr/bin/python3 

2# 

3# Copyright (C) Citrix Systems Inc. 

4# 

5# This program is free software; you can redistribute it and/or modify 

6# it under the terms of the GNU Lesser General Public License as published 

7# by the Free Software Foundation; version 2.1 only. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

14# You should have received a copy of the GNU Lesser General Public License 

15# along with this program; if not, write to the Free Software Foundation, Inc., 

16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

17# 

18# SRCommand: parse SR command-line objects 

19# 

20import traceback 

21 

22import XenAPI # pylint: disable=import-error 

23import sys 

24import xs_errors 

25import xmlrpc.client 

26from xmlrpc.client import ProtocolError 

27import SR 

28import util 

29import blktap2 

30import resetvdis 

31import os 

32 

33NEEDS_VDI_OBJECT = [ 

34 "vdi_update", "vdi_create", "vdi_delete", "vdi_snapshot", "vdi_clone", 

35 "vdi_resize", "vdi_resize_online", "vdi_attach", "vdi_detach", 

36 "vdi_activate", "vdi_deactivate", "vdi_attach_from_config", "vdi_detach_from_config", 

37 "vdi_generate_config", "vdi_compose", "vdi_epoch_begin", 

38 "vdi_epoch_end", "vdi_enable_cbt", "vdi_disable_cbt", "vdi_data_destroy", 

39 "vdi_list_changed_blocks"] 

40 

41# don't log the commands that spam the log file too much 

42NO_LOGGING = { 

43 "iso": ["sr_scan"], 

44 "nfs_iso": ["sr_scan"] 

45} 

46 

47EXCEPTION_TYPE = { 

48 "sr_scan": "SRScan", 

49 "vdi_init": "VDILoad", 

50 "vdi_create": "VDICreate", 

51 "vdi_delete": "VDIDelete", 

52 "vdi_attach": "VDIUnavailable", 

53 "vdi_detach": "VDIUnavailable", 

54 "vdi_activate": "VDIUnavailable", 

55 "vdi_deactivate": "VDIUnavailable", 

56 "vdi_resize": "VDIResize", 

57 "vdi_resize_online": "VDIResize", 

58 "vdi_snapshot": "VDISnapshot", 

59 "vdi_clone": "VDIClone" 

60} 

61 

62 

63class SRCommand: 

64 def __init__(self, driver_info): 

65 self.dconf = '' 

66 self.type = '' 

67 self.sr_uuid = '' 

68 self.cmdname = '' 

69 self.cmdtype = '' 

70 self.cmd = None 

71 self.args = None 

72 self.driver_info = driver_info 

73 

74 def parse(self): 

75 if len(sys.argv) != 2: 75 ↛ 76line 75 didn't jump to line 76, because the condition on line 75 was never true

76 util.SMlog("Failed to parse commandline; wrong number of arguments; argv = %s" % (repr(sys.argv))) 

77 raise xs_errors.XenError('BadRequest') 

78 

79 # Debug logging of the actual incoming command from the caller. 

80 # util.SMlog( "" ) 

81 # util.SMlog( "SM.parse: DEBUG: args = %s,\n%s" % \ 

82 # ( sys.argv[0], \ 

83 # util.splitXmlText( util.hideMemberValuesInXmlParams( \ 

84 # sys.argv[1] ), showContd=True ) ), \ 

85 # priority=util.LOG_DEBUG ) 

86 

87 try: 

88 params, methodname = xmlrpc.client.loads(os.fsencode(sys.argv[1])) 

89 self.cmd = methodname 

90 params = params[0] # expect a single struct 

91 self.params = params 

92 

93 # params is a dictionary 

94 self.dconf = params['device_config'] 

95 if 'sr_uuid' in params: 

96 self.sr_uuid = params['sr_uuid'] 

97 if 'vdi_uuid' in params: 97 ↛ 98line 97 didn't jump to line 98, because the condition on line 97 was never true

98 self.vdi_uuid = params['vdi_uuid'] 

99 elif self.cmd == "vdi_create": 

100 self.vdi_uuid = util.gen_uuid() 

101 

102 except Exception as e: 

103 util.SMlog("Failed to parse commandline; exception = %s argv = %s" % (str(e), repr(sys.argv))) 

104 raise xs_errors.XenError('BadRequest') 

105 

106 def run_statics(self): 

107 if self.params['command'] == 'sr_get_driver_info': 

108 print(util.sr_get_driver_info(self.driver_info)) 

109 sys.exit(0) 

110 

111 def run(self, sr): 

112 try: 

113 return self._run_locked(sr) 

114 except (util.CommandException, util.SMException, XenAPI.Failure) as e: 

115 util.logException(self.cmd) 

116 msg = str(e) 

117 if isinstance(e, util.CommandException): 

118 msg = "Command %s failed (%s): %s" % \ 

119 (e.cmd, e.reason, os.strerror(abs(e.code))) 

120 excType = EXCEPTION_TYPE.get(self.cmd) 

121 if not excType: 

122 excType = "SMGeneral" 

123 raise xs_errors.XenError(excType, opterr=msg) 

124 

125 except blktap2.TapdiskFailed as e: 

126 util.logException('tapdisk failed exception: %s' % e) 

127 raise xs_errors.XenError('TapdiskFailed', 

128 os.strerror( 

129 e.get_error().get_error_code())) 

130 

131 except blktap2.TapdiskExists as e: 

132 util.logException('tapdisk exists exception: %s' % e) 

133 raise xs_errors.XenError('TapdiskAlreadyRunning', e.__str__()) 

134 

135 except: 

136 util.logException('generic exception: %s' % self.cmd) 

137 raise 

138 

139 def _run_locked(self, sr): 

140 lockSR = False 

141 lockInitOnly = False 

142 rv = None 

143 e = None 

144 if self.cmd in sr.ops_exclusive: 144 ↛ 146line 144 didn't jump to line 146, because the condition on line 144 was never false

145 lockSR = True 

146 elif self.cmd in NEEDS_VDI_OBJECT and "vdi_init" in sr.ops_exclusive: 

147 lockInitOnly = True 

148 

149 target = None 

150 acquired = False 

151 if lockSR or lockInitOnly: 151 ↛ 154line 151 didn't jump to line 154, because the condition on line 151 was never false

152 sr.lock.acquire() 

153 acquired = True 

154 try: 

155 try: 

156 if self.cmd in NEEDS_VDI_OBJECT: 156 ↛ 157line 156 didn't jump to line 157, because the condition on line 156 was never true

157 target = sr.vdi(self.vdi_uuid) 

158 finally: 

159 if acquired and lockInitOnly: 159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true

160 sr.lock.release() 

161 acquired = False 

162 try: 

163 rv = self._run(sr, target) 

164 except Exception as exc: 

165 e = exc 

166 raise 

167 finally: 

168 if acquired: 168 ↛ 170line 168 didn't jump to line 170, because the condition on line 168 was never false

169 sr.lock.release() 

170 try: 

171 sr.cleanup() 171 ↛ exitline 171 didn't except from function '_run_locked', because the raise on line 166 wasn't executed

172 except Exception as e1: 

173 msg = 'failed to clean up SR: %s' % e1 

174 if not e: 

175 util.SMlog(msg) 

176 raise e1 

177 else: 

178 util.SMlog('WARNING: %s (error ignored)' % msg) 

179 return rv 

180 

181 def _run(self, sr, target): 

182 dconf_type = sr.dconf.get("type") 

183 if not dconf_type or not NO_LOGGING.get(dconf_type) or \ 183 ↛ 196line 183 didn't jump to line 196, because the condition on line 183 was never false

184 self.cmd not in NO_LOGGING[dconf_type]: 

185 params_to_log = self.params 

186 

187 if 'device_config' in params_to_log: 187 ↛ 191line 187 didn't jump to line 191, because the condition on line 187 was never false

188 params_to_log = util.hidePasswdInParams( 

189 self.params, 'device_config') 

190 

191 if 'session_ref' in params_to_log: 191 ↛ 194line 191 didn't jump to line 194, because the condition on line 191 was never false

192 params_to_log['session_ref'] = '******' 

193 

194 util.SMlog("%s %s" % (self.cmd, repr(params_to_log))) 

195 

196 caching_params = dict((k, self.params.get(k)) for k in 

197 [blktap2.VDI.CONF_KEY_ALLOW_CACHING, 

198 blktap2.VDI.CONF_KEY_MODE_ON_BOOT, 

199 blktap2.VDI.CONF_KEY_CACHE_SR, 

200 blktap2.VDI.CONF_KEY_O_DIRECT]) 

201 

202 if self.cmd == 'vdi_create': 202 ↛ 211line 202 didn't jump to line 211, because the condition on line 202 was never true

203 # These are the fields owned by the backend, passed on the 

204 # commandline: 

205 

206 # LVM SRs store their metadata in XML format. XML does not support 

207 # all unicode characters, so we must check if the label or the 

208 # description contain such characters. We must enforce this 

209 # restriction to other SRs as well (even if they do allow these 

210 # characters) in order to be consistent. 

211 target.label = self.params['args'][1] 

212 target.description = self.params['args'][2] 

213 

214 if not util.isLegalXMLString(target.label) \ 

215 or not util.isLegalXMLString(target.description): 

216 raise xs_errors.XenError('IllegalXMLChar', 

217 opterr='The name and/or description you supplied contains one or more unsupported characters. The name and/or description must contain valid XML characters. See http://www.w3.org/TR/2004/REC-xml-20040204/#charsets for more information.') 

218 

219 target.ty = self.params['vdi_type'] 

220 target.metadata_of_pool = self.params['args'][3] 

221 target.is_a_snapshot = self.params['args'][4] == "true" 

222 target.snapshot_time = self.params['args'][5] 

223 target.snapshot_of = self.params['args'][6] 

224 target.read_only = self.params['args'][7] == "true" 

225 

226 return target.create(self.params['sr_uuid'], self.vdi_uuid, int(self.params['args'][0])) 

227 

228 elif self.cmd == 'vdi_update': 228 ↛ 232line 228 didn't jump to line 232, because the condition on line 228 was never true

229 # Check for invalid XML characters, similar to VDI.create right 

230 # above. 

231 

232 vdi_ref = sr.session.xenapi.VDI.get_by_uuid(self.vdi_uuid) 

233 name_label = sr.session.xenapi.VDI.get_name_label(vdi_ref) 

234 description = sr.session.xenapi.VDI.get_name_description(vdi_ref) 

235 

236 if not util.isLegalXMLString(name_label) \ 

237 or not util.isLegalXMLString(description): 

238 raise xs_errors.XenError('IllegalXMLChar', 

239 opterr='The name and/or description you supplied contains one or more unsupported characters. The name and/or description must contain valid XML characters. See http://www.w3.org/TR/2004/REC-xml-20040204/#charsets for more information.') 

240 

241 return target.update(self.params['sr_uuid'], self.vdi_uuid) 

242 

243 elif self.cmd == 'vdi_introduce': 243 ↛ 244line 243 didn't jump to line 244, because the condition on line 243 was never true

244 target = sr.vdi(self.params['new_uuid']) 

245 return target.introduce(self.params['sr_uuid'], self.params['new_uuid']) 

246 

247 elif self.cmd == 'vdi_delete': 247 ↛ 248line 247 didn't jump to line 248, because the condition on line 247 was never true

248 if 'VDI_CONFIG_CBT' in util.sr_get_capability( 

249 self.params['sr_uuid'], 

250 session=sr.session): 

251 return target.delete(self.params['sr_uuid'], 

252 self.vdi_uuid, data_only=False) 

253 else: 

254 return target.delete(self.params['sr_uuid'], self.vdi_uuid) 

255 

256 elif self.cmd == 'vdi_attach': 256 ↛ 257line 256 didn't jump to line 257, because the condition on line 256 was never true

257 target = blktap2.VDI(self.vdi_uuid, target, self.driver_info) 

258 writable = self.params['args'][0] == 'true' 

259 return target.attach(self.params['sr_uuid'], self.vdi_uuid, 

260 writable, caching_params=caching_params) 

261 

262 elif self.cmd == 'vdi_detach': 262 ↛ 263line 262 didn't jump to line 263, because the condition on line 262 was never true

263 target = blktap2.VDI(self.vdi_uuid, target, self.driver_info) 

264 return target.detach(self.params['sr_uuid'], self.vdi_uuid) 

265 

266 elif self.cmd == 'vdi_snapshot': 266 ↛ 267line 266 didn't jump to line 267, because the condition on line 266 was never true

267 return target.snapshot(self.params['sr_uuid'], self.vdi_uuid) 

268 

269 elif self.cmd == 'vdi_clone': 269 ↛ 270line 269 didn't jump to line 270, because the condition on line 269 was never true

270 return target.clone(self.params['sr_uuid'], self.vdi_uuid) 

271 

272 elif self.cmd == 'vdi_resize': 272 ↛ 273line 272 didn't jump to line 273, because the condition on line 272 was never true

273 return target.resize(self.params['sr_uuid'], self.vdi_uuid, int(self.params['args'][0])) 

274 

275 elif self.cmd == 'vdi_resize_online': 275 ↛ 276line 275 didn't jump to line 276, because the condition on line 275 was never true

276 return target.resize_online(self.params['sr_uuid'], self.vdi_uuid, int(self.params['args'][0])) 

277 

278 elif self.cmd == 'vdi_activate': 278 ↛ 279line 278 didn't jump to line 279, because the condition on line 278 was never true

279 target = blktap2.VDI(self.vdi_uuid, target, self.driver_info) 

280 writable = self.params['args'][0] == 'true' 

281 return target.activate(self.params['sr_uuid'], self.vdi_uuid, 

282 writable, caching_params) 

283 

284 elif self.cmd == 'vdi_deactivate': 284 ↛ 285line 284 didn't jump to line 285, because the condition on line 284 was never true

285 target = blktap2.VDI(self.vdi_uuid, target, self.driver_info) 

286 return target.deactivate(self.params['sr_uuid'], self.vdi_uuid, 

287 caching_params) 

288 

289 elif self.cmd == 'vdi_epoch_begin': 289 ↛ 290line 289 didn't jump to line 290, because the condition on line 289 was never true

290 if caching_params.get(blktap2.VDI.CONF_KEY_MODE_ON_BOOT) != "reset": 

291 return 

292 if "VDI_RESET_ON_BOOT/2" not in self.driver_info['capabilities']: 

293 raise xs_errors.XenError('Unimplemented') 

294 return target.reset_leaf(self.params['sr_uuid'], self.vdi_uuid) 

295 

296 elif self.cmd == 'vdi_epoch_end': 296 ↛ 297line 296 didn't jump to line 297, because the condition on line 296 was never true

297 return 

298 

299 elif self.cmd == 'vdi_generate_config': 299 ↛ 300line 299 didn't jump to line 300, because the condition on line 299 was never true

300 return target.generate_config(self.params['sr_uuid'], self.vdi_uuid) 

301 

302 elif self.cmd == 'vdi_compose': 302 ↛ 303line 302 didn't jump to line 303, because the condition on line 302 was never true

303 vdi1_uuid = sr.session.xenapi.VDI.get_uuid(self.params['args'][0]) 

304 return target.compose(self.params['sr_uuid'], vdi1_uuid, self.vdi_uuid) 

305 

306 elif self.cmd == 'vdi_attach_from_config': 306 ↛ 307line 306 didn't jump to line 307, because the condition on line 306 was never true

307 ret = target.attach_from_config(self.params['sr_uuid'], self.vdi_uuid) 

308 if not target.sr.driver_config.get("ATTACH_FROM_CONFIG_WITH_TAPDISK"): 

309 return ret 

310 target = blktap2.VDI(self.vdi_uuid, target, self.driver_info) 

311 return target.attach(self.params['sr_uuid'], self.vdi_uuid, True, True) 

312 

313 elif self.cmd == 'vdi_detach_from_config': 313 ↛ 314line 313 didn't jump to line 314, because the condition on line 313 was never true

314 extras = {} 

315 if target.sr.driver_config.get("ATTACH_FROM_CONFIG_WITH_TAPDISK"): 

316 target = blktap2.VDI(self.vdi_uuid, target, self.driver_info) 

317 extras['deactivate'] = True 

318 extras['caching_params'] = caching_params 

319 target.detach(self.params['sr_uuid'], self.vdi_uuid, ** extras) 

320 

321 elif self.cmd == 'vdi_enable_cbt': 321 ↛ 322line 321 didn't jump to line 322, because the condition on line 321 was never true

322 return target.configure_blocktracking(self.params['sr_uuid'], 

323 self.vdi_uuid, True) 

324 

325 elif self.cmd == 'vdi_disable_cbt': 325 ↛ 326line 325 didn't jump to line 326, because the condition on line 325 was never true

326 return target.configure_blocktracking(self.params['sr_uuid'], 

327 self.vdi_uuid, False) 

328 

329 elif self.cmd == 'vdi_data_destroy': 329 ↛ 330line 329 didn't jump to line 330, because the condition on line 329 was never true

330 return target.data_destroy(self.params['sr_uuid'], self.vdi_uuid) 

331 

332 elif self.cmd == 'vdi_list_changed_blocks': 332 ↛ 333line 332 didn't jump to line 333, because the condition on line 332 was never true

333 return target.list_changed_blocks() 

334 

335 elif self.cmd == 'sr_create': 335 ↛ 336line 335 didn't jump to line 336, because the condition on line 335 was never true

336 return sr.create(self.params['sr_uuid'], int(self.params['args'][0])) 

337 

338 elif self.cmd == 'sr_delete': 338 ↛ 341line 338 didn't jump to line 341, because the condition on line 338 was never false

339 return sr.delete(self.params['sr_uuid']) 

340 

341 elif self.cmd == 'sr_update': 

342 return sr.update(self.params['sr_uuid']) 

343 

344 elif self.cmd == 'sr_probe': 

345 txt = sr.probe() 

346 util.SMlog("sr_probe result: %s" % util.splitXmlText(txt, showContd=True)) 

347 # return the XML document as a string 

348 return xmlrpc.client.dumps((txt, ), "", True) 

349 

350 elif self.cmd == 'sr_attach': 

351 is_master = False 

352 if sr.dconf.get("SRmaster") == "true": 

353 is_master = True 

354 

355 sr_uuid = self.params['sr_uuid'] 

356 

357 resetvdis.reset_sr(sr.session, util.get_this_host(), 

358 sr_uuid, is_master) 

359 

360 if is_master: 

361 # Schedule a scan only when attaching on the SRmaster 

362 util.set_dirty(sr.session, self.params["sr_ref"]) 

363 

364 try: 

365 return sr.attach(sr_uuid) 

366 finally: 

367 if is_master: 

368 sr.after_master_attach(sr_uuid) 

369 

370 elif self.cmd == 'sr_detach': 

371 return sr.detach(self.params['sr_uuid']) 

372 

373 elif self.cmd == 'sr_content_type': 

374 return sr.content_type(self.params['sr_uuid']) 

375 

376 elif self.cmd == 'sr_scan': 

377 return sr.scan(self.params['sr_uuid']) 

378 

379 else: 

380 util.SMlog("Unknown command: %s" % self.cmd) 

381 raise xs_errors.XenError('BadRequest') 

382 

383 

384def run(driver, driver_info): 

385 """Convenience method to run command on the given driver""" 

386 cmd = SRCommand(driver_info) 

387 try: 

388 cmd.parse() 

389 cmd.run_statics() 

390 sr = driver(cmd, cmd.sr_uuid) 

391 sr.direct = True 

392 ret = cmd.run(sr) 

393 

394 if ret is None: 

395 print(util.return_nil()) 

396 else: 

397 print(ret) 

398 

399 except ProtocolError: 

400 # xs_errors.XenError.__new__ returns a different class 

401 # pylint: disable-msg=E1101 

402 exception_trace = traceback.format_exc() 

403 util.SMlog(f'XenAPI Protocol error {exception_trace}') 

404 print(xs_errors.XenError( 

405 'APIProtocolError', 

406 opterr=exception_trace).toxml()) 

407 except (Exception, xs_errors.SRException) as e: 

408 try: 

409 util.logException(driver_info['name']) 

410 except KeyError: 

411 util.SMlog('driver_info does not contain a \'name\' key.') 

412 except: 

413 pass 

414 

415 # If exception is of type SR.SRException, pass to Xapi. 

416 # If generic python Exception, print a generic xmlrpclib 

417 # dump and pass to XAPI. 

418 if isinstance(e, xs_errors.SRException): 

419 print(e.toxml()) 

420 else: 

421 print(xmlrpc.client.dumps(xmlrpc.client.Fault(1200, str(e)), "", True)) 

422 

423 sys.exit(0)