blob: 88e84eb1f5d7525e932f292835152861e747719b [file] [log] [blame]
Peter Pentchevd763b712022-06-24 13:17:17 +03001From 279148e7d77da3390d4417836fc0e970ec1f7fc8 Mon Sep 17 00:00:00 2001
Peter Pentchevd0130fb2021-11-30 10:50:05 +02002From: Peter Penchev <openstack-dev@storpool.com>
3Date: Mon, 12 Mar 2018 12:00:10 +0200
Peter Pentchev4ef68d02022-06-23 14:21:37 +03004Subject: [PATCH 8/8] Add iSCSI export support to the StorPool driver
Peter Pentchevd0130fb2021-11-30 10:50:05 +02005
6Add four new driver options:
7- iscsi_cinder_volume: use StorPool iSCSI attachments whenever
8 the cinder-volume service needs to attach a volume to the controller,
9 e.g. for copying an image to a volume or vice versa
Peter Pentchev4ef68d02022-06-23 14:21:37 +030010- iscsi_export_to:
11 - an empty string to use the StorPool native protocol for exporting volumes
12 protocol for exporting volumes)
13 - the string "*" to always use iSCSI for exporting volumes
14 - an experimental, not fully supported list of IQN patterns to export
15 volumes to using iSCSI; this results in a Cinder driver that exports
16 different volumes using different storage protocols
Peter Pentchevd0130fb2021-11-30 10:50:05 +020017- iscsi_portal_group: the name of the iSCSI portal group defined in
18 the StorPool configuration to use for these export
19- iscsi_learn_initiator_iqns: automatically create StorPool configuration
20 records for an initiator when a volume is first exported to it
21
Peter Pentchevd763b712022-06-24 13:17:17 +030022When exporting volumes via iSCSI, report the storage protocol as "iSCSI" and
23disable multiattach (the StorPool CI failures with iSCSI multiattach may need
24further investigation).
25
Peter Pentchevd0130fb2021-11-30 10:50:05 +020026Change-Id: I9de64306e0e6976268df782053b0651dd1cca96f
27---
Peter Pentchevd763b712022-06-24 13:17:17 +030028 .../unit/volume/drivers/test_storpool.py | 64 +++-
29 cinder/volume/drivers/storpool.py | 361 +++++++++++++++++-
30 2 files changed, 420 insertions(+), 5 deletions(-)
Peter Pentchevd0130fb2021-11-30 10:50:05 +020031
32diff --git a/cinder/tests/unit/volume/drivers/test_storpool.py b/cinder/tests/unit/volume/drivers/test_storpool.py
Peter Pentchev4ef68d02022-06-23 14:21:37 +030033index 51db7f292..aafaf7108 100644
Peter Pentchevd0130fb2021-11-30 10:50:05 +020034--- a/cinder/tests/unit/volume/drivers/test_storpool.py
35+++ b/cinder/tests/unit/volume/drivers/test_storpool.py
Peter Pentchev4ef68d02022-06-23 14:21:37 +030036@@ -32,6 +32,7 @@ fakeStorPool.sptypes = mock.Mock()
37 sys.modules['storpool'] = fakeStorPool
38
39
40+from cinder.common import constants
41 from cinder import exception
42 from cinder.tests.unit import test
43 from cinder.volume import configuration as conf
44@@ -219,7 +220,14 @@ class StorPoolTestCase(test.TestCase):
Peter Pentchevd0130fb2021-11-30 10:50:05 +020045 self.cfg.volume_backend_name = 'storpool_test'
46 self.cfg.storpool_template = None
47 self.cfg.storpool_replication = 3
48+ self.cfg.iscsi_cinder_volume = False
49+ self.cfg.iscsi_export_to = ''
50+ self.cfg.iscsi_portal_group = 'test-group'
51
Peter Pentchev4ef68d02022-06-23 14:21:37 +030052+ self._setup_test_driver()
53+
54+ def _setup_test_driver(self):
55+ """Initialize a StorPool driver as per the current configuration."""
Peter Pentchevd0130fb2021-11-30 10:50:05 +020056 mock_exec = mock.Mock()
57 mock_exec.return_value = ('', '')
Peter Pentchev4ef68d02022-06-23 14:21:37 +030058
59@@ -228,7 +236,7 @@ class StorPoolTestCase(test.TestCase):
Peter Pentchevd0130fb2021-11-30 10:50:05 +020060 self.driver.check_for_setup_error()
61
62 @ddt.data(
63- (5, TypeError),
64+ (5, (TypeError, AttributeError)),
65 ({'no-host': None}, KeyError),
66 ({'host': 'sbad'}, driver.StorPoolConfigurationInvalid),
67 ({'host': 's01'}, None),
Peter Pentchev4ef68d02022-06-23 14:21:37 +030068@@ -244,7 +252,7 @@ class StorPoolTestCase(test.TestCase):
Peter Pentchevd0130fb2021-11-30 10:50:05 +020069 conn)
70
71 @ddt.data(
72- (5, TypeError),
73+ (5, (TypeError, AttributeError)),
74 ({'no-host': None}, KeyError),
75 ({'host': 'sbad'}, driver.StorPoolConfigurationInvalid),
76 )
Peter Pentchev4ef68d02022-06-23 14:21:37 +030077@@ -635,3 +643,55 @@ class StorPoolTestCase(test.TestCase):
78 self.driver.get_pool({
79 'volume_type': volume_type
80 }))
81+
82+ @ddt.data(
83+ # The default values
84+ ('', False, constants.STORPOOL, 'beleriand', False),
85+
86+ # Export to all
87+ ('*', True, constants.ISCSI, 'beleriand', True),
88+ ('*', True, constants.ISCSI, 'beleriand', True),
89+
90+ # Only export to the controller
91+ ('', False, constants.STORPOOL, 'beleriand', False),
92+
93+ # Some of the not-fully-supported pattern lists
94+ ('roh*', False, constants.STORPOOL, 'beleriand', False),
95+ ('roh*', False, constants.STORPOOL, 'rohan', True),
96+ ('*riand roh*', False, constants.STORPOOL, 'beleriand', True),
97+ ('*riand roh*', False, constants.STORPOOL, 'rohan', True),
98+ )
99+ @ddt.unpack
100+ def test_wants_iscsi(self, iscsi_export_to, use_iscsi, storage_protocol,
101+ hostname, expected):
102+ """Check the "should this export use iSCSI?" detection."""
103+ self.cfg.iscsi_export_to = iscsi_export_to
104+ self._setup_test_driver()
105+ self.assertEqual(self.driver._use_iscsi, use_iscsi)
106+
107+ # Make sure the driver reports the correct protocol in the stats
108+ self.driver._update_volume_stats()
109+ self.assertEqual(self.driver._stats["vendor_name"], "StorPool")
110+ self.assertEqual(self.driver._stats["storage_protocol"],
111+ storage_protocol)
112+
113+ def check(conn, forced, expected):
114+ """Pass partially or completely valid connector info."""
115+ for initiator in (None, hostname):
116+ for host in (None, 'gondor'):
117+ self.assertEqual(
118+ self.driver._connector_wants_iscsi({
119+ "host": host,
120+ "initiator": initiator,
121+ **conn,
122+ }),
123+ expected if initiator is not None and host is not None
124+ else forced)
125+
126+ # If iscsi_cinder_volume is set and this is the controller, then yes.
127+ check({"storpool_wants_iscsi": True}, True, True)
128+
129+ # If iscsi_cinder_volume is not set or this is not the controller, then
130+ # look at the specified expected value.
131+ check({"storpool_wants_iscsi": False}, use_iscsi, expected)
132+ check({}, use_iscsi, expected)
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200133diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py
Peter Pentchevd763b712022-06-24 13:17:17 +0300134index 401e3709a..6eaf1f42a 100644
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200135--- a/cinder/volume/drivers/storpool.py
136+++ b/cinder/volume/drivers/storpool.py
137@@ -15,6 +15,7 @@
138
139 """StorPool block device driver"""
140
141+import fnmatch
142 import platform
143
144 from oslo_config import cfg
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300145@@ -44,6 +45,31 @@ if storpool:
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200146
147
148 storpool_opts = [
149+ cfg.BoolOpt('iscsi_cinder_volume',
150+ default=False,
151+ help='Let the cinder-volume service use iSCSI instead of '
152+ 'the StorPool block device driver for accessing '
153+ 'StorPool volumes, e.g. when creating a volume from '
154+ 'an image or vice versa.'),
155+ cfg.StrOpt('iscsi_export_to',
156+ default='',
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300157+ help='Whether to export volumes using iSCSI. '
158+ 'An empty string (the default) makes the driver export '
159+ 'all volumes using the StorPool native network protocol. '
160+ 'The value "*" makes the driver export all volumes using '
161+ 'iSCSI. '
162+ 'Any other value leads to an experimental not fully '
163+ 'supported configuration and is interpreted as '
164+ 'a whitespace-separated list of patterns for IQNs for '
165+ 'hosts that need volumes to be exported via iSCSI, e.g. '
166+ '"iqn.1991-05.com.microsoft:*" for Windows hosts.'),
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200167+ cfg.BoolOpt('iscsi_learn_initiator_iqns',
168+ default=True,
169+ help='Create a StorPool record for a new initiator as soon as '
170+ 'Cinder asks for a volume to be exported to it.'),
171+ cfg.StrOpt('iscsi_portal_group',
172+ default=None,
173+ help='The portal group to export volumes via iSCSI in.'),
174 cfg.StrOpt('storpool_template',
175 default=None,
176 help='The StorPool template for volumes with no type.'),
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300177@@ -102,6 +128,7 @@ class StorPoolDriver(driver.VolumeDriver):
178 - Declare the capability to clone a volume into a different
179 pool, thus enabling the use of create_cloned_volume() for
180 Cinder-backed Glance images on StorPool volumes
181+ - Add support for exporting volumes via iSCSI
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200182 """
183
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300184 VERSION = '2.0.0'
185@@ -114,6 +141,7 @@ class StorPoolDriver(driver.VolumeDriver):
186 self._ourId = None
187 self._ourIdInt = None
188 self._attach = None
189+ self._use_iscsi = None
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200190
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300191 @staticmethod
192 def get_driver_options():
Peter Pentchevd763b712022-06-24 13:17:17 +0300193@@ -171,10 +199,312 @@ class StorPoolDriver(driver.VolumeDriver):
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200194 raise StorPoolConfigurationInvalid(
195 section=hostname, param='SP_OURID', error=e)
196
197+ def _connector_wants_iscsi(self, connector):
198+ """Should we do this export via iSCSI?
199+
200+ Check the configuration to determine whether this connector is
201+ expected to provide iSCSI exports as opposed to native StorPool
202+ protocol ones. Match the initiator's IQN against the list of
203+ patterns supplied in the "iscsi_export_to" configuration setting.
204+ """
205+ if connector is None:
206+ return False
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300207+ if self._use_iscsi:
208+ LOG.debug(' - forcing iSCSI for all exported volumes')
209+ return True
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200210+ if connector.get('storpool_wants_iscsi'):
211+ LOG.debug(' - forcing iSCSI for the controller')
212+ return True
213+
214+ try:
215+ iqn = connector.get('initiator')
216+ except Exception:
217+ iqn = None
218+ try:
219+ host = connector.get('host')
220+ except Exception:
221+ host = None
222+ if iqn is None or host is None:
223+ LOG.debug(' - this connector certainly does not want iSCSI')
224+ return False
225+
226+ LOG.debug(' - check whether %(host)s (%(iqn)s) wants iSCSI',
227+ {
228+ 'host': host,
229+ 'iqn': iqn,
230+ })
231+
232+ export_to = self.configuration.iscsi_export_to
233+ if export_to is None:
234+ return False
235+
236+ for pat in export_to.split():
237+ LOG.debug(' - matching against %(pat)s', {'pat': pat})
238+ if fnmatch.fnmatch(iqn, pat):
239+ LOG.debug(' - got it!')
240+ return True
241+ LOG.debug(' - nope')
242+ return False
243+
244 def validate_connector(self, connector):
245+ if self._connector_wants_iscsi(connector):
246+ return True
247 return self._storpool_client_id(connector) >= 0
248
249+ def _get_iscsi_config(self, iqn, volume_id):
250+ """Get the StorPool iSCSI config items pertaining to this volume.
251+
252+ Find the elements of the StorPool iSCSI configuration tree that
253+ will be needed to create, ensure, or remove the iSCSI export of
254+ the specified volume to the specified initiator.
255+ """
256+ cfg = self._attach.api().iSCSIConfig()
257+
258+ pg_name = self.configuration.iscsi_portal_group
259+ pg_found = [
260+ pg for pg in cfg.iscsi.portalGroups.values() if pg.name == pg_name
261+ ]
262+ if not pg_found:
263+ raise Exception('StorPool Cinder iSCSI configuration error: '
264+ 'no portal group "{pg}"'.format(pg=pg_name))
265+ pg = pg_found[0]
266+
267+ # Do we know about this initiator?
268+ i_found = [
269+ init for init in cfg.iscsi.initiators.values() if init.name == iqn
270+ ]
271+ if i_found:
272+ initiator = i_found[0]
273+ else:
274+ initiator = None
275+
276+ # Is this volume already being exported?
277+ volname = self._attach.volumeName(volume_id)
278+ t_found = [
279+ tgt for tgt in cfg.iscsi.targets.values() if tgt.volume == volname
280+ ]
281+ if t_found:
282+ target = t_found[0]
283+ else:
284+ target = None
285+
286+ # OK, so is this volume being exported to this initiator?
287+ export = None
288+ if initiator is not None and target is not None:
289+ e_found = [
290+ exp for exp in initiator.exports
291+ if exp.portalGroup == pg.name and exp.target == target.name
292+ ]
293+ if e_found:
294+ export = e_found[0]
295+
296+ return {
297+ 'cfg': cfg,
298+ 'pg': pg,
299+ 'initiator': initiator,
300+ 'target': target,
301+ 'export': export,
302+ 'volume_name': volname,
303+ 'volume_id': volume_id,
304+ }
305+
306+ def _create_iscsi_export(self, volume, connector):
307+ """Create (if needed) an iSCSI export for the StorPool volume."""
308+ LOG.debug(
309+ '_create_iscsi_export() invoked for volume '
310+ '"%(vol_name)s" (%(vol_id)s) connector %(connector)s',
311+ {
312+ 'vol_name': volume['display_name'],
313+ 'vol_id': volume['id'],
314+ 'connector': connector,
315+ }
316+ )
317+ iqn = connector['initiator']
318+ try:
319+ cfg = self._get_iscsi_config(iqn, volume['id'])
320+ except Exception as exc:
321+ LOG.error(
322+ 'Could not fetch the iSCSI config: %(exc)s', {'exc': exc}
323+ )
324+ raise
325+
326+ if cfg['initiator'] is None:
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200327+ if not (self.configuration.iscsi_learn_initiator_iqns or
328+ self.configuration.iscsi_cinder_volume and
329+ connector.get('storpool_wants_iscsi')):
330+ raise Exception('The "{iqn}" initiator IQN for the "{host}" '
331+ 'host is not defined in the StorPool '
332+ 'configuration.'
333+ .format(iqn=iqn, host=connector['host']))
334+ else:
335+ LOG.info('Creating a StorPool iSCSI initiator '
336+ 'for "{host}s" ({iqn}s)',
337+ {'host': connector['host'], 'iqn': iqn})
338+ self._attach.api().iSCSIConfigChange({
339+ 'commands': [
340+ {
341+ 'createInitiator': {
342+ 'name': iqn,
343+ 'username': '',
344+ 'secret': '',
345+ },
346+ },
347+ {
348+ 'initiatorAddNetwork': {
349+ 'initiator': iqn,
350+ 'net': '0.0.0.0/0',
351+ },
352+ },
353+ ]
354+ })
355+
356+ if cfg['target'] is None:
357+ LOG.info(
358+ 'Creating a StorPool iSCSI target '
359+ 'for the "%(vol_name)s" volume (%(vol_id)s)',
360+ {
361+ 'vol_name': volume['display_name'],
362+ 'vol_id': volume['id'],
363+ }
364+ )
365+ self._attach.api().iSCSIConfigChange({
366+ 'commands': [
367+ {
368+ 'createTarget': {
369+ 'volumeName': cfg['volume_name'],
370+ },
371+ },
372+ ]
373+ })
374+ cfg = self._get_iscsi_config(iqn, volume['id'])
375+
376+ if cfg['export'] is None:
377+ LOG.info('Creating a StorPool iSCSI export '
378+ 'for the "{vol_name}s" volume ({vol_id}s) '
379+ 'to the "{host}s" initiator ({iqn}s) '
380+ 'in the "{pg}s" portal group',
381+ {
382+ 'vol_name': volume['display_name'],
383+ 'vol_id': volume['id'],
384+ 'host': connector['host'],
385+ 'iqn': iqn,
386+ 'pg': cfg['pg'].name
387+ })
388+ self._attach.api().iSCSIConfigChange({
389+ 'commands': [
390+ {
391+ 'export': {
392+ 'initiator': iqn,
393+ 'portalGroup': cfg['pg'].name,
394+ 'volumeName': cfg['volume_name'],
395+ },
396+ },
397+ ]
398+ })
399+
400+ res = {
401+ 'driver_volume_type': 'iscsi',
402+ 'data': {
403+ 'target_discovered': False,
404+ 'target_iqn': cfg['target'].name,
405+ 'target_portal': '{}:3260'.format(
406+ cfg['pg'].networks[0].address
407+ ),
408+ 'target_lun': 0,
409+ 'volume_id': volume['id'],
410+ 'discard': True,
411+ },
412+ }
413+ LOG.debug('returning %(res)s', {'res': res})
414+ return res
415+
416+ def _remove_iscsi_export(self, volume, connector):
417+ """Remove an iSCSI export for the specified StorPool volume."""
418+ LOG.debug(
419+ '_remove_iscsi_export() invoked for volume '
420+ '"%(vol_name)s" (%(vol_id)s) connector %(conn)s',
421+ {
422+ 'vol_name': volume['display_name'],
423+ 'vol_id': volume['id'],
424+ 'conn': connector,
425+ }
426+ )
427+ try:
428+ cfg = self._get_iscsi_config(connector['initiator'], volume['id'])
429+ except Exception as exc:
430+ LOG.error(
431+ 'Could not fetch the iSCSI config: %(exc)s', {'exc': exc}
432+ )
433+ raise
434+
435+ if cfg['export'] is not None:
436+ LOG.info('Removing the StorPool iSCSI export '
437+ 'for the "%(vol_name)s" volume (%(vol_id)s) '
438+ 'to the "%(host)s" initiator (%(iqn)s) '
439+ 'in the "%(pg)s" portal group',
440+ {
441+ 'vol_name': volume['display_name'],
442+ 'vol_id': volume['id'],
443+ 'host': connector['host'],
444+ 'iqn': connector['initiator'],
445+ 'pg': cfg['pg'].name,
446+ })
447+ try:
448+ self._attach.api().iSCSIConfigChange({
449+ 'commands': [
450+ {
451+ 'exportDelete': {
452+ 'initiator': cfg['initiator'].name,
453+ 'portalGroup': cfg['pg'].name,
454+ 'volumeName': cfg['volume_name'],
455+ },
456+ },
457+ ]
458+ })
459+ except spapi.ApiError as e:
460+ if e.name not in ('objectExists', 'objectDoesNotExist'):
461+ raise
462+ LOG.info('Looks like somebody beat us to it')
463+
464+ if cfg['target'] is not None:
465+ last = True
466+ for initiator in cfg['cfg'].iscsi.initiators.values():
467+ if initiator.name == cfg['initiator'].name:
468+ continue
469+ for exp in initiator.exports:
470+ if exp.target == cfg['target'].name:
471+ last = False
472+ break
473+ if not last:
474+ break
475+
476+ if last:
477+ LOG.info(
478+ 'Removing the StorPool iSCSI target '
479+ 'for the "{vol_name}s" volume ({vol_id}s)',
480+ {
481+ 'vol_name': volume['display_name'],
482+ 'vol_id': volume['id'],
483+ }
484+ )
485+ try:
486+ self._attach.api().iSCSIConfigChange({
487+ 'commands': [
488+ {
489+ 'deleteTarget': {
490+ 'volumeName': cfg['volume_name'],
491+ },
492+ },
493+ ]
494+ })
495+ except spapi.ApiError as e:
496+ if e.name not in ('objectDoesNotExist', 'invalidParam'):
497+ raise
498+ LOG.info('Looks like somebody beat us to it')
499+
500 def initialize_connection(self, volume, connector):
501+ if self._connector_wants_iscsi(connector):
502+ return self._create_iscsi_export(volume, connector)
503 return {'driver_volume_type': 'storpool',
504 'data': {
505 'client_id': self._storpool_client_id(connector),
Peter Pentchevd763b712022-06-24 13:17:17 +0300506@@ -183,6 +513,9 @@ class StorPoolDriver(driver.VolumeDriver):
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200507 }}
508
509 def terminate_connection(self, volume, connector, **kwargs):
510+ if self._connector_wants_iscsi(connector):
511+ LOG.debug('- removing an iSCSI export')
512+ self._remove_iscsi_export(volume, connector)
513 pass
514
515 def create_snapshot(self, snapshot):
Peter Pentchevd763b712022-06-24 13:17:17 +0300516@@ -284,11 +617,20 @@ class StorPoolDriver(driver.VolumeDriver):
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300517 )
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200518
519 def create_export(self, context, volume, connector):
520- pass
521+ if self._connector_wants_iscsi(connector):
522+ LOG.debug('- creating an iSCSI export')
523+ self._create_iscsi_export(volume, connector)
524
525 def remove_export(self, context, volume):
526 pass
527
528+ def _attach_volume(self, context, volume, properties, remote=False):
Peter Pentcheva102cc52021-11-30 16:10:27 +0200529+ if self.configuration.iscsi_cinder_volume and not remote:
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200530+ LOG.debug('- adding the "storpool_wants_iscsi" flag')
531+ properties['storpool_wants_iscsi'] = True
532+
533+ return super()._attach_volume(context, volume, properties, remote)
534+
535 def delete_volume(self, volume):
536 name = self._attach.volumeName(volume['id'])
537 try:
Peter Pentchevd763b712022-06-24 13:17:17 +0300538@@ -325,6 +667,17 @@ class StorPoolDriver(driver.VolumeDriver):
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200539 LOG.error("StorPoolDriver API initialization failed: %s", e)
540 raise
541
542+ export_to = self.configuration.iscsi_export_to
543+ export_to_set = export_to is not None and export_to.split()
544+ vol_iscsi = self.configuration.iscsi_cinder_volume
545+ pg_name = self.configuration.iscsi_portal_group
546+ if (export_to_set or vol_iscsi) and pg_name is None:
547+ msg = _('The "iscsi_portal_group" option is required if '
548+ 'any patterns are listed in "iscsi_export_to"')
549+ raise exception.VolumeDriverException(message=msg)
550+
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300551+ self._use_iscsi = export_to == "*"
552+
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200553 def _update_volume_stats(self):
554 try:
555 dl = self._attach.api().disksList()
Peter Pentchevd763b712022-06-24 13:17:17 +0300556@@ -350,7 +703,7 @@ class StorPoolDriver(driver.VolumeDriver):
557 'total_capacity_gb': total / units.Gi,
558 'free_capacity_gb': free / units.Gi,
559 'reserved_percentage': 0,
560- 'multiattach': True,
561+ 'multiattach': not self._use_iscsi,
562 'QoS_support': False,
563 'thick_provisioning_support': False,
564 'thin_provisioning_support': True,
565@@ -368,7 +721,9 @@ class StorPoolDriver(driver.VolumeDriver):
Peter Pentchev4ef68d02022-06-23 14:21:37 +0300566 'volume_backend_name') or 'storpool',
567 'vendor_name': 'StorPool',
568 'driver_version': self.VERSION,
569- 'storage_protocol': constants.STORPOOL,
570+ 'storage_protocol': (
571+ constants.ISCSI if self._use_iscsi else constants.STORPOOL
572+ ),
573
574 'clone_across_pools': True,
575 'sparse_copy_volume': True,
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200576--
Peter Pentchev4ef68d02022-06-23 14:21:37 +03005772.35.1
Peter Pentchevd0130fb2021-11-30 10:50:05 +0200578