Coverage for drivers/tapdisk-pause : 47%
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
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
31from constants import NS_PREFIX_LVM, VG_PREFIX
32from cowutil import getCowUtil
33from lvmcowutil import LV_PREFIX, LV_PREFIX_TO_VDI_TYPE, LvmCowUtil
35try:
36 from linstorcowutil import LinstorCowUtil
37 from linstorvolumemanager import get_controller_uri, LinstorVolumeManager
38 LINSTOR_AVAILABLE = True
39except ImportError:
40 LINSTOR_AVAILABLE = False
42TAPDEV_BACKPATH_PFX = "/dev/sm/backend"
43TAPDEV_PHYPATH_PFX = "/dev/sm/phy"
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
77def _getDevMajor_minor(dev):
78 st = os.stat(dev)
79 return [os.major(st.st_rdev),os.minor(st.st_rdev)]
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
87def tapPause(session, args):
88 tap = Tapdisk(session, args)
89 return tap.Pause()
91def tapUnpause(session, args):
92 tap = Tapdisk(session, args)
93 return tap.Unpause()
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()
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
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 )
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
159 host_ref = util.get_this_host_ref(session)
160 sr_ref = session.xenapi.SR.get_by_uuid(self.sr_uuid)
162 pbd = util.find_my_pbd(session, host_ref, sr_ref)
163 if pbd is None:
164 raise util.SMException('Failed to find PBD')
166 dconf = session.xenapi.PBD.get_device_config(pbd)
167 group_name = dconf['group-name']
169 linstor = LinstorVolumeManager(
170 get_controller_uri(),
171 group_name,
172 logger=util.SMlog
173 )
175 import cleanup
176 from srmetadata import VDI_TYPE_TAG
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 )
185 device_path = chain.leaf_path
186 chain.close()
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
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
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)
218 tapdisk.pause()
219 return str(True)
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)
233 vdi = VDI.VDI.from_uuid(self.session, self.vdi_uuid)
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)
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
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)
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})