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
« 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#
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
31CAPABILITIES = ["SR_PROBE", "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH",
32 "VDI_DETACH", "VDI_INTRODUCE"]
34CONFIGURATION = [['type', 'HBA type (optional) (e.g. FC, iSCSI, SAS etc..)']]
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 }
48class HBASR(SR.SR):
49 """HBA storage repository"""
51 def handles(type):
52 if type == "hba":
53 return True
54 return False
55 handles = staticmethod(handles)
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 = {}
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()
75 def _init_hba_hostname(self):
76 """ get the HBA Host WWN information on this host function """
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
91 def _init_hbas(self):
92 """ get the HBA information on this host function """
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
114 def _probe_hba(self):
115 try:
116 # use sysfs tree to gather FC data
118 dom = xml.dom.minidom.Document()
119 hbalist = dom.createElement("HBAInfoList")
120 dom.appendChild(hbalist)
122 hostlist = util.listdir("/sys/class/fc_host")
123 for host in hostlist:
125 hbainfo = dom.createElement("HBAInfo")
126 hbalist.appendChild(hbainfo)
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)
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)
144 port = dom.createElement("Port")
145 hbainfo.appendChild(port)
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)
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)
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)
170 return dom.toxml()
171 except:
172 raise xs_errors.XenError('XMLParse', \
173 opterr='HBA probe failed')
175 def attach(self, sr_uuid):
176 self._mpathHandle()
178 def detach(self, sr_uuid):
179 if util._containsVDIinuse(self):
180 return
181 return
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)
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)
212 def delete(self, sr_uuid):
213 self.detach(sr_uuid)
214 return
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)
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)
245 def print_devs(self):
246 self.attach("")
247 self._init_hbadict()
248 return devscan.scan(self)
250 # This function returns a dictionary of HBA attached LUNs
251 def _loadvdis(self):
252 if self.vdis:
253 return
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
271 def _getLUNbySMconfig(self, sm_config):
272 raise xs_errors.XenError('VDIUnavailable')
274 def vdi(self, uuid):
275 return LUNperVDI.RAWVDI(self, uuid)
277 def srlist_toxml(self, SRs):
278 dom = xml.dom.minidom.Document()
279 element = dom.createElement("SRlist")
280 dom.appendChild(element)
282 for val in SRs:
283 record = SRs[val]
284 entry = dom.createElement('SR')
285 element.appendChild(entry)
287 subentry = dom.createElement("UUID")
288 entry.appendChild(subentry)
289 textnode = dom.createTextNode(val)
290 subentry.appendChild(textnode)
291 return dom.toprettyxml()
293# SR registration at import
294SR.registerSR(HBASR)