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
« 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#
21import os
22import re
23import sys
24import xmlrpc.client
25import glob
27from sm.drivers import HBASR
28from sm.drivers import LVHDSR
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
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"]
43CONFIGURATION = [['SCSIid', 'The scsi_id of the destination LUN'], \
44 ['allocation', 'Valid values are thick or thin (optional, defaults to thick)']]
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 }
58class LVHDoHBASR(LVHDSR.LVHDSR):
59 """LVHD over HBA storage repository"""
61 @staticmethod
62 def handles(type):
63 if type == "lvmohba":
64 return True
65 if type == "lvhdohba":
66 return True
67 return False
69 def load(self, sr_uuid):
70 driver = SR.driver('hba')
71 self.hbasr = driver(self.original_srcmd, sr_uuid)
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
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']
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
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')
104 self.SCSIid = self.dconf['SCSIid']
105 LVHDSR.LVHDSR.load(self, sr_uuid)
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)
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))
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)
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)
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
164 if self.dconf['SCSIid'] in maps:
165 raise xs_errors.XenError('SRInUse')
167 self.mpathmodule.refresh(self.SCSIid, 0)
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
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
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')
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()
213 def vdi(self, uuid):
214 return LVHDoHBAVDI(self, uuid)
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)
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')
247def match_scsidev(s):
248 regex = re.compile("^/dev/disk/by-id|^/dev/mapper")
249 return regex.search(s, 0)
251# SR registration at import
252SR.registerSR(LVHDoHBASR)