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# Pause/unpause tapdisk on the local host 

19 

20import os 

21import sys 

22import XenAPIPlugin 

23sys.path.append("/opt/xensource/sm/") 

24import blktap2, util 

25from lock import Lock 

26import xs_errors 

27import XenAPI 

28import lvmcache 

29import VDI 

30 

31from constants import NS_PREFIX_LVM, VG_PREFIX 

32from cowutil import getCowUtil 

33from lvmcowutil import LV_PREFIX, LV_PREFIX_TO_VDI_TYPE, LvmCowUtil 

34 

35try: 

36 from linstorcowutil import LinstorCowUtil 

37 from linstorvolumemanager import get_controller_uri, LinstorVolumeManager 

38 LINSTOR_AVAILABLE = True 

39except ImportError: 

40 LINSTOR_AVAILABLE = False 

41 

42TAPDEV_BACKPATH_PFX = "/dev/sm/backend" 

43TAPDEV_PHYPATH_PFX = "/dev/sm/phy" 

44 

45 

46def locking(excType, override=True): 

47 def locking2(op): 

48 def wrapper(self, *args): 

49 if self.failfast: 49 ↛ 50line 49 didn't jump to line 50, because the condition on line 49 was never true

50 if not self.lock.acquireNoblock(): 

51 raise xs_errors.XenError(excType, 

52 opterr='VDI already locked') 

53 else: 

54 self.lock.acquire() 

55 try: 

56 try: 

57 ret = op(self, *args) 

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

59 util.logException("TAP-PAUSE:%s" % op) 

60 msg = str(e) 

61 if isinstance(e, util.CommandException): 

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

63 (e.cmd, e.code, e.reason) 

64 if override: 

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

66 else: 

67 raise 

68 except: 

69 util.logException("TAP-PAUSE:%s" % op) 

70 raise 

71 finally: 

72 self.lock.release() 72 ↛ exitline 72 didn't except from function 'wrapper', because the raise on line 65 wasn't executed or the raise on line 67 wasn't executed or the raise on line 70 wasn't executed

73 return ret 

74 return wrapper 

75 return locking2 

76 

77def _getDevMajor_minor(dev): 

78 st = os.stat(dev) 

79 return [os.major(st.st_rdev),os.minor(st.st_rdev)] 

80 

81def _mkphylink(sr_uuid, vdi_uuid, path): 

82 sympath = "/dev/sm/phy/%s/%s" % (sr_uuid,vdi_uuid) 

83 cmd = ['ln', '-sf', path, sympath] 

84 util.pread2(cmd) 

85 return path 

86 

87def tapPause(session, args): 

88 tap = Tapdisk(session, args) 

89 return tap.Pause() 

90 

91def tapUnpause(session, args): 

92 tap = Tapdisk(session, args) 

93 return tap.Unpause() 

94 

95def tapRefresh(session, args): 

96 tap = Tapdisk(session, args) 

97 if tap.Pause() != "True": 97 ↛ 98line 97 didn't jump to line 98, because the condition on line 97 was never true

98 return str(False) 

99 return tap.Unpause() 

100 

101 

102class Tapdisk: 

103 def __init__(self, session, args): 

104 self.sr_uuid = args["sr_uuid"] 

105 self.vdi_uuid = args["vdi_uuid"] 

106 # Tells whether the lock must be acquired in a non-blocking manner. 

107 if 'failfast' in args: 107 ↛ 108line 107 didn't jump to line 108, because the condition on line 107 was never true

108 self.failfast = eval(args['failfast']) 

109 else: 

110 self.failfast = False 

111 self.session = session 

112 self.path = os.path.join(TAPDEV_BACKPATH_PFX,self.sr_uuid,self.vdi_uuid) 

113 self.phypath = os.path.join(TAPDEV_PHYPATH_PFX,self.sr_uuid,self.vdi_uuid) 

114 self.lock = Lock("vdi", self.vdi_uuid) 

115 self.realpath = None 

116 self.vdi_type = None 

117 self.secondary = None 

118 if "secondary" in args: 118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true

119 self.secondary = args["secondary"] 

120 self.activate_parents = False 

121 if args.get("activate_parents") == "true": 121 ↛ 122line 121 didn't jump to line 122, because the condition on line 121 was never true

122 self.activate_parents = True 

123 

124 def _pathRefresh(self): 

125 # LVM rename check 

126 try: 

127 realpath = os.readlink(self.phypath) 

128 except OSError as e: 

129 util.SMlog("Phypath %s does not exist" % self.phypath) 

130 return 

131 util.SMlog("Realpath: %s" % realpath) 

132 if realpath.startswith("/dev/VG_XenStorage-") and \ 132 ↛ 134line 132 didn't jump to line 134, because the condition on line 132 was never true

133 not os.path.exists(realpath): 

134 util.SMlog("Path inconsistent") 

135 pfx = "/dev/VG_XenStorage-%s/" % self.sr_uuid 

136 for ty in LV_PREFIX.values(): 

137 p = pfx + ty + self.vdi_uuid 

138 util.SMlog("Testing path: %s" % p) 

139 if os.path.exists(p): 

140 _mkphylink(self.sr_uuid, self.vdi_uuid, p) 

141 self.realpath = p 

142 self.vdi_type = LV_PREFIX_TO_VDI_TYPE[ty] 

143 elif realpath.startswith('/dev/drbd/by-res/xcp-volume-'): 143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true

144 if not LINSTOR_AVAILABLE: 

145 raise util.SMException( 

146 'Can\'t refresh tapdisk: LINSTOR libraries are missing' 

147 ) 

148 

149 # We must always recreate the symlink to ensure we have 

150 # the right info. Why? Because if the volume UUID is changed in 

151 # LINSTOR the symlink is not directly updated. When live leaf 

