blob: 23117a6ae1b2ff2ecf42e6686ef1d38fec1e0d8e [file] [log] [blame]
Peter Pentchev89d52a42023-01-23 10:42:44 +02001From 51e163e721378ad280e588a2eb1195d220f6304c Mon Sep 17 00:00:00 2001
Peter Pentchev9c24be92022-09-26 22:35:24 +03002From: Peter Penchev <openstack-dev@storpool.com>
3Date: Mon, 12 Mar 2018 12:00:10 +0200
4Subject: [PATCH] Add iSCSI export support to the StorPool driver
5
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
10- 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
17- 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
22When 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
26Change-Id: I9de64306e0e6976268df782053b0651dd1cca96f
27---
28 .../unit/volume/drivers/test_storpool.py | 64 +++-
29 cinder/volume/drivers/storpool.py | 360 +++++++++++++++++-
30 .../drivers/storpool-volume-driver.rst | 49 ++-
31 3 files changed, 464 insertions(+), 9 deletions(-)
32
Peter Pentchev9c24be92022-09-26 22:35:24 +030033--- a/cinder/tests/unit/volume/drivers/test_storpool.py
34+++ b/cinder/tests/unit/volume/drivers/test_storpool.py
Peter Pentchev01838362023-02-08 15:47:47 +020035@@ -32,6 +32,7 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +030036 sys.modules['storpool'] = fakeStorPool
37
38
39+from cinder.common import constants
40 from cinder import exception
41 from cinder.tests.unit import test
42 from cinder.volume import configuration as conf
Peter Pentchev01838362023-02-08 15:47:47 +020043@@ -222,7 +223,14 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +030044 self.cfg.volume_backend_name = 'storpool_test'
45 self.cfg.storpool_template = None
46 self.cfg.storpool_replication = 3
47+ self.cfg.iscsi_cinder_volume = False
48+ self.cfg.iscsi_export_to = ''
49+ self.cfg.iscsi_portal_group = 'test-group'
50
51+ self._setup_test_driver()
52+
53+ def _setup_test_driver(self):
54+ """Initialize a StorPool driver as per the current configuration."""
55 mock_exec = mock.Mock()
56 mock_exec.return_value = ('', '')
57
Peter Pentchev01838362023-02-08 15:47:47 +020058@@ -231,7 +239,7 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +030059 self.driver.check_for_setup_error()
60
61 @ddt.data(
62- (5, TypeError),
63+ (5, (TypeError, AttributeError)),
64 ({'no-host': None}, KeyError),
65 ({'host': 'sbad'}, driver.StorPoolConfigurationInvalid),
66 ({'host': 's01'}, None),
Peter Pentchev01838362023-02-08 15:47:47 +020067@@ -247,7 +255,7 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +030068 conn)
69
70 @ddt.data(
71- (5, TypeError),
72+ (5, (TypeError, AttributeError)),
73 ({'no-host': None}, KeyError),
74 ({'host': 'sbad'}, driver.StorPoolConfigurationInvalid),
75 )
Peter Pentchev01838362023-02-08 15:47:47 +020076@@ -644,3 +652,55 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +030077 self.driver.get_pool({
78 'volume_type': volume_type
79 }))
80+
81+ @ddt.data(
82+ # The default values
83+ ('', False, constants.STORPOOL, 'beleriand', False),
84+
85+ # Export to all
86+ ('*', True, constants.ISCSI, 'beleriand', True),
87+ ('*', True, constants.ISCSI, 'beleriand', True),
88+
89+ # Only export to the controller
90+ ('', False, constants.STORPOOL, 'beleriand', False),
91+
92+ # Some of the not-fully-supported pattern lists
93+ ('roh*', False, constants.STORPOOL, 'beleriand', False),
94+ ('roh*', False, constants.STORPOOL, 'rohan', True),
95+ ('*riand roh*', False, constants.STORPOOL, 'beleriand', True),
96+ ('*riand roh*', False, constants.STORPOOL, 'rohan', True),
97+ )
98+ @ddt.unpack
99+ def test_wants_iscsi(self, iscsi_export_to, use_iscsi, storage_protocol,
100+ hostname, expected):
101+ """Check the "should this export use iSCSI?" detection."""
102+ self.cfg.iscsi_export_to = iscsi_export_to
103+ self._setup_test_driver()
104+ self.assertEqual(self.driver._use_iscsi, use_iscsi)
105+
106+ # Make sure the driver reports the correct protocol in the stats
107+ self.driver._update_volume_stats()
108+ self.assertEqual(self.driver._stats["vendor_name"], "StorPool")
109+ self.assertEqual(self.driver._stats["storage_protocol"],
110+ storage_protocol)
111+
112+ def check(conn, forced, expected):
113+ """Pass partially or completely valid connector info."""
114+ for initiator in (None, hostname):
115+ for host in (None, 'gondor'):
116+ self.assertEqual(
117+ self.driver._connector_wants_iscsi({
118+ "host": host,
119+ "initiator": initiator,
120+ **conn,
121+ }),
122+ expected if initiator is not None and host is not None
123+ else forced)
124+
125+ # If iscsi_cinder_volume is set and this is the controller, then yes.
126+ check({"storpool_wants_iscsi": True}, True, True)
127+
128+ # If iscsi_cinder_volume is not set or this is not the controller, then
129+ # look at the specified expected value.
130+ check({"storpool_wants_iscsi": False}, use_iscsi, expected)
131+ check({}, use_iscsi, expected)
Peter Pentchev9c24be92022-09-26 22:35:24 +0300132--- a/cinder/volume/drivers/storpool.py
133+++ b/cinder/volume/drivers/storpool.py
134@@ -15,6 +15,7 @@
135
136 """StorPool block device driver"""
137
138+import fnmatch
139 import platform
140
141 from oslo_config import cfg
Peter Pentchev01838362023-02-08 15:47:47 +0200142@@ -44,6 +45,31 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300143
144
145 storpool_opts = [
146+ cfg.BoolOpt('iscsi_cinder_volume',
147+ default=False,
148+ help='Let the cinder-volume service use iSCSI instead of '
149+ 'the StorPool block device driver for accessing '
150+ 'StorPool volumes, e.g. when creating a volume from '
151+ 'an image or vice versa.'),
152+ cfg.StrOpt('iscsi_export_to',
153+ default='',
154+ help='Whether to export volumes using iSCSI. '
155+ 'An empty string (the default) makes the driver export '
156+ 'all volumes using the StorPool native network protocol. '
157+ 'The value "*" makes the driver export all volumes using '
158+ 'iSCSI. '
159+ 'Any other value leads to an experimental not fully '
160+ 'supported configuration and is interpreted as '
161+ 'a whitespace-separated list of patterns for IQNs for '
162+ 'hosts that need volumes to be exported via iSCSI, e.g. '
163+ '"iqn.1991-05.com.microsoft:\\*" for Windows hosts.'),
164+ cfg.BoolOpt('iscsi_learn_initiator_iqns',
165+ default=True,
166+ help='Create a StorPool record for a new initiator as soon as '
167+ 'Cinder asks for a volume to be exported to it.'),
168+ cfg.StrOpt('iscsi_portal_group',
169+ default=None,
170+ help='The portal group to export volumes via iSCSI in.'),
171 cfg.StrOpt('storpool_template',
172 default=None,
173 help='The StorPool template for volumes with no type.'),
Peter Pentchev01838362023-02-08 15:47:47 +0200174@@ -105,6 +131,7 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300175 self._ourId = None
176 self._ourIdInt = None
177 self._attach = None
178+ self._use_iscsi = None
179
180 @staticmethod
181 def get_driver_options():
Peter Pentchev01838362023-02-08 15:47:47 +0200182@@ -162,10 +189,326 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300183 raise StorPoolConfigurationInvalid(
184 section=hostname, param='SP_OURID', error=e)
185
186+ def _connector_wants_iscsi(self, connector):
187+ """Should we do this export via iSCSI?
188+
189+ Check the configuration to determine whether this connector is
190+ expected to provide iSCSI exports as opposed to native StorPool
191+ protocol ones. Match the initiator's IQN against the list of
192+ patterns supplied in the "iscsi_export_to" configuration setting.
193+ """
194+ if connector is None:
195+ return False
196+ if self._use_iscsi:
197+ LOG.debug(' - forcing iSCSI for all exported volumes')
198+ return True
199+ if connector.get('storpool_wants_iscsi'):
200+ LOG.debug(' - forcing iSCSI for the controller')
201+ return True
202+
203+ try:
204+ iqn = connector.get('initiator')
205+ except Exception:
206+ iqn = None
207+ try:
208+ host = connector.get('host')
209+ except Exception:
210+ host = None
211+ if iqn is None or host is None:
212+ LOG.debug(' - this connector certainly does not want iSCSI')
213+ return False
214+
215+ LOG.debug(' - check whether %(host)s (%(iqn)s) wants iSCSI',
216+ {
217+ 'host': host,
218+ 'iqn': iqn,
219+ })
220+
221+ export_to = self.configuration.iscsi_export_to
222+ if export_to is None:
223+ return False
224+
225+ for pat in export_to.split():
226+ LOG.debug(' - matching against %(pat)s', {'pat': pat})
227+ if fnmatch.fnmatch(iqn, pat):
228+ LOG.debug(' - got it!')
229+ return True
230+ LOG.debug(' - nope')
231+ return False
232+
233 def validate_connector(self, connector):
234+ if self._connector_wants_iscsi(connector):
235+ return True
236 return self._storpool_client_id(connector) >= 0
237
238+ def _get_iscsi_config(self, iqn, volume_id):
239+ """Get the StorPool iSCSI config items pertaining to this volume.
240+
241+ Find the elements of the StorPool iSCSI configuration tree that
242+ will be needed to create, ensure, or remove the iSCSI export of
243+ the specified volume to the specified initiator.
244+ """
245+ cfg = self._attach.api().iSCSIConfig()
246+
247+ pg_name = self.configuration.iscsi_portal_group
248+ pg_found = [
249+ pg for pg in cfg.iscsi.portalGroups.values() if pg.name == pg_name
250+ ]
251+ if not pg_found:
252+ raise Exception('StorPool Cinder iSCSI configuration error: '
253+ 'no portal group "{pg}"'.format(pg=pg_name))
254+ pg = pg_found[0]
255+
256+ # Do we know about this initiator?
257+ i_found = [
258+ init for init in cfg.iscsi.initiators.values() if init.name == iqn
259+ ]
260+ if i_found:
261+ initiator = i_found[0]
262+ else:
263+ initiator = None
264+
265+ # Is this volume already being exported?
266+ volname = self._attach.volumeName(volume_id)
267+ t_found = [
268+ tgt for tgt in cfg.iscsi.targets.values() if tgt.volume == volname
269+ ]
270+ if t_found:
271+ target = t_found[0]
272+ else:
273+ target = None
274+
275+ # OK, so is this volume being exported to this initiator?
276+ export = None
277+ if initiator is not None and target is not None:
278+ e_found = [
279+ exp for exp in initiator.exports
280+ if exp.portalGroup == pg.name and exp.target == target.name
281+ ]
282+ if e_found:
283+ export = e_found[0]
284+
285+ return {
286+ 'cfg': cfg,
287+ 'pg': pg,
288+ 'initiator': initiator,
289+ 'target': target,
290+ 'export': export,
291+ 'volume_name': volname,
292+ 'volume_id': volume_id,
293+ }
294+
295+ def _create_iscsi_export(self, volume, connector):
296+ """Create (if needed) an iSCSI export for the StorPool volume."""
297+ LOG.debug(
298+ '_create_iscsi_export() invoked for volume '
299+ '"%(vol_name)s" (%(vol_id)s) connector %(connector)s',
300+ {
301+ 'vol_name': volume['display_name'],
302+ 'vol_id': volume['id'],
303+ 'connector': connector,
304+ }
305+ )
306+ iqn = connector['initiator']
307+ try:
308+ cfg = self._get_iscsi_config(iqn, volume['id'])
309+ except Exception as exc:
310+ LOG.error(
311+ 'Could not fetch the iSCSI config: %(exc)s', {'exc': exc}
312+ )
313+ raise
314+
315+ if cfg['initiator'] is None:
316+ if not (self.configuration.iscsi_learn_initiator_iqns or
317+ self.configuration.iscsi_cinder_volume and
318+ connector.get('storpool_wants_iscsi')):
319+ raise Exception('The "{iqn}" initiator IQN for the "{host}" '
320+ 'host is not defined in the StorPool '
321+ 'configuration.'
322+ .format(iqn=iqn, host=connector['host']))
323+ else:
324+ LOG.info('Creating a StorPool iSCSI initiator '
325+ 'for "{host}s" ({iqn}s)',
326+ {'host': connector['host'], 'iqn': iqn})
327+ self._attach.api().iSCSIConfigChange({
328+ 'commands': [
329+ {
330+ 'createInitiator': {
331+ 'name': iqn,
332+ 'username': '',
333+ 'secret': '',
334+ },
335+ },
336+ {
337+ 'initiatorAddNetwork': {
338+ 'initiator': iqn,
339+ 'net': '0.0.0.0/0',
340+ },
341+ },
342+ ]
343+ })
344+
345+ if cfg['target'] is None:
346+ LOG.info(
347+ 'Creating a StorPool iSCSI target '
348+ 'for the "%(vol_name)s" volume (%(vol_id)s)',
349+ {
350+ 'vol_name': volume['display_name'],
351+ 'vol_id': volume['id'],
352+ }
353+ )
354+ self._attach.api().iSCSIConfigChange({
355+ 'commands': [
356+ {
357+ 'createTarget': {
358+ 'volumeName': cfg['volume_name'],
359+ },
360+ },
361+ ]
362+ })
363+ cfg = self._get_iscsi_config(iqn, volume['id'])
364+
365+ if cfg['export'] is None:
366+ LOG.info('Creating a StorPool iSCSI export '
367+ 'for the "{vol_name}s" volume ({vol_id}s) '
368+ 'to the "{host}s" initiator ({iqn}s) '
369+ 'in the "{pg}s" portal group',
370+ {
371+ 'vol_name': volume['display_name'],
372+ 'vol_id': volume['id'],
373+ 'host': connector['host'],
374+ 'iqn': iqn,
375+ 'pg': cfg['pg'].name
376+ })
377+ self._attach.api().iSCSIConfigChange({
378+ 'commands': [
379+ {
380+ 'export': {
381+ 'initiator': iqn,
382+ 'portalGroup': cfg['pg'].name,
383+ 'volumeName': cfg['volume_name'],
384+ },
385+ },
386+ ]
387+ })
388+
Peter Pentchevc53e6c02023-02-08 15:13:56 +0200389+ target_portals = [
390+ "{addr}:3260".format(addr=net.address)
391+ for net in cfg['pg'].networks
392+ ]
393+ target_iqns = [cfg['target'].name] * len(target_portals)
394+ target_luns = [0] * len(target_portals)
395+ if connector.get('multipath', False):
396+ multipath_settings = {
397+ 'target_iqns': target_iqns,
398+ 'target_portals': target_portals,
399+ 'target_luns': target_luns,
400+ }
401+ else:
402+ multipath_settings = {}
403+
Peter Pentchev9c24be92022-09-26 22:35:24 +0300404+ res = {
405+ 'driver_volume_type': 'iscsi',
406+ 'data': {
Peter Pentchevc53e6c02023-02-08 15:13:56 +0200407+ **multipath_settings,
Peter Pentchev9c24be92022-09-26 22:35:24 +0300408+ 'target_discovered': False,
Peter Pentchevc53e6c02023-02-08 15:13:56 +0200409+ 'target_iqn': target_iqns[0],
410+ 'target_portal': target_portals[0],
411+ 'target_lun': target_luns[0],
Peter Pentchev9c24be92022-09-26 22:35:24 +0300412+ 'volume_id': volume['id'],
413+ 'discard': True,
414+ },
415+ }
416+ LOG.debug('returning %(res)s', {'res': res})
417+ return res
418+
419+ def _remove_iscsi_export(self, volume, connector):
420+ """Remove an iSCSI export for the specified StorPool volume."""
421+ LOG.debug(
422+ '_remove_iscsi_export() invoked for volume '
423+ '"%(vol_name)s" (%(vol_id)s) connector %(conn)s',
424+ {
425+ 'vol_name': volume['display_name'],
426+ 'vol_id': volume['id'],
427+ 'conn': connector,
428+ }
429+ )
430+ try:
431+ cfg = self._get_iscsi_config(connector['initiator'], volume['id'])
432+ except Exception as exc:
433+ LOG.error(
434+ 'Could not fetch the iSCSI config: %(exc)s', {'exc': exc}
435+ )
436+ raise
437+
438+ if cfg['export'] is not None:
439+ LOG.info('Removing the StorPool iSCSI export '
440+ 'for the "%(vol_name)s" volume (%(vol_id)s) '
441+ 'to the "%(host)s" initiator (%(iqn)s) '
442+ 'in the "%(pg)s" portal group',
443+ {
444+ 'vol_name': volume['display_name'],
445+ 'vol_id': volume['id'],
446+ 'host': connector['host'],
447+ 'iqn': connector['initiator'],
448+ 'pg': cfg['pg'].name,
449+ })
450+ try:
451+ self._attach.api().iSCSIConfigChange({
452+ 'commands': [
453+ {
454+ 'exportDelete': {
455+ 'initiator': cfg['initiator'].name,
456+ 'portalGroup': cfg['pg'].name,
457+ 'volumeName': cfg['volume_name'],
458+ },
459+ },
460+ ]
461+ })
462+ except spapi.ApiError as e:
463+ if e.name not in ('objectExists', 'objectDoesNotExist'):
464+ raise
465+ LOG.info('Looks like somebody beat us to it')
466+
467+ if cfg['target'] is not None:
468+ last = True
469+ for initiator in cfg['cfg'].iscsi.initiators.values():
470+ if initiator.name == cfg['initiator'].name:
471+ continue
472+ for exp in initiator.exports:
473+ if exp.target == cfg['target'].name:
474+ last = False
475+ break
476+ if not last:
477+ break
478+
479+ if last:
480+ LOG.info(
481+ 'Removing the StorPool iSCSI target '
482+ 'for the "{vol_name}s" volume ({vol_id}s)',
483+ {
484+ 'vol_name': volume['display_name'],
485+ 'vol_id': volume['id'],
486+ }
487+ )
488+ try:
489+ self._attach.api().iSCSIConfigChange({
490+ 'commands': [
491+ {
492+ 'deleteTarget': {
493+ 'volumeName': cfg['volume_name'],
494+ },
495+ },
496+ ]
497+ })
498+ except spapi.ApiError as e:
499+ if e.name not in ('objectDoesNotExist', 'invalidParam'):
500+ raise
501+ LOG.info('Looks like somebody beat us to it')
502+
503 def initialize_connection(self, volume, connector):
504+ if self._connector_wants_iscsi(connector):
505+ return self._create_iscsi_export(volume, connector)
506 return {'driver_volume_type': 'storpool',
507 'data': {
508 'client_id': self._storpool_client_id(connector),
Peter Pentchev01838362023-02-08 15:47:47 +0200509@@ -174,6 +517,9 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300510 }}
511
512 def terminate_connection(self, volume, connector, **kwargs):
513+ if self._connector_wants_iscsi(connector):
514+ LOG.debug('- removing an iSCSI export')
515+ self._remove_iscsi_export(volume, connector)
516 pass
517
518 def create_snapshot(self, snapshot):
Peter Pentchev01838362023-02-08 15:47:47 +0200519@@ -275,11 +621,20 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300520 )
521
522 def create_export(self, context, volume, connector):
523- pass
524+ if self._connector_wants_iscsi(connector):
525+ LOG.debug('- creating an iSCSI export')
526+ self._create_iscsi_export(volume, connector)
527
528 def remove_export(self, context, volume):
529 pass
530
531+ def _attach_volume(self, context, volume, properties, remote=False):
532+ if self.configuration.iscsi_cinder_volume and not remote:
533+ LOG.debug('- adding the "storpool_wants_iscsi" flag')
534+ properties['storpool_wants_iscsi'] = True
535+
536+ return super()._attach_volume(context, volume, properties, remote)
537+
538 def delete_volume(self, volume):
539 name = self._attach.volumeName(volume['id'])
540 try:
Peter Pentchev01838362023-02-08 15:47:47 +0200541@@ -316,6 +671,17 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300542 LOG.error("StorPoolDriver API initialization failed: %s", e)
543 raise
544
545+ export_to = self.configuration.iscsi_export_to
546+ export_to_set = export_to is not None and export_to.split()
547+ vol_iscsi = self.configuration.iscsi_cinder_volume
548+ pg_name = self.configuration.iscsi_portal_group
549+ if (export_to_set or vol_iscsi) and pg_name is None:
550+ msg = _('The "iscsi_portal_group" option is required if '
551+ 'any patterns are listed in "iscsi_export_to"')
552+ raise exception.VolumeDriverException(message=msg)
553+
554+ self._use_iscsi = export_to == "*"
555+
556 def _update_volume_stats(self):
557 try:
558 dl = self._attach.api().disksList()
Peter Pentchev01838362023-02-08 15:47:47 +0200559@@ -341,7 +707,7 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300560 'total_capacity_gb': total / units.Gi,
561 'free_capacity_gb': free / units.Gi,
562 'reserved_percentage': 0,
563- 'multiattach': True,
564+ 'multiattach': not self._use_iscsi,
565 'QoS_support': False,
566 'thick_provisioning_support': False,
567 'thin_provisioning_support': True,
Peter Pentchev01838362023-02-08 15:47:47 +0200568@@ -359,7 +725,9 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300569 'volume_backend_name') or 'storpool',
570 'vendor_name': 'StorPool',
571 'driver_version': self.VERSION,
572- 'storage_protocol': constants.STORPOOL,
573+ 'storage_protocol': (
574+ constants.ISCSI if self._use_iscsi else constants.STORPOOL
575+ ),
576
577 'clone_across_pools': True,
578 'sparse_copy_volume': True,
Peter Pentchev9c24be92022-09-26 22:35:24 +0300579--- a/doc/source/configuration/block-storage/drivers/storpool-volume-driver.rst
580+++ b/doc/source/configuration/block-storage/drivers/storpool-volume-driver.rst
Peter Pentchev01838362023-02-08 15:47:47 +0200581@@ -19,12 +19,15 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300582 * The controller and all the compute nodes must have access to the StorPool
583 API service.
584
585-* All nodes where StorPool-backed volumes will be attached must have access to
586+* If iSCSI is not being used as a transport (see below), all nodes where
587+ StorPool-backed volumes will be attached must have access to
588 the StorPool data network and run the ``storpool_block`` service.
589
590-* If StorPool-backed Cinder volumes need to be created directly from Glance
591- images, then the node running the ``cinder-volume`` service must also have
592- access to the StorPool data network and run the ``storpool_block`` service.
593+* If Glance uses Cinder as its image store, or if StorPool-backed Cinder
594+ volumes need to be created directly from Glance images, and iSCSI is not
595+ being used as a transport, then the node running the ``cinder-volume``
596+ service must also have access to the StorPool data network and run
597+ the ``storpool_block`` service.
598
599 * All nodes that need to access the StorPool API (the compute nodes and
600 the node running the ``cinder-volume`` service) must have the following
Peter Pentchev01838362023-02-08 15:47:47 +0200601@@ -34,6 +37,29 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300602 * the storpool Python bindings package
603 * the storpool.spopenstack Python helper package
604
605+Using iSCSI as the transport protocol
606+-------------------------------------
607+
608+The StorPool distributed storage system uses its own, highly optimized and
609+tailored for its specifics, network protocol for communication between
610+the storage servers and the clients (the OpenStack cluster nodes where
611+StorPool-backed volumes will be attached). There are cases when granting
612+various nodes access to the StorPool data network or installing and
613+running the ``storpool_block`` client service on them may pose difficulties.
614+The StorPool servers may also expose the user-created volumes and snapshots
615+using the standard iSCSI protocol that only requires TCP routing and
616+connectivity between the storage servers and the StorPool clients.
617+The StorPool Cinder driver may be configured to export volumes and
618+snapshots via iSCSI using the ``iscsi_export_to`` and ``iscsi_portal_group``
619+configuration options.
620+
621+Additionally, even if e.g. the hypervisor nodes running Nova will use
622+the StorPool network protocol and run the ``storpool_block`` service
623+(so the ``iscsi_export_to`` option has its default empty string value),
624+the ``iscsi_cinder_volume`` option configures the StorPool Cinder driver
625+so that only the ``cinder-volume`` service will use the iSCSI protocol when
626+attaching volumes and snapshots to transfer data to and from Glance images.
627+
628 Configuring the StorPool volume driver
629 --------------------------------------
630
Peter Pentchev01838362023-02-08 15:47:47 +0200631@@ -55,6 +81,21 @@
Peter Pentchev9c24be92022-09-26 22:35:24 +0300632 with the default placement constraints for the StorPool cluster.
633 The default value for the chain replication is 3.
634
635+- ``iscsi_export_to``: if set to the value ``*``, the StorPool Cinder driver
636+ will export volumes and snapshots using the iSCSI protocol instead of
637+ the StorPool network protocol. The ``iscsi_portal_group`` option must also
638+ be specified.
639+
640+- ``iscsi_portal_group``: if the ``iscsi_export_to`` option is set to
641+ the value ``*`` or the ``iscsi_cinder_volume`` option is turned on,
642+ this option specifies the name of the iSCSI portal group that Cinder
643+ volumes will be exported to.
644+
645+- ``iscsi_cinder_volume``: if enabled, even if the ``iscsi_export_to`` option
646+ has its default empty value, the ``cinder-volume`` service will use iSCSI
647+ to attach the volumes and snapshots for transferring data to and from
648+ Glance images.
649+
650 Using the StorPool volume driver
651 --------------------------------
652