Coverage for libs/sm/drivers/HBASR.py: 55%

204 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# HBASR: Hardware HBA LUN driver, e.g. Fibre Channel or SAS or 

17# hardware based iSCSI 

18# matches with drivers/HBASR 

19# 

20 

21from sm import SR 

22from sm import devscan 

23from sm.core import scsiutil 

24from sm.core import util 

25from sm import LUNperVDI 

26import os 

27import time 

28from sm.core import xs_errors 

29import xml.dom.minidom 

30 

31CAPABILITIES = ["SR_PROBE", "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", 

32 "VDI_DETACH", "VDI_INTRODUCE"] 

33 

34CONFIGURATION = [['type', 'HBA type (optional) (e.g. FC, iSCSI, SAS etc..)']] 

35 

36DRIVER_INFO = { 

37 'name': 'HBA LUN-per-VDI driver', 

38 'description': 'SR plugin which represents LUNs as VDIs sourced by hardware HBA adapters, e.g. hardware-based iSCSI or FC support', 

39 'vendor': 'Citrix Systems Inc', 

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

41 'driver_version': '1.0', 

42 'required_api_version': '1.0', 

43 'capabilities': CAPABILITIES, 

44 'configuration': CONFIGURATION 

45 } 

46 

47 

48class HBASR(SR.SR): 

49 """HBA storage repository""" 

50 

51 def handles(type): 

52 if type == "hba": 

53 return True 

54 return False 

55 handles = staticmethod(handles) 

56 

57 def load(self, sr_uuid): 

58 self.sr_vditype = 'phy' 

59 self.type = "any" 

60 if 'type' in self.dconf and self.dconf['type']: 

61 self.type = self.dconf['type'] 

62 self.attached = False 

63 self.procname = "" 

64 self.devs = {} 

65 

66 def _init_hbadict(self): 

67 if not hasattr(self, "hbas"): 

68 dict = devscan.adapters(filterstr=self.type) 

69 self.hbadict = dict['devs'] 

70 self.hbas = dict['adt'] 

71 if len(self.hbas): 

72 self.attached = True 

73 self.devs = scsiutil.cacheSCSIidentifiers() 

74 

75 def _init_hba_hostname(self): 

76 """ get the HBA Host WWN information on this host function """ 

77 

78 fc_xml = self._probe_hba() 

79 nodewwnval = '' 

80 try: 

81 fcs = xml.dom.minidom.parseString(fc_xml) 

82 infos = fcs.getElementsByTagName("HBAInfo") 

83 for info in infos: 83 ↛ 89line 83 didn't jump to line 89, because the loop on line 83 didn't complete

84 nodewwn = info.getElementsByTagName("nodeWWN") 

85 nodewwnval = str(nodewwn[0].firstChild.nodeValue) 

86 break 

87 except: 

88 raise xs_errors.XenError('XMLParse', opterr='HBA Host WWN scanning failed') 

89 return nodewwnval 

90 

91 def _init_hbas(self): 

92 """ get the HBA information on this host function """ 

93 

94 fc_xml = self._probe_hba() 

95 adt = {} 

96 try: 

97 fcs = xml.dom.minidom.parseString(fc_xml) 

98 infos = fcs.getElementsByTagName("HBAInfo") 

99 # HBAInfo --> Port --> portWWN 

100 # HBAInfo --> Port --> deviceName 

101 for info in infos: 

102 ports = info.getElementsByTagName("Port") 

103 for port in ports: 

104 portwwns = port.getElementsByTagName("portWWN") 

105 devnames = port.getElementsByTagName("deviceName") 

106 portval = str(portwwns[0].firstChild.nodeValue) 

107 devpath = str(devnames[0].firstChild.nodeValue).split('/')[-1] 

108 adt[devpath] = portval.split()[0] 

109 except: 

110 raise xs_errors.XenError('XMLParse', \ 

111 opterr='HBA scanning failed') 

112 return adt 

113 

114 def _probe_hba(self): 

115 try: 

116 # use sysfs tree to gather FC data 

117 

118 dom = xml.dom.minidom.Document() 

119 hbalist = dom.createElement("HBAInfoList") 

120 dom.appendChild(hbalist) 

121 

122 hostlist = util.listdir("/sys/class/fc_host") 

123 for host in hostlist: 

124 

125 hbainfo = dom.createElement("HBAInfo") 

126 hbalist.appendChild(hbainfo) 

127 

128 cmd = ["cat", "/sys/class/fc_host/%s/symbolic_name" % host] 

129 sname = util.pread(cmd)[:-1] 

130 entry = dom.createElement("model") 

131 hbainfo.appendChild(entry) 

132 textnode = dom.createTextNode(sname) 

133 entry.appendChild(textnode) 

134 

135 cmd = ["cat", "/sys/class/fc_host/%s/node_name" % host] 

136 nname = util.pread(cmd)[:-1] 

137 nname = util.make_WWN(nname) 

138 entry = dom.createElement("nodeWWN") 

139 hbainfo.appendChild(entry) 

140 # adjust nodename to look like expected string 

141 textnode = dom.createTextNode(nname) 

142 entry.appendChild(textnode) 

143 

144 port = dom.createElement("Port") 

145 hbainfo.appendChild(port) 

146 

147 cmd = ["cat", "/sys/class/fc_host/%s/port_name" % host] 

148 pname = util.pread(cmd)[:-1] 

149 pname = util.make_WWN(pname) 

150 entry = dom.createElement("portWWN") 

