Coverage for libs/sm/drivers/LVHDoHBASR.py: 33%

162 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2026-07-10 07:59 +0000

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# LVHDoHBASR: LVHD over Hardware HBA LUN driver, e.g. Fibre Channel or 

17# hardware based iSCSI 

18# matches with drivers/LVHDoHBASR 

19# 

20 

21import os 

22import re 

23import sys 

24import xmlrpc.client 

25import glob 

26 

27from sm.drivers import HBASR 

28from sm.drivers import LVHDSR 

29 

30from sm import SR 

31from sm import lvutil 

32from sm.core import xs_errors 

33from sm.core import util 

34from sm.core import scsiutil 

35from sm.core import mpath_cli 

36 

37CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_METADATA", "SR_TRIM", "SR_CACHING", 

38 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", 

39 "VDI_GENERATE_CONFIG", "VDI_SNAPSHOT", "VDI_CLONE", "VDI_MIRROR", 

40 "VDI_RESIZE", "ATOMIC_PAUSE", "VDI_RESET_ON_BOOT/2", 

41 "VDI_UPDATE", "VDI_CONFIG_CBT", "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

42 

43CONFIGURATION = [['SCSIid', 'The scsi_id of the destination LUN'], \ 

44 ['allocation', 'Valid values are thick or thin (optional, defaults to thick)']] 

45 

46DRIVER_INFO = { 

47 'name': 'LVHD over FC', 

48 'description': 'SR plugin which represents disks as VHDs on Logical Volumes within a Volume Group created on an HBA LUN, e.g. hardware-based iSCSI or FC support', 

49 'vendor': 'Citrix Systems Inc', 

50 'copyright': '(C) 2008 Citrix Systems Inc', 

51 'driver_version': '1.0', 

52 'required_api_version': '1.0', 

53 'capabilities': CAPABILITIES, 

54 'configuration': CONFIGURATION 

55 } 

56 

57 

58class LVHDoHBASR(LVHDSR.LVHDSR): 

59 """LVHD over HBA storage repository""" 

60 

61 @staticmethod 

62 def handles(type): 

63 if type == "lvmohba": 

64 return True 

65 if type == "lvhdohba": 

66 return True 

67 return False 

68 

69 def load(self, sr_uuid): 

70 driver = SR.driver('hba') 

71 self.hbasr = driver(self.original_srcmd, sr_uuid) 

72 

73 # If this is a vdi command, don't initialise SR 

74 if not (util.isVDICommand(self.original_srcmd.cmd)): 74 ↛ 75line 74 didn't jump to line 75, because the condition on line 74 was never true

75 pbd = None 

76 try: 

77 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

78 except: 

79 pass 

80 

81 try: 

82 if 'SCSIid' not in self.dconf and 'device' in self.dconf: 

83 # UPGRADE FROM MIAMI: add SCSIid key to device_config 

84 util.SMlog("Performing upgrade from Miami") 

85 if not os.path.exists(self.dconf['device']): 

86 raise xs_errors.XenError('InvalidDev') 

87 SCSIid = scsiutil.getSCSIid(self.dconf['device']) 

88 self.dconf['SCSIid'] = SCSIid 

89 del self.dconf['device'] 

90 

91 if pbd is not None: 

92 device_config = self.session.xenapi.PBD.get_device_config(pbd) 

93 device_config['SCSIid'] = SCSIid 

94 device_config['upgraded_from_miami'] = 'true' 

95 del device_config['device'] 

96 self.session.xenapi.PBD.set_device_config(pbd, device_config) 

97 except: 

98 pass 

99 

100 if 'SCSIid' not in self.dconf or not self.dconf['SCSIid']: 

101 print(self.hbasr.print_devs(), file=sys.stderr) 

102 raise xs_errors.XenError('ConfigSCSIid') 

103 

104 self.SCSIid = self.dconf['SCSIid'] 

105 LVHDSR.LVHDSR.load(self, sr_uuid) 

106 

107 def create(self, sr_uuid, size): 

108 self.hbasr.attach(sr_uuid) 

109 if self.mpath == "true": 

110 self.mpathmodule.refresh(self.SCSIid, 0) 

111 self._pathrefresh(LVHDoHBASR) 

112 try: 

113 LVHDSR.LVHDSR.create(self, sr_uuid, size) 

114 finally: 

115 if self.mpath == "true": 

116 self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) 

117 util.remove_mpathcount_field(self.session, self.host_ref, \ 

118 self.sr_ref, self.SCSIid) 

119 

120 def attach(self, sr_uuid): 

121 self.hbasr.attach(sr_uuid) 

122 if self.mpath == "true": 

123 self.mpathmodule.refresh(self.SCSIid, 0) 

124 # set the device mapper's I/O scheduler 

125 path = '/dev/disk/by-scsid/%s' % self.dconf['SCSIid'] 

126 for file in os.listdir(path): 

127 self.block_setscheduler('%s/%s' % (path, file)) 

128 

129 self._pathrefresh(LVHDoHBASR) 

130 if not os.path.exists(self.dconf['device']): 

131 # Force a rescan on the bus 

132 self.hbasr._init_hbadict() 

