blob: 0e23a25699f6fd3c3c74de2d4cbcaa052ff16f84 [file] [log] [blame]
Biser Milanov7a541582025-01-29 16:31:14 +02001From 89a7a3bf5fb34198d595aa906cdf3ee9f6647f48 Mon Sep 17 00:00:00 2001
Biser Milanov84585622025-01-27 15:11:47 +02002From: Biser Milanov <biser.milanov@storpool.com>
3Date: Mon, 14 Oct 2024 14:25:22 +0300
4Subject: [PATCH] StorPool: Use os-brick instead of packages `storpool` and
5 `storpool.spopenstack`
6
7Stop depending on modules `storpool` and `storpool.spopenstack` for
8access to the StorPool API and reading the StorPool configuration from
9files. Use the new new in-tree implementation introduced in `os-brick`.
10
11Change-Id: Ieb6be04133e1639b3fa6e3a322604b366e909d81
12---
13 .../unit/volume/drivers/test_storpool.py | 168 +++++++---------
14 cinder/volume/drivers/storpool.py | 183 +++++++++---------
Biser Milanov7a541582025-01-29 16:31:14 +020015 .../drivers/storpool-volume-driver.rst | 8 -
Biser Milanov84585622025-01-27 15:11:47 +020016 ...-config-code-in-tree-92cfe30690b78ef1.yaml | 8 +
17 requirements.txt | 2 +-
Biser Milanov7a541582025-01-29 16:31:14 +020018 5 files changed, 173 insertions(+), 196 deletions(-)
Biser Milanov84585622025-01-27 15:11:47 +020019 create mode 100644 releasenotes/notes/storpool-move-api-and-config-code-in-tree-92cfe30690b78ef1.yaml
20
21diff --git a/cinder/tests/unit/volume/drivers/test_storpool.py b/cinder/tests/unit/volume/drivers/test_storpool.py
22index 44707d0b8..2015c734d 100644
23--- a/cinder/tests/unit/volume/drivers/test_storpool.py
24+++ b/cinder/tests/unit/volume/drivers/test_storpool.py
25@@ -16,21 +16,13 @@
26
27 import itertools
28 import re
29-import sys
30 from unittest import mock
31
32 import ddt
33+from os_brick.initiator import storpool_utils
34+from os_brick.tests.initiator import test_storpool_utils
35 from oslo_utils import units
36
37-
38-fakeStorPool = mock.Mock()
39-fakeStorPool.spopenstack = mock.Mock()
40-fakeStorPool.spapi = mock.Mock()
41-fakeStorPool.spconfig = mock.Mock()
42-fakeStorPool.sptypes = mock.Mock()
43-sys.modules['storpool'] = fakeStorPool
44-
45-
46 from cinder import exception
47 from cinder.tests.unit import fake_constants
48 from cinder.tests.unit import test
49@@ -64,64 +56,52 @@ def mock_volume_types(f):
50
51
52 def volumeName(vid):
53- return 'os--volume--{id}'.format(id=vid)
54-
55-
56-def snapshotName(vtype, vid):
57- return 'os--snap--{t}--{id}'.format(t=vtype, id=vid)
58-
59-
60-class MockDisk(object):
61- def __init__(self, diskId):
62- self.id = diskId
63- self.generationLeft = -1
64- self.agCount = 14
65- self.agFree = 12
66- self.agAllocated = 1
67-
68-
69-class MockVolume(object):
70- def __init__(self, v):
71- self.name = v['name']
72-
73-
74-class MockTemplate(object):
75- def __init__(self, name):
76- self.name = name
77+ return 'os--volume-{id}'.format(id=vid)
78
79
80-class MockApiError(Exception):
81- def __init__(self, msg):
82- super(MockApiError, self).__init__(msg)
83+def snapshotName(vtype, vid, more=None):
84+ return 'os--{t}--{m}--snapshot-{id}'.format(
85+ t=vtype,
86+ m="none" if more is None else more,
87+ id=vid
88+ )
89
90
91 class MockAPI(object):
92- def __init__(self):
93- self._disks = {diskId: MockDisk(diskId) for diskId in (1, 2, 3, 4)}
94- self._disks[3].generationLeft = 42
95-
96- self._templates = [MockTemplate(name) for name in ('ssd', 'hdd')]
97-
98- def setlog(self, log):
99- self._log = log
100-
101- def disksList(self):
102+ def __init__(self, *args):
103+ self._disks = {}
104+ for disk_id in [1, 2, 3, 4]:
105+ self._disks[disk_id] = {
106+ 'id': disk_id,
107+ 'generationLeft': -1,
108+ 'agCount': 14,
109+ 'agFree': 12,
110+ 'agAllocated': 1
111+ }
112+ self._disks[3]['generationLeft'] = 42
113+
114+ self._templates = [{'name': name} for name in ('ssd', 'hdd')]
115+
116+ def disks_list(self):
117 return self._disks
118
119- def snapshotCreate(self, vname, snap):
120+ def snapshot_create(self, vname, snap):
121 snapshots[snap['name']] = dict(volumes[vname])
122
123- def snapshotUpdate(self, snap, data):
124+ def snapshot_update(self, snap, data):
125 sdata = snapshots[snap]
126 sdata.update(data)
127
128- def snapshotDelete(self, name):
129+ def snapshot_delete(self, name):
130 del snapshots[name]
131
132- def volumeCreate(self, vol):
133+ def volume_create(self, vol):
134 name = vol['name']
135 if name in volumes:
136- raise MockApiError('volume already exists')
137+ raise storpool_utils.StorPoolAPIError(
138+ 'none',
139+ {'error': {
140+ 'descr': 'volume already exists'}})
141 data = dict(vol)
142
143 if 'parent' in vol and 'template' not in vol:
144@@ -139,19 +119,22 @@ class MockAPI(object):
145
146 volumes[name] = data
147
148- def volumeDelete(self, name):
149+ def volume_delete(self, name):
150 del volumes[name]
151
152- def volumesList(self):
153- return [MockVolume(v[1]) for v in volumes.items()]
154+ def volumes_list(self):
155+ the_volumes = []
156+ for volume in volumes:
157+ the_volumes.append({'name': volume})
158+ return the_volumes
159
160- def volumeTemplatesList(self):
161+ def volume_templates_list(self):
162 return self._templates
163
164- def volumesReassign(self, json):
165+ def volumes_reassign(self, json):
166 pass
167
168- def volumeUpdate(self, name, data):
169+ def volume_update(self, name, data):
170 if 'size' in data:
171 volumes[name]['size'] = data['size']
172
173@@ -162,54 +145,23 @@ class MockAPI(object):
174 volumes[new_name]['name'] = new_name
175 del volumes[name]
176
177- def volumeRevert(self, name, data):
178+ def volume_revert(self, name, data):
179 if name not in volumes:
180- raise MockApiError('No such volume {name}'.format(name=name))
181+ raise storpool_utils.StorPoolAPIError(
182+ 'none',
183+ {'error': {
184+ 'descr': 'No such volume {name}'.format(name=name)}})
185
186 snapname = data['toSnapshot']
187 if snapname not in snapshots:
188- raise MockApiError('No such snapshot {name}'.format(name=snapname))
189+ raise storpool_utils.StorPoolAPIError(
190+ 'none',
191+ {'error': {
192+ 'descr': 'No such snapshot {name}'.format(name=snapname)}})
193
194 volumes[name] = dict(snapshots[snapname])
195
196
197-class MockAttachDB(object):
198- def __init__(self, log):
199- self._api = MockAPI()
200-
201- def api(self):
202- return self._api
203-
204- def volumeName(self, vid):
205- return volumeName(vid)
206-
207- def snapshotName(self, vtype, vid):
208- return snapshotName(vtype, vid)
209-
210-
211-def MockVolumeRevertDesc(toSnapshot):
212- return {'toSnapshot': toSnapshot}
213-
214-
215-def MockVolumeUpdateDesc(size):
216- return {'size': size}
217-
218-
219-def MockSPConfig(section = 's01'):
220- res = {}
221- m = re.match('^s0*([A-Za-z0-9]+)$', section)
222- if m:
223- res['SP_OURID'] = m.group(1)
224- return res
225-
226-
227-fakeStorPool.spapi.ApiError = MockApiError
228-fakeStorPool.spconfig.SPConfig = MockSPConfig
229-fakeStorPool.spopenstack.AttachDB = MockAttachDB
230-fakeStorPool.sptypes.VolumeRevertDesc = MockVolumeRevertDesc
231-fakeStorPool.sptypes.VolumeUpdateDesc = MockVolumeUpdateDesc
232-
233-
234 class MockVolumeDB(object):
235 """Simulate a Cinder database with a volume_get() method."""
236
237@@ -227,7 +179,16 @@ class MockVolumeDB(object):
238 }
239
240
241+def MockSPConfig(section = 's01'):
242+ res = {}
243+ m = re.match('^s0*([A-Za-z0-9]+)$', section)
244+ if m:
245+ res['SP_OURID'] = m.group(1)
246+ return res
247+
248+
249 @ddt.ddt
250+@mock.patch('os_brick.initiator.storpool_utils.get_conf', MockSPConfig)
251 class StorPoolTestCase(test.TestCase):
252
253 def setUp(self):
254@@ -243,7 +204,16 @@ class StorPoolTestCase(test.TestCase):
255
256 self.driver = driver.StorPoolDriver(execute=mock_exec,
257 configuration=self.cfg)
258- self.driver.check_for_setup_error()
259+
260+ with (
261+ mock.patch(
262+ 'os_brick.initiator.storpool_utils.get_conf'
263+ ) as get_conf,
264+ mock.patch(
265+ 'os_brick.initiator.storpool_utils.StorPoolAPI', MockAPI)
266+ ):
267+ get_conf.return_value = test_storpool_utils.SP_CONF
268+ self.driver.check_for_setup_error()
269
270 @ddt.data(
271 (5, TypeError),
272diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py
273index a8200a7f1..2dc7bb6be 100644
274--- a/cinder/volume/drivers/storpool.py
275+++ b/cinder/volume/drivers/storpool.py
276@@ -17,10 +17,10 @@
277
278 import platform
279
280+from os_brick.initiator import storpool_utils
281 from oslo_config import cfg
282 from oslo_log import log as logging
283 from oslo_utils import excutils
284-from oslo_utils import importutils
285 from oslo_utils import units
286
287 from cinder.common import constants
288@@ -34,13 +34,6 @@ from cinder.volume import volume_types
289
290 LOG = logging.getLogger(__name__)
291
292-storpool = importutils.try_import('storpool')
293-if storpool:
294- from storpool import spapi
295- from storpool import spconfig
296- from storpool import spopenstack
297- from storpool import sptypes
298-
299
300 storpool_opts = [
301 cfg.StrOpt('storpool_template',
302@@ -93,9 +86,13 @@ class StorPoolDriver(driver.VolumeDriver):
303 add ignore_errors to the internal _detach_volume() method
304 1.2.3 - Advertise some more driver capabilities.
305 2.0.0 - Implement revert_to_snapshot().
306+ 2.1.0 - Use the new API client in os-brick to communicate with the
307+ StorPool API instead of packages `storpool` and
308+ `storpool.spopenstack`
309+
310 """
311
312- VERSION = '2.0.0'
313+ VERSION = '2.1.0'
314 CI_WIKI_NAME = 'StorPool_distributed_storage_CI'
315
316 def __init__(self, *args, **kwargs):
317@@ -104,7 +101,8 @@ class StorPoolDriver(driver.VolumeDriver):
318 self._sp_config = None
319 self._ourId = None
320 self._ourIdInt = None
321- self._attach = None
322+ self._sp_api = None
323+ self._volume_prefix = None
324
325 @staticmethod
326 def get_driver_options():
327@@ -131,7 +129,8 @@ class StorPoolDriver(driver.VolumeDriver):
328
329 def create_volume(self, volume):
330 size = int(volume['size']) * units.Gi
331- name = self._attach.volumeName(volume['id'])
332+ name = storpool_utils.os_to_sp_volume_name(
333+ self._volume_prefix, volume['id'])
334 template = self._template_from_volume(volume)
335
336 create_request = {'name': name, 'size': size}
337@@ -142,8 +141,8 @@ class StorPoolDriver(driver.VolumeDriver):
338 self.configuration.storpool_replication
339
340 try:
341- self._attach.api().volumeCreate(create_request)
342- except spapi.ApiError as e:
343+ self._sp_api.volume_create(create_request)
344+ except storpool_utils.StorPoolAPIError as e:
345 raise self._backendException(e)
346
347 def _storpool_client_id(self, connector):
348@@ -151,7 +150,7 @@ class StorPoolDriver(driver.VolumeDriver):
349 if hostname == self.host or hostname == CONF.host:
350 hostname = platform.node()
351 try:
352- cfg = spconfig.SPConfig(section=hostname)
353+ cfg = storpool_utils.get_conf(section=hostname)
354 return int(cfg['SP_OURID'])
355 except KeyError:
356 return 65
357@@ -174,30 +173,36 @@ class StorPoolDriver(driver.VolumeDriver):
358 pass
359
360 def create_snapshot(self, snapshot):
361- volname = self._attach.volumeName(snapshot['volume_id'])
362- name = self._attach.snapshotName('snap', snapshot['id'])
363+ volname = storpool_utils.os_to_sp_volume_name(
364+ self._volume_prefix, snapshot['volume_id'])
365+ name = storpool_utils.os_to_sp_snapshot_name(
366+ self._volume_prefix, 'snap', snapshot['id'])
367 try:
368- self._attach.api().snapshotCreate(volname, {'name': name})
369- except spapi.ApiError as e:
370+ self._sp_api.snapshot_create(volname, {'name': name})
371+ except storpool_utils.StorPoolAPIError as e:
372 raise self._backendException(e)
373
374 def create_volume_from_snapshot(self, volume, snapshot):
375 size = int(volume['size']) * units.Gi
376- volname = self._attach.volumeName(volume['id'])
377- name = self._attach.snapshotName('snap', snapshot['id'])
378+ volname = storpool_utils.os_to_sp_volume_name(
379+ self._volume_prefix, volume['id'])
380+ name = storpool_utils.os_to_sp_snapshot_name(
381+ self._volume_prefix, 'snap', snapshot['id'])
382 try:
383- self._attach.api().volumeCreate({
384+ self._sp_api.volume_create({
385 'name': volname,
386 'size': size,
387 'parent': name
388 })
389- except spapi.ApiError as e:
390+ except storpool_utils.StorPoolAPIError as e:
391 raise self._backendException(e)
392
393 def create_cloned_volume(self, volume, src_vref):
394- refname = self._attach.volumeName(src_vref['id'])
395+ refname = storpool_utils.os_to_sp_volume_name(
396+ self._volume_prefix, src_vref['id'])
397 size = int(volume['size']) * units.Gi
398- volname = self._attach.volumeName(volume['id'])
399+ volname = storpool_utils.os_to_sp_volume_name(
400+ self._volume_prefix, volume['id'])
401
402 src_volume = self.db.volume_get(
403 context.get_admin_context(),
404@@ -213,50 +218,51 @@ class StorPoolDriver(driver.VolumeDriver):
405 if template == src_template:
406 LOG.info('Using baseOn to clone a volume into the same template')
407 try:
408- self._attach.api().volumeCreate({
409+ self._sp_api.volume_create({
410 'name': volname,
411 'size': size,
412 'baseOn': refname,
413 })
414- except spapi.ApiError as e:
415+ except storpool_utils.StorPoolAPIError as e:
416 raise self._backendException(e)
417
418 return None
419
420- snapname = self._attach.snapshotName('clone', volume['id'])
421+ snapname = storpool_utils.os_to_sp_snapshot_name(
422+ self._volume_prefix, 'clone', volume['id'])
423 LOG.info(
424 'A transient snapshot for a %(src)s -> %(dst)s template change',
425 {'src': src_template, 'dst': template})
426 try:
427- self._attach.api().snapshotCreate(refname, {'name': snapname})
428- except spapi.ApiError as e:
429+ self._sp_api.snapshot_create(refname, {'name': snapname})
430+ except storpool_utils.StorPoolAPIError as e:
431 if e.name != 'objectExists':
432 raise self._backendException(e)
433
434 try:
435 try:
436- self._attach.api().snapshotUpdate(
437+ self._sp_api.snapshot_update(
438 snapname,
439 {'template': template},
440 )
441- except spapi.ApiError as e:
442+ except storpool_utils.StorPoolAPIError as e:
443 raise self._backendException(e)
444
445 try:
446- self._attach.api().volumeCreate({
447+ self._sp_api.volume_create({
448 'name': volname,
449 'size': size,
450 'parent': snapname
451 })
452- except spapi.ApiError as e:
453+ except storpool_utils.StorPoolAPIError as e:
454 raise self._backendException(e)
455
456 try:
457- self._attach.api().snapshotUpdate(
458+ self._sp_api.snapshot_update(
459 snapname,
460 {'tags': {'transient': '1.0'}},
461 )
462- except spapi.ApiError as e:
463+ except storpool_utils.StorPoolAPIError as e:
464 raise self._backendException(e)
465 except Exception:
466 with excutils.save_and_reraise_exception():
467@@ -264,8 +270,8 @@ class StorPoolDriver(driver.VolumeDriver):
468 LOG.warning(
469 'Something went wrong, removing the transient snapshot'
470 )
471- self._attach.api().snapshotDelete(snapname)
472- except spapi.ApiError as e:
473+ self._sp_api.snapshot_delete(snapname)
474+ except storpool_utils.StorPoolAPIError as e:
475 LOG.error(
476 'Could not delete the %(name)s snapshot: %(err)s',
477 {'name': snapname, 'err': str(e)}
478@@ -278,57 +284,59 @@ class StorPoolDriver(driver.VolumeDriver):
479 pass
480
481 def delete_volume(self, volume):
482- name = self._attach.volumeName(volume['id'])
483+ name = storpool_utils.os_to_sp_volume_name(
484+ self._volume_prefix, volume['id'])
485 try:
486- self._attach.api().volumesReassign(
487- json=[{"volume": name, "detach": "all"}])
488- self._attach.api().volumeDelete(name)
489- except spapi.ApiError as e:
490+ self._sp_api.volumes_reassign([{"volume": name, "detach": "all"}])
491+ self._sp_api.volume_delete(name)
492+ except storpool_utils.StorPoolAPIError as e:
493 if e.name == 'objectDoesNotExist':
494 pass
495 else:
496 raise self._backendException(e)
497
498 def delete_snapshot(self, snapshot):
499- name = self._attach.snapshotName('snap', snapshot['id'])
500+ name = storpool_utils.os_to_sp_snapshot_name(
501+ self._volume_prefix, 'snap', snapshot['id'])
502 try:
503- self._attach.api().volumesReassign(
504- json=[{"snapshot": name, "detach": "all"}])
505- self._attach.api().snapshotDelete(name)
506- except spapi.ApiError as e:
507+ self._sp_api.volumes_reassign(
508+ [{"snapshot": name, "detach": "all"}])
509+ self._sp_api.snapshot_delete(name)
510+ except storpool_utils.StorPoolAPIError as e:
511 if e.name == 'objectDoesNotExist':
512 pass
513 else:
514 raise self._backendException(e)
515
516 def check_for_setup_error(self):
517- if storpool is None:
518- msg = _('storpool libraries not found')
519- raise exception.VolumeBackendAPIException(data=msg)
520-
521- self._attach = spopenstack.AttachDB(log=LOG)
522 try:
523- self._attach.api()
524+ self._sp_config = storpool_utils.get_conf()
525+ self._sp_api = storpool_utils.StorPoolAPI(
526+ self._sp_config["SP_API_HTTP_HOST"],
527+ self._sp_config["SP_API_HTTP_PORT"],
528+ self._sp_config["SP_AUTH_TOKEN"])
529+ self._volume_prefix = self._sp_config.get(
530+ "SP_OPENSTACK_VOLUME_PREFIX", "os")
531 except Exception as e:
532 LOG.error("StorPoolDriver API initialization failed: %s", e)
533 raise
534
535 def _update_volume_stats(self):
536 try:
537- dl = self._attach.api().disksList()
538- templates = self._attach.api().volumeTemplatesList()
539- except spapi.ApiError as e:
540+ dl = self._sp_api.disks_list()
541+ templates = self._sp_api.volume_templates_list()
542+ except storpool_utils.StorPoolAPIError as e:
543 raise self._backendException(e)
544 total = 0
545 used = 0
546 free = 0
547 agSize = 512 * units.Mi
548 for (id, desc) in dl.items():
549- if desc.generationLeft != -1:
550+ if desc['generationLeft'] != -1:
551 continue
552- total += desc.agCount * agSize
553- used += desc.agAllocated * agSize
554- free += desc.agFree * agSize * 4096 / (4096 + 128)
555+ total += desc['agCount'] * agSize
556+ used += desc['agAllocated'] * agSize
557+ free += desc['agFree'] * agSize * 4096 / (4096 + 128)
558
559 # Report the free space as if all new volumes will be created
560 # with StorPool replication 3; anything else is rare.
561@@ -347,8 +355,8 @@ class StorPoolDriver(driver.VolumeDriver):
562 pools = [dict(space, pool_name='default')]
563
564 pools += [dict(space,
565- pool_name='template_' + t.name,
566- storpool_template=t.name
567+ pool_name='template_' + t['name'],
568+ storpool_template=t['name']
569 ) for t in templates]
570
571 self._stats = {
572@@ -367,11 +375,11 @@ class StorPoolDriver(driver.VolumeDriver):
573
574 def extend_volume(self, volume, new_size):
575 size = int(new_size) * units.Gi
576- name = self._attach.volumeName(volume['id'])
577+ name = storpool_utils.os_to_sp_volume_name(
578+ self._volume_prefix, volume['id'])
579 try:
580- upd = sptypes.VolumeUpdateDesc(size=size)
581- self._attach.api().volumeUpdate(name, upd)
582- except spapi.ApiError as e:
583+ self._sp_api.volume_update(name, {'size': size})
584+ except storpool_utils.StorPoolAPIError as e:
585 raise self._backendException(e)
586
587 def ensure_export(self, context, volume):
588@@ -409,11 +417,11 @@ class StorPoolDriver(driver.VolumeDriver):
589 update['replication'] = repl
590
591 if update:
592- name = self._attach.volumeName(volume['id'])
593+ name = storpool_utils.os_to_sp_volume_name(
594+ self._volume_prefix, volume['id'])
595 try:
596- upd = sptypes.VolumeUpdateDesc(**update)
597- self._attach.api().volumeUpdate(name, upd)
598- except spapi.ApiError as e:
599+ self._sp_api.volume_update(name, **update)
600+ except storpool_utils.StorPoolAPIError as e:
601 raise self._backendException(e)
602
603 return True
604@@ -421,10 +429,12 @@ class StorPoolDriver(driver.VolumeDriver):
605 def update_migrated_volume(self, context, volume, new_volume,
606 original_volume_status):
607 orig_id = volume['id']
608- orig_name = self._attach.volumeName(orig_id)
609+ orig_name = storpool_utils.os_to_sp_volume_name(
610+ self._volume_prefix, orig_id)
611 temp_id = new_volume['id']
612- temp_name = self._attach.volumeName(temp_id)
613- vols = {v.name: True for v in self._attach.api().volumesList()}
614+ temp_name = storpool_utils.os_to_sp_volume_name(
615+ self._volume_prefix, temp_id)
616+ vols = {v['name']: True for v in self._sp_api.volumes_list()}
617 if temp_name not in vols:
618 LOG.error('StorPool update_migrated_volume(): it seems '
619 'that the StorPool volume "%(tid)s" was not '
620@@ -444,20 +454,17 @@ class StorPoolDriver(driver.VolumeDriver):
621 try:
622 LOG.debug('- rename "%(orig)s" to "%(int)s"',
623 {'orig': orig_name, 'int': int_name})
624- self._attach.api().volumeUpdate(orig_name,
625- {'rename': int_name})
626+ self._sp_api.volume_update(orig_name, {'rename': int_name})
627
628 LOG.debug('- rename "%(temp)s" to "%(orig)s"',
629 {'temp': temp_name, 'orig': orig_name})
630- self._attach.api().volumeUpdate(temp_name,
631- {'rename': orig_name})
632+ self._sp_api.volume_update(temp_name, {'rename': orig_name})
633
634 LOG.debug('- rename "%(int)s" to "%(temp)s"',
635 {'int': int_name, 'temp': temp_name})
636- self._attach.api().volumeUpdate(int_name,
637- {'rename': temp_name})
638+ self._sp_api.volume_update(int_name, {'rename': temp_name})
639 return {'_name_id': None}
640- except spapi.ApiError as e:
641+ except storpool_utils.StorPoolAPIError as e:
642 LOG.error('StorPool update_migrated_volume(): '
643 'could not rename a volume: '
644 '%(err)s',
645@@ -465,10 +472,9 @@ class StorPoolDriver(driver.VolumeDriver):
646 return {'_name_id': new_volume['_name_id'] or new_volume['id']}
647
648 try:
649- self._attach.api().volumeUpdate(temp_name,
650- {'rename': orig_name})
651+ self._sp_api.volume_update(temp_name, {'rename': orig_name})
652 return {'_name_id': None}
653- except spapi.ApiError as e:
654+ except storpool_utils.StorPoolAPIError as e:
655 LOG.error('StorPool update_migrated_volume(): '
656 'could not rename %(tname)s to %(oname)s: '
657 '%(err)s',
658@@ -476,12 +482,13 @@ class StorPoolDriver(driver.VolumeDriver):
659 return {'_name_id': new_volume['_name_id'] or new_volume['id']}
660
661 def revert_to_snapshot(self, context, volume, snapshot):
662- volname = self._attach.volumeName(volume['id'])
663- snapname = self._attach.snapshotName('snap', snapshot['id'])
664+ volname = storpool_utils.os_to_sp_volume_name(
665+ self._volume_prefix, volume['id'])
666+ snapname = storpool_utils.os_to_sp_snapshot_name(
667+ self._volume_prefix, 'snap', snapshot['id'])
668 try:
669- rev = sptypes.VolumeRevertDesc(toSnapshot=snapname)
670- self._attach.api().volumeRevert(volname, rev)
671- except spapi.ApiError as e:
672+ self._sp_api.volume_revert(volname, {'toSnapshot': snapname})
673+ except storpool_utils.StorPoolAPIError as e:
674 LOG.error('StorPool revert_to_snapshot(): could not revert '
675 'the %(vol_id)s volume to the %(snap_id)s snapshot: '
676 '%(err)s',
Biser Milanov7a541582025-01-29 16:31:14 +0200677diff --git a/doc/source/configuration/block-storage/drivers/storpool-volume-driver.rst b/doc/source/configuration/block-storage/drivers/storpool-volume-driver.rst
678index d2c5895a9..e9209b0cc 100644
679--- a/doc/source/configuration/block-storage/drivers/storpool-volume-driver.rst
680+++ b/doc/source/configuration/block-storage/drivers/storpool-volume-driver.rst
681@@ -26,14 +26,6 @@ Prerequisites
682 images, then the node running the ``cinder-volume`` service must also have
683 access to the StorPool data network and run the ``storpool_block`` service.
684
685-* All nodes that need to access the StorPool API (the compute nodes and
686- the node running the ``cinder-volume`` service) must have the following
687- packages installed:
688-
689- * storpool-config (part of the StorPool installation)
690- * the storpool Python bindings package
691- * the storpool.spopenstack Python helper package
692-
693 Configuring the StorPool volume driver
694 --------------------------------------
695
Biser Milanov84585622025-01-27 15:11:47 +0200696diff --git a/releasenotes/notes/storpool-move-api-and-config-code-in-tree-92cfe30690b78ef1.yaml b/releasenotes/notes/storpool-move-api-and-config-code-in-tree-92cfe30690b78ef1.yaml
697new file mode 100644
698index 000000000..13c2cbd65
699--- /dev/null
700+++ b/releasenotes/notes/storpool-move-api-and-config-code-in-tree-92cfe30690b78ef1.yaml
701@@ -0,0 +1,8 @@
702+---
703+other:
704+ - |
705+ Use the new implementation in os-brick for communicating with the
706+ StorPool API and reading StorPool configuration files.
707+
708+ The StorPool backend no longer requires the OpenStack nodes to have
709+ the Python packages `storpool` and `storpool.spopenstack` installed.
710diff --git a/requirements.txt b/requirements.txt
711index c7aee22ec..fbb911648 100644
712--- a/requirements.txt
713+++ b/requirements.txt
714@@ -50,7 +50,7 @@ tenacity>=6.3.1 # Apache-2.0
715 WebOb>=1.8.6 # MIT
716 oslo.i18n>=5.1.0 # Apache-2.0
717 oslo.vmware>=3.10.0 # Apache-2.0
718-os-brick>=6.0.0 # Apache-2.0
719+os-brick>=6.10.0 # Apache-2.0
720 os-win>=5.5.0 # Apache-2.0
721 tooz>=2.8.0 # Apache-2.0
722 google-api-python-client>=1.11.0 # Apache-2.0
723--
7242.43.0
725