Coverage for drivers/HBASR.py : 57%

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# HBASR: Hardware HBA LUN driver, e.g. Fibre Channel or SAS or
19# hardware based iSCSI
20#
22from sm_typing import Dict, List, override
24import SR
25import SRCommand
26import VDI
27import devscan
28import scsiutil
29import util
30import LUNperVDI
31import os
32import time
33import xs_errors
34import xml.dom.minidom
36CAPABILITIES = ["SR_PROBE", "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH",
37 "VDI_DETACH", "VDI_INTRODUCE"]
39CONFIGURATION = [['type', 'HBA type (optional) (e.g. FC, iSCSI, SAS etc..)']]
41DRIVER_INFO = {
42 'name': 'HBA LUN-per-VDI driver',
43 'description': 'SR plugin which represents LUNs as VDIs sourced by hardware HBA adapters, e.g. hardware-based iSCSI or FC support',
44 'vendor': 'Citrix Systems Inc',
45 'copyright': '(C) 2008 Citrix Systems Inc',
46 'driver_version': '1.0',
47 'required_api_version': '1.0',
48 'capabilities': CAPABILITIES,
49 'configuration': CONFIGURATION
50 }
53class HBASR(SR.SR):
54 """HBA storage repository"""
56 @override
57 @staticmethod
58 def handles(type) -> bool:
59 if type == "hba":
60 return True
61 return False
63 @override
64 def load(self, sr_uuid) -> None:
65 self.sr_vditype = 'phy'
66 self.type = "any"
67 if 'type' in self.dconf and self.dconf['type']:
68 self.type = self.dconf['type']
69 self.attached = False
70 self.procname = ""
71 self.devs: Dict[str, List[str]] = {}
73 def _init_hbadict(self):
74 if not hasattr(self, "hbas"):
75 dict = devscan.adapters(filterstr=self.type)
76 self.hbadict = dict['devs']
77 self.hbas = dict['adt']
78 if len(self.hbas):
79 self.attached = True
80 self.devs = scsiutil.cacheSCSIidentifiers()
82 def _init_hba_hostname(self):
83 """ get the HBA Host WWN information on this host function """
85 fc_xml = self._probe_hba()
86 nodewwnval = ''
87 try:
88 fcs = xml.dom.minidom.parseString(fc_xml)
89 infos = fcs.getElementsByTagName("HBAInfo")
90 for info in infos: 90 ↛ 96line 90 didn't jump to line 96, because the loop on line 90 didn't complete
91 nodewwn = info.getElementsByTagName("nodeWWN")
92 nodewwnval = str(nodewwn[0].firstChild.nodeValue)
93 break
94 except:
95 raise xs_errors.XenError('XMLParse', opterr='HBA Host WWN scanning failed')
96 return nodewwnval
98 def _init_hbas(self):
99 """ get the HBA information on this host function """
101 fc_xml = self._probe_hba()
102 adt = {}
103 try:
104 fcs = xml.dom.minidom.parseString(fc_xml)
105 infos = fcs.getElementsByTagName("HBAInfo")
106 # HBAInfo --> Port --> portWWN
107 # HBAInfo --> Port --> deviceName
108 for info in infos:
109 ports = info.getElementsByTagName("Port")
110 for port in ports:
111 portwwns = port.getElementsByTagName("portWWN")
112 devnames = port.getElementsByTagName("deviceName")
113 portval = str(portwwns[0].firstChild.nodeValue)
114 devpath = str(devnames[0].firstChild.nodeValue).split('/')[-1]
115 adt[devpath] = portval.split()[0]
116 except:
117 raise xs_errors.XenError('XMLParse', \
118 opterr='HBA scanning failed')
119 return adt
121 def _probe_hba(self):
122 try:
123 # use sysfs tree to gather FC data
125 dom = xml.dom.minidom.Document()
126 hbalist = dom.createElement("HBAInfoList")
127 dom.appendChild(hbalist)
129 hostlist = util.listdir("/sys/class/fc_host")
130 for host in hostlist:
132 hbainfo = dom.createElement("HBAInfo")
133 hbalist.appendChild(hbainfo)
135 cmd = ["cat", "/sys/class/fc_host/%s/symbolic_name" % host]
136 sname = util.pread(cmd)[:-1]
137 entry = dom.createElement("model")
138 hbainfo.appendChild(entry)
139 textnode = dom.createTextNode(sname)
140 entry.appendChild(textnode)
142 cmd = ["cat", "/sys/class/fc_host/%s/node_name" % host]
143 nname = util.pread(cmd)[:-1]
144 nname = util.make_WWN(nname)
145 entry = dom.createElement("nodeWWN")
146 hbainfo.appendChild(entry)
147 # adjust nodename to look like expected string
148 textnode = dom.createTextNode(nname)
149 entry.appendChild(textnode)
151 port = dom.createElement("Port")
152 hbainfo.appendChild(port)
154 cmd = ["cat", "/sys/class/fc_host/%s/port_name" % host]
155 pname = util.pread(cmd)[:-1]
156 pname = util.make_WWN(pname)
157 entry = dom.createElement("portWWN")
158 port.appendChild(entry)
159 # adjust nodename to look like expected string
160 textnode = dom.createTextNode(pname)
161 entry.appendChild(textnode)
163 cmd = ["cat", "/sys/class/fc_host/%s/port_state" % host]
164 state = util.pread(cmd)[:-1]
165 entry = dom.createElement("state")
166 port.appendChild(entry)
167 # adjust nodename to look like expected string
168 textnode = dom.createTextNode(state)
169 entry.appendChild(textnode)
171 entry = dom.createElement("deviceName")
172 port.appendChild(entry)
173 # adjust nodename to look like expected string
174 textnode = dom.createTextNode("/sys/class/scsi_host/%s" % host)
175 entry.appendChild(textnode)
177 return dom.toxml()
178 except:
179 raise xs_errors.XenError('XMLParse', \
180 opterr='HBA probe failed')
182 @override
183 def attach(self, sr_uuid) -> None:
184 self._mpathHandle()
186 @override
187 def detach(self, sr_uuid) -> None:
188 if util._containsVDIinuse(self):
189 return
190 return
192 @override
193 def create(self, sr_uuid, size) -> None:
194 # Check whether an SR already exists
195 SRs = self.session.xenapi.SR.get_all_records()
196 for sr in SRs:
197 record = SRs[sr]
198 sm_config = record["sm_config"]
199 if 'datatype' in sm_config and \
200 sm_config['datatype'] == 'HBA' and \
201 sm_config['hbatype'] == self.type:
202 raise xs_errors.XenError('SRInUse')
203 self._init_hbadict()
204 if not self.attached:
205 raise xs_errors.XenError('InvalidDev', \
206 opterr=('No such HBA Device detected [%s]') % self.type)
208 if self._loadvdis() > 0:
209 scanrecord = SR.ScanRecord(self)
210 scanrecord.synchronise()
211 try:
212 self.detach(sr_uuid)
213 except:
214 pass
215 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref)
216 self.sm_config['disktype'] = 'Raw'
217 self.sm_config['datatype'] = 'HBA'
218 self.sm_config['hbatype'] = self.type
219 self.sm_config['multipathable'] = 'true'
220 self.session.xenapi.SR.set_sm_config(self.sr_ref, self.sm_config)
222 @override
223 def delete(self, sr_uuid) -> None:
224 self.detach(sr_uuid)
225 return
227 @override
228 def probe(self) -> str:
229 self._init_hbadict()
230 self.attach("")
231 SRs = self.session.xenapi.SR.get_all_records()
232 Recs = {}
233 for sr in SRs:
234 record = SRs[sr]
235 sm_config = record["sm_config"]
236 if 'datatype' in sm_config and \
237 sm_config['datatype'] == 'HBA':
238 Recs[record["uuid"]] = sm_config
239 return self.srlist_toxml(Recs)
241 @override
242 def scan(self, sr_uuid) -> None:
243 self._init_hbadict()
244 if not self.passthrough:
245 if not self.attached:
246 raise xs_errors.XenError('SRUnavailable')
247 # HBA adapter scan already forced a bus rescan
248 # Sleep for 2 seconds to allow devices to settle
249 time.sleep(2)
250 self._loadvdis()
251 self.physical_utilisation = self.physical_size
252 for uuid, vdi in self.vdis.items():
253 if vdi.managed:
254 self.physical_utilisation += vdi.size
255 self.virtual_allocation = self.physical_utilisation
256 super(HBASR, self).scan(sr_uuid)
258 def print_devs(self):
259 self.attach("")
260 self._init_hbadict()
261 return devscan.scan(self)
263 # This function returns a dictionary of HBA attached LUNs
264 def _loadvdis(self):
265 if self.vdis:
266 return
268 self._init_hbadict()
269 count = 0
270 for key in self.hbadict.keys():
271 vdi_path = os.path.join("/dev", key)
272 if vdi_path not in self.devs:
273 continue
274 uuid = scsiutil.gen_uuid_from_string(scsiutil.getuniqueserial(vdi_path))
275 obj = self.vdi(uuid)
276 path = self.mpathmodule.path(scsiutil.getSCSIid(vdi_path))
277 ids = self.devs[vdi_path]
278 obj._query(vdi_path, ids[4])
279 self.vdis[uuid] = obj
280 self.physical_size += obj.size
281 count += 1
282 return count
284 def _getLUNbySMconfig(self, sm_config):
285 raise xs_errors.XenError('VDIUnavailable')
287 @override
288 def vdi(self, uuid) -> VDI.VDI:
289 return LUNperVDI.RAWVDI(self, uuid)
291 def srlist_toxml(self, SRs):
292 dom = xml.dom.minidom.Document()
293 element = dom.createElement("SRlist")
294 dom.appendChild(element)
296 for val in SRs:
297 record = SRs[val]
298 entry = dom.createElement('SR')
299 element.appendChild(entry)
301 subentry = dom.createElement("UUID")
302 entry.appendChild(subentry)
303 textnode = dom.createTextNode(val)
304 subentry.appendChild(textnode)
305 return dom.toprettyxml()
307if __name__ == '__main__': 307 ↛ 308line 307 didn't jump to line 308, because the condition on line 307 was never true
308 SRCommand.run(HBASR, DRIVER_INFO)
309else:
310 SR.registerSR(HBASR)