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# 

20 

21import XenAPI # pylint: disable=import-error 

22import sys 

23import xs_errors 

24import xmlrpc.client 

25import SR 

26import util 

27import blktap2 

28import resetvdis 

29import os 

30 

31NEEDS_VDI_OBJECT = [ 

32 "vdi_update", "vdi_create", "vdi_delete", "vdi_snapshot", "vdi_clone", 

33 "vdi_resize", "vdi_resize_online", "vdi_attach", "vdi_detach", 

34 "vdi_activate", "vdi_deactivate", "vdi_attach_from_config", "vdi_detach_from_config", 

35 "vdi_generate_config", "vdi_compose", "vdi_epoch_begin", 

36 "vdi_epoch_end", "vdi_enable_cbt", "vdi_disable_cbt", "vdi_data_destroy", 

37 "vdi_list_changed_blocks"] 

38 

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

40NO_LOGGING = { 

41 "iso": ["sr_scan"], 

42 "nfs_iso": ["sr_scan"] 

43} 

44 

45EXCEPTION_TYPE = { 

46 "sr_scan": "SRScan", 

47 "vdi_init": "VDILoad", 

48 "vdi_create": "VDICreate", 

49 "vdi_delete": "VDIDelete", 

50 "vdi_attach": "VDIUnavailable", 

51 "vdi_detach": "VDIUnavailable", 

52 "vdi_activate": "VDIUnavailable", 

53 "vdi_deactivate": "VDIUnavailable", 

54 "vdi_resize": "VDIResize", 

55 "vdi_resize_online": "VDIResize", 

56 "vdi_snapshot": "VDISnapshot", 

57 "vdi_clone": "VDIClone" 

58} 

59 

60 

61class SRCommand: 

62 def __init__(self, driver_info): 

63 self.dconf = '' 

64 self.type = '' 

65 self.sr_uuid = '' 

66 self.cmdname = '' 

67 self.cmdtype = '' 

68 self.cmd = None 

69 self.args = None 

70 self.driver_info = driver_info 

71 

72 def parse(self): 

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

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

75 raise xs_errors.XenError('BadRequest') 

76 

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

78 # util.SMlog( "" ) 

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

80 # ( sys.argv[0], \ 

81 # util.splitXmlText( util.hideMemberValuesInXmlParams( \ 

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

83 # priority=util.LOG_DEBUG ) 

84 

85 try: 

86 params, methodname = xmlrpc.client.loads(sys.argv[1]) 

87 self.cmd = methodname 

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

89 self.params = params 

90 

91 # params is a dictionary 

92 self.dconf = params['device_config'] 

93 if 'sr_uuid' in params: 93 ↛ 94line 93 didn't jump to line 94, because the condition on line 93 was never true

94 self.sr_uuid = params['sr_uuid'] 

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

96 self.vdi_uuid = params['vdi_uuid'] 

97 elif self.cmd == "vdi_create": 97 ↛ 98line 97 didn't jump to line 98, because the condition on line 97 was never true

98 self.vdi_uuid = util.gen_uuid() 

99 

100 except Exception as e: 

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

102 raise xs_errors.XenError('BadRequest') 

103 

104 def run_statics(self): 

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

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

107 sys.exit(0) 

108 

109 def run(self, sr): 

110 try: 

111 return self._run_locked(sr) 

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

113 util.logException(self.cmd) 

114 msg = str(e) 

115 if isinstance(e, util.CommandException): 

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

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

118 excType = EXCEPTION_TYPE.get(self.cmd) 

119 if not excType: 

120 excType = "SMGeneral" 

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

122 

123 except blktap2.TapdiskFailed as e: 

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

125 raise xs_errors.XenError('TapdiskFailed', 

126 os.strerror( 

127 e.get_error().get_error_code())) 

128 

129 except blktap2.TapdiskExists as e: 

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

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

132 

133 except: 

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

135 raise 

136 

137 def _run_locked(self, sr): 

138 lockSR = False 

139 lockInitOnly = False 

140 rv = None 

141 e = None 

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

143 lockSR = True 

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

145 lockInitOnly = True 

146 

147 target = None 

148 acquired = False 

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

150 sr.lock.acquire() 

151 acquired = True 

152 try: 

153 try: 

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

155 target = sr.vdi(self.vdi_uuid) 

156 finally: 

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

158 sr.lock.release() 

159 acquired = False 

160 try: 

161 rv = self._run(sr, target) 

162 except Exception as exc: 

