Coverage for drivers/HBASR.py : 55%

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