152 # coalesce is executed we have these steps: 

153 # "A" -> "OLD_A" 

154 # "B" -> "A" 

155 # Without symlink update the previous "A" path is reused instead of 

156 # "B" path. Note: "A", "B" and "OLD_A" are UUIDs. 

157 session = self.session 

158 

159 host_ref = util.get_this_host_ref(session) 

160 sr_ref = session.xenapi.SR.get_by_uuid(self.sr_uuid) 

161 

162 pbd = util.find_my_pbd(session, host_ref, sr_ref) 

163 if pbd is None: 

164 raise util.SMException('Failed to find PBD') 

165 

166 dconf = session.xenapi.PBD.get_device_config(pbd) 

167 group_name = dconf['group-name'] 

168 

169 linstor = LinstorVolumeManager( 

170 get_controller_uri(), 

171 group_name, 

172 logger=util.SMlog 

173 ) 

174 

175 import cleanup 

176 from srmetadata import VDI_TYPE_TAG 

177 

178 self.vdi_type = linstor.get_volume_metadata(self.vdi_uuid)[VDI_TYPE_TAG] 

179 chain = LinstorCowUtil(session, linstor, self.vdi_type).create_chain_paths( 

180 self.vdi_uuid, 

181 readonly=False, 

182 cb_openers=cleanup.LinstorSR.abort_gc_from_openers_vdi 

183 ) 

184 

185 device_path = chain.leaf_path 

186 chain.close() 

187 

188 if realpath != device_path: 

189 util.SMlog( 

190 'Update LINSTOR PhyLink (previous={}, current={})' 

191 .format(realpath, device_path) 

192 ) 

193 os.unlink(self.phypath) 

194 _mkphylink(self.sr_uuid, self.vdi_uuid, device_path) 

195 self.realpath = device_path 

196 

197 def _is_snap_paused(self): 

198 vdi_ref = self.session.xenapi.VDI.get_by_uuid(self.vdi_uuid) 

199 sm_config = self.session.xenapi.VDI.get_sm_config(vdi_ref) 

200 return 'paused' in sm_config 

201 

202 @locking("VDIUnavailable") 

203 def Pause(self): 

204 util.SMlog("Pause for %s" % self.vdi_uuid) 

205 if not os.path.exists(self.path): 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true

206 util.SMlog("No %s: nothing to pause" % self.path) 

207 return str(True) 

208 self.major, self.minor = _getDevMajor_minor(self.path) 

209 if self.major != blktap2.Tapdisk.major(): 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true

210 util.SMlog("Non-tap major number: %d" % self.major) 

211 return str(False) 

212 util.SMlog("Calling tap pause with minor %d" % self.minor) 

213 tapdisk = blktap2.Tapdisk.from_minor(self.minor) 

214 if tapdisk.is_paused() and not self._is_snap_paused(): 

215 # If we're already paused and not in a snapshot, return 

216 return str(True) 

217 

218 tapdisk.pause() 

219 return str(True) 

220 

221 @locking("VDIUnavailable") 

222 def Unpause(self): 

223 util.SMlog("Unpause for %s" % self.vdi_uuid) 

224 if not os.path.exists(self.path): 224 ↛ 225line 224 didn't jump to line 225, because the condition on line 224 was never true

225 util.SMlog("No %s: nothing to unpause" % self.path) 

226 return str(True) 

227 self._pathRefresh() 

228 self.major, self.minor = _getDevMajor_minor(self.path) 

229 if self.major != blktap2.Tapdisk.major(): 229 ↛ 230line 229 didn't jump to line 230, because the condition on line 229 was never true

230 util.SMlog("Non-tap major number: %d" % self.major) 

231 return str(False) 

232 

233 vdi = VDI.VDI.from_uuid(self.session, self.vdi_uuid) 

234 

235 if self.activate_parents: 235 ↛ 236line 235 didn't jump to line 236, because the condition on line 235 was never true

236 util.SMlog("Activating parents of %s" % self.vdi_uuid) 

237 vg_name = VG_PREFIX + self.sr_uuid 

238 ns = NS_PREFIX_LVM + self.sr_uuid 

239 lvm_cache = lvmcache.LVMCache(vg_name) 

240 lv_name = LV_PREFIX[vdi.vdi_type] + self.vdi_uuid 

241 vdi_list = getCowUtil(vdi.vdi_type).getParentChain(lv_name, LvmCowUtil.extractUuid, vg_name) 

242 for uuid, lv_name in vdi_list.items(): 

243 if uuid == self.vdi_uuid: 

244 continue 

245 lvm_cache.activate(ns, uuid, lv_name, False) 

246 

247 # Check if CBT is enabled on disk we are about to unpause 

248 if vdi._get_blocktracking_status(): 248 ↛ 249line 248 didn't jump to line 249, because the condition on line 248 was never true

249 logname = vdi._get_cbt_logname(self.vdi_uuid) 

250 # Ensure CBT log file associated with virtual disk 

251 # is activated before use 

252 vdi._activate_cbt_log(logname) 

253 self.cbtlog = vdi._get_cbt_logpath(self.vdi_uuid) 

254 else: 

255 self.cbtlog = None 

256 

257 util.SMlog("Calling tap unpause with minor %d" % self.minor) 

258 tapdisk = blktap2.Tapdisk.from_minor(self.minor) 

259 tapdisk.unpause(self.vdi_type, self.realpath, self.secondary, self.cbtlog) 

260 return str(True) 

261 

262 

263if __name__ == "__main__": 263 ↛ 264line 263 didn't jump to line 264, because the condition on line 263 was never true

264 XenAPIPlugin.dispatch({"pause": tapPause, 

265 "unpause": tapUnpause, 

266 "refresh": tapRefresh})