163 e = exc 

164 raise 

165 finally: 

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

167 sr.lock.release() 

168 try: 

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

170 except Exception as e1: 

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

172 if not e: 

173 util.SMlog(msg) 

174 raise e1 

175 else: 

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

177 return rv 

178 

179 def _run(self, sr, target): 

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

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

182 self.cmd not in NO_LOGGING[dconf_type]: 

183 if 'device_config' in self.params: 183 ↛ 188line 183 didn't jump to line 188, because the condition on line 183 was never false

184 util.SMlog("%s %s" % ( 

185 self.cmd, util.hidePasswdInParams( 

186 self.params, 'device_config'))) 

187 else: 

188 util.SMlog("%s %s" % (self.cmd, repr(self.params))) 

189 

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

191 [blktap2.VDI.CONF_KEY_ALLOW_CACHING, 

192 blktap2.VDI.CONF_KEY_MODE_ON_BOOT, 

193 blktap2.VDI.CONF_KEY_CACHE_SR, 

194 blktap2.VDI.CONF_KEY_O_DIRECT]) 

195 

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

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

198 # commandline: 

199 

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

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

202 # description contain such characters. We must enforce this 

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

204 # characters) in order to be consistent. 

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

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

207 

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

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

210 raise xs_errors.XenError('IllegalXMLChar', 

211 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.') 

212 

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

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

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

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

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

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

219 

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

221 

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

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

224 # above. 

225 

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

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

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

229 

230 if not util.isLegalXMLString(name_label) \ 

231 or not util.isLegalXMLString(description): 

232 raise xs_errors.XenError('IllegalXMLChar', 

233 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.') 

234 

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

236 

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

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

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

240 

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

242 if 'VDI_CONFIG_CBT' in util.sr_get_capability( 

243 self.params['sr_uuid']): 

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

245 self.vdi_uuid, data_only=False) 

246 else: 

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

248 

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

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

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

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

253 writable, caching_params=caching_params) 

254 

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

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

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

258 

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

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

261 

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

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

264 

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

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

267 

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

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

270 

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

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

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

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

275 writable, caching_params) 

276 

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

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

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

280 caching_params) 

281 

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

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

284 return 

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

286 raise xs_errors.XenError('Unimplemented') 

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

288 

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

290 return 

291 

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

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

294 

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

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

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

298 

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

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

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

302 return ret 

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

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

305 

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

307 extras = {} 

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

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

310 extras['deactivate'] = True 

311 extras['caching_params'] = caching_params 

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

313 

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

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

316 self.vdi_uuid, True) 

317 

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

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

320 self.vdi_uuid, False) 

321 

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

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

324 

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

326 return target.list_changed_blocks() 

327 

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

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

330 

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

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

333 

334 elif self.cmd == 'sr_update': 

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

336 

337 elif self.cmd == 'sr_probe': 

338 txt = sr.probe() 

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

340 # return the XML document as a string 

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

342 

343 elif self.cmd == 'sr_attach': 

344 is_master = False 

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

346 is_master = True 

347 

348 sr_uuid = self.params['sr_uuid'] 

349 

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

351 sr_uuid, is_master) 

352 

353 if is_master: 

354 # Schedule a scan only when attaching on the SRmaster 

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

356 

357 try: 

358 return sr.attach(sr_uuid) 

359 finally: 

360 if is_master: 

361 sr.after_master_attach(sr_uuid) 

362 

363 elif self.cmd == 'sr_detach': 

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

365 

366 elif self.cmd == 'sr_content_type': 

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

368 

369 elif self.cmd == 'sr_scan': 

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

371 

372 else: 

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

374 raise xs_errors.XenError('BadRequest') 

375 

376 

377def run(driver, driver_info): 

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

379 cmd = SRCommand(driver_info) 

380 try: 

381 cmd.parse() 

382 cmd.run_statics() 

383 sr = driver(cmd, cmd.sr_uuid) 

384 sr.direct = True 

385 ret = cmd.run(sr) 

386 

387 if ret is None: 

388 print(util.return_nil()) 

389 else: 

390 print(ret) 

391 

392 except (Exception, SR.SRException) as e: 

393 try: 

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

395 except KeyError: 

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

397 except: 

398 pass 

399 

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

401 # If generic python Exception, print a generic xmlrpclib 

402 # dump and pass to XAPI. 

403 if isinstance(e, SR.SRException): 

404 print(e.toxml()) 

405 else: 

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

407 

408 sys.exit(0)