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