151 port.appendChild(entry) 

152 # adjust nodename to look like expected string 

153 textnode = dom.createTextNode(pname) 

154 entry.appendChild(textnode) 

155 

156 cmd = ["cat", "/sys/class/fc_host/%s/port_state" % host] 

157 state = util.pread(cmd)[:-1] 

158 entry = dom.createElement("state") 

159 port.appendChild(entry) 

160 # adjust nodename to look like expected string 

161 textnode = dom.createTextNode(state) 

162 entry.appendChild(textnode) 

163 

164 entry = dom.createElement("deviceName") 

165 port.appendChild(entry) 

166 # adjust nodename to look like expected string 

167 textnode = dom.createTextNode("/sys/class/scsi_host/%s" % host) 

168 entry.appendChild(textnode) 

169 

170 return dom.toxml() 

171 except: 

172 raise xs_errors.XenError('XMLParse', \ 

173 opterr='HBA probe failed') 

174 

175 def attach(self, sr_uuid): 

176 self._mpathHandle() 

177 

178 def detach(self, sr_uuid): 

179 if util._containsVDIinuse(self): 

180 return 

181 return 

182 

183 def create(self, sr_uuid, size): 

184 # Check whether an SR already exists 

185 SRs = self.session.xenapi.SR.get_all_records() 

186 for sr in SRs: 

187 record = SRs[sr] 

188 sm_config = record["sm_config"] 

189 if 'datatype' in sm_config and \ 

190 sm_config['datatype'] == 'HBA' and \ 

191 sm_config['hbatype'] == self.type: 

192 raise xs_errors.XenError('SRInUse') 

193 self._init_hbadict() 

194 if not self.attached: 

195 raise xs_errors.XenError('InvalidDev', \ 

196 opterr=('No such HBA Device detected [%s]') % self.type) 

197 

198 if self._loadvdis() > 0: 

199 scanrecord = SR.ScanRecord(self) 

200 scanrecord.synchronise() 

201 try: 

202 self.detach(sr_uuid) 

203 except: 

204 pass 

205 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref) 

206 self.sm_config['disktype'] = 'Raw' 

207 self.sm_config['datatype'] = 'HBA' 

208 self.sm_config['hbatype'] = self.type 

209 self.sm_config['multipathable'] = 'true' 

210 self.session.xenapi.SR.set_sm_config(self.sr_ref, self.sm_config) 

211 

212 def delete(self, sr_uuid): 

213 self.detach(sr_uuid) 

214 return 

215 

216 def probe(self): 

217 self._init_hbadict() 

218 self.attach("") 

219 SRs = self.session.xenapi.SR.get_all_records() 

220 Recs = {} 

221 for sr in SRs: 

222 record = SRs[sr] 

223 sm_config = record["sm_config"] 

224 if 'datatype' in sm_config and \ 

225 sm_config['datatype'] == 'HBA': 

226 Recs[record["uuid"]] = sm_config 

227 return self.srlist_toxml(Recs) 

228 

229 def scan(self, sr_uuid): 

230 self._init_hbadict() 

231 if not self.passthrough: 

232 if not self.attached: 

233 raise xs_errors.XenError('SRUnavailable') 

234 # HBA adapter scan already forced a bus rescan 

235 # Sleep for 2 seconds to allow devices to settle 

236 time.sleep(2) 

237 self._loadvdis() 

238 self.physical_utilisation = self.physical_size 

239 for uuid, vdi in self.vdis.items(): 

240 if vdi.managed: 

241 self.physical_utilisation += vdi.size 

242 self.virtual_allocation = self.physical_utilisation 

243 return super(HBASR, self).scan(sr_uuid) 

244 

245 def print_devs(self): 

246 self.attach("") 

247 self._init_hbadict() 

248 return devscan.scan(self) 

249 

250 # This function returns a dictionary of HBA attached LUNs 

251 def _loadvdis(self): 

252 if self.vdis: 

253 return 

254 

255 self._init_hbadict() 

256 count = 0 

257 for key in self.hbadict.keys(): 

258 vdi_path = os.path.join("/dev", key) 

259 if vdi_path not in self.devs: 

260 continue 

261 uuid = scsiutil.gen_uuid_from_string(scsiutil.getuniqueserial(vdi_path)) 

262 obj = self.vdi(uuid) 

263 path = self.mpathmodule.path(scsiutil.getSCSIid(vdi_path)) 

264 ids = self.devs[vdi_path] 

265 obj._query(vdi_path, ids[4]) 

266 self.vdis[uuid] = obj 

267 self.physical_size += obj.size 

268 count += 1 

269 return count 

270 

271 def _getLUNbySMconfig(self, sm_config): 

272 raise xs_errors.XenError('VDIUnavailable') 

273 

274 def vdi(self, uuid): 

275 return LUNperVDI.RAWVDI(self, uuid) 

276 

277 def srlist_toxml(self, SRs): 

278 dom = xml.dom.minidom.Document() 

279 element = dom.createElement("SRlist") 

280 dom.appendChild(element) 

281 

282 for val in SRs: 

283 record = SRs[val] 

284 entry = dom.createElement('SR') 

285 element.appendChild(entry) 

286 

287 subentry = dom.createElement("UUID") 

288 entry.appendChild(subentry) 

289 textnode = dom.createTextNode(val) 

290 subentry.appendChild(textnode) 

291 return dom.toprettyxml() 

292 

293# SR registration at import 

294SR.registerSR(HBASR)