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