133 # Must re-initialise the multipath node 

134 if self.mpath == "true": 

135 self.mpathmodule.refresh(self.SCSIid, 0) 

136 LVHDSR.LVHDSR.attach(self, sr_uuid) 

137 self._setMultipathableFlag(SCSIid=self.SCSIid) 

138 

139 def scan(self, sr_uuid): 

140 # During a reboot, scan is called ahead of attach, which causes the MGT 

141 # to point of the wrong device instead of dm-x. Running multipathing will 

142 # take care of this scenario. 

143 if self.mpath == "true": 

144 if 'device' not in self.dconf or not os.path.exists(self.dconf['device']): 

145 util.SMlog("@@@@@ path does not exists") 

146 self.mpathmodule.refresh(self.SCSIid, 0) 

147 self._pathrefresh(LVHDoHBASR) 

148 self._setMultipathableFlag(SCSIid=self.SCSIid) 

149 else: 

150 self._pathrefresh(LVHDoHBASR) 

151 LVHDSR.LVHDSR.scan(self, sr_uuid) 

152 

153 def probe(self): 

154 if self.mpath == "true" and 'SCSIid' in self.dconf: 

155 # When multipathing is enabled, since we don't refcount the multipath maps, 

156 # we should not attempt to do the iscsi.attach/detach when the map is already present, 

157 # as this will remove it (which may well be in use). 

158 maps = [] 

159 try: 

160 maps = mpath_cli.list_maps() 

161 except: 

162 pass 

163 

164 if self.dconf['SCSIid'] in maps: 

165 raise xs_errors.XenError('SRInUse') 

166 

167 self.mpathmodule.refresh(self.SCSIid, 0) 

168 

169 try: 

170 self._pathrefresh(LVHDoHBASR) 

171 result = LVHDSR.LVHDSR.probe(self) 

172 if self.mpath == "true": 

173 self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) 

174 return result 

175 except: 

176 if self.mpath == "true": 

177 self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) 

178 raise 

179 

180 def detach(self, sr_uuid): 

181 LVHDSR.LVHDSR.detach(self, sr_uuid) 

182 self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) 

183 try: 

184 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

185 except: 

186 pass 

187 for key in ["mpath-" + self.SCSIid, "multipathed"]: 

188 try: 

189 self.session.xenapi.PBD.remove_from_other_config(pbdref, key) 

190 except: 

191 pass 

192 

193 def _remove_device_nodes(self): 

194 """ 

195 Remove the kernel device nodes 

196 """ 

197 nodes = glob.glob('/dev/disk/by-scsid/%s/*' % self.SCSIid) 

198 util.SMlog('Remove_nodes, nodes are %s' % nodes) 

199 for node in nodes: 

200 with open('/sys/block/%s/device/delete' % 

201 (os.path.basename(node)), 'w') as f: 

202 f.write('1\n') 

203 

204 def delete(self, sr_uuid): 

205 self._pathrefresh(LVHDoHBASR) 

206 try: 

207 LVHDSR.LVHDSR.delete(self, sr_uuid) 

208 finally: 

209 if self.mpath == "true": 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true

210 self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) 

211 self._remove_device_nodes() 

212 

213 def vdi(self, uuid): 

214 return LVHDoHBAVDI(self, uuid) 

215 

216 

217class LVHDoHBAVDI(LVHDSR.LVHDVDI): 

218 def generate_config(self, sr_uuid, vdi_uuid): 

219 util.SMlog("LVHDoHBAVDI.generate_config") 

220 if not lvutil._checkLV(self.path): 

221 raise xs_errors.XenError('VDIUnavailable') 

222 dict = {} 

223 self.sr.dconf['multipathing'] = self.sr.mpath 

224 self.sr.dconf['multipathhandle'] = self.sr.mpathhandle 

225 dict['device_config'] = self.sr.dconf 

226 dict['sr_uuid'] = sr_uuid 

227 dict['vdi_uuid'] = vdi_uuid 

228 dict['command'] = 'vdi_attach_from_config' 

229 # Return the 'config' encoded within a normal XMLRPC response so that 

230 # we can use the regular response/error parsing code. 

231 config = xmlrpc.client.dumps(tuple([dict]), "vdi_attach_from_config") 

232 return xmlrpc.client.dumps((config, ), "", True) 

233 

234 def attach_from_config(self, sr_uuid, vdi_uuid): 

235 util.SMlog("LVHDoHBAVDI.attach_from_config") 

236 self.sr.hbasr.attach(sr_uuid) 

237 if self.sr.mpath == "true": 

238 self.sr.mpathmodule.refresh(self.sr.SCSIid, 0) 

239 try: 

240 return self.attach(sr_uuid, vdi_uuid) 

241 except: 

242 util.logException("LVHDoHBAVDI.attach_from_config") 

243 raise xs_errors.XenError('SRUnavailable', \ 

244 opterr='Unable to attach the heartbeat disk') 

245 

246 

247def match_scsidev(s): 

248 regex = re.compile("^/dev/disk/by-id|^/dev/mapper") 

249 return regex.search(s, 0) 

250 

251# SR registration at import 

252SR.registerSR(LVHDoHBASR)