Add the use-os-brick-api-client patch
Change-Id: I3f74c33d1a8260a766272ff985aaba12eee79053
diff --git a/patches/openstack/cinder/use-os-brick-api-client.patch b/patches/openstack/cinder/use-os-brick-api-client.patch
new file mode 100644
index 0000000..ec9cc76
--- /dev/null
+++ b/patches/openstack/cinder/use-os-brick-api-client.patch
@@ -0,0 +1,705 @@
+From e22f4c612183d4552ea3f4917537dd50963a5d26 Mon Sep 17 00:00:00 2001
+From: Biser Milanov <biser.milanov@storpool.com>
+Date: Mon, 14 Oct 2024 14:25:22 +0300
+Subject: [PATCH] StorPool: Use os-brick instead of packages `storpool` and
+ `storpool.spopenstack`
+
+Stop depending on modules `storpool` and `storpool.spopenstack` for
+access to the StorPool API and reading the StorPool configuration from
+files. Use the new new in-tree implementation introduced in `os-brick`.
+
+Change-Id: Ieb6be04133e1639b3fa6e3a322604b366e909d81
+---
+ .../unit/volume/drivers/test_storpool.py | 168 +++++++---------
+ cinder/volume/drivers/storpool.py | 183 +++++++++---------
+ ...-config-code-in-tree-92cfe30690b78ef1.yaml | 8 +
+ requirements.txt | 2 +-
+ 4 files changed, 173 insertions(+), 188 deletions(-)
+ create mode 100644 releasenotes/notes/storpool-move-api-and-config-code-in-tree-92cfe30690b78ef1.yaml
+
+diff --git a/cinder/tests/unit/volume/drivers/test_storpool.py b/cinder/tests/unit/volume/drivers/test_storpool.py
+index 44707d0b8..2015c734d 100644
+--- a/cinder/tests/unit/volume/drivers/test_storpool.py
++++ b/cinder/tests/unit/volume/drivers/test_storpool.py
+@@ -16,21 +16,13 @@
+
+ import itertools
+ import re
+-import sys
+ from unittest import mock
+
+ import ddt
++from os_brick.initiator import storpool_utils
++from os_brick.tests.initiator import test_storpool_utils
+ from oslo_utils import units
+
+-
+-fakeStorPool = mock.Mock()
+-fakeStorPool.spopenstack = mock.Mock()
+-fakeStorPool.spapi = mock.Mock()
+-fakeStorPool.spconfig = mock.Mock()
+-fakeStorPool.sptypes = mock.Mock()
+-sys.modules['storpool'] = fakeStorPool
+-
+-
+ from cinder import exception
+ from cinder.tests.unit import fake_constants
+ from cinder.tests.unit import test
+@@ -64,64 +56,52 @@ def mock_volume_types(f):
+
+
+ def volumeName(vid):
+- return 'os--volume--{id}'.format(id=vid)
+-
+-
+-def snapshotName(vtype, vid):
+- return 'os--snap--{t}--{id}'.format(t=vtype, id=vid)
+-
+-
+-class MockDisk(object):
+- def __init__(self, diskId):
+- self.id = diskId
+- self.generationLeft = -1
+- self.agCount = 14
+- self.agFree = 12
+- self.agAllocated = 1
+-
+-
+-class MockVolume(object):
+- def __init__(self, v):
+- self.name = v['name']
+-
+-
+-class MockTemplate(object):
+- def __init__(self, name):
+- self.name = name
++ return 'os--volume-{id}'.format(id=vid)
+
+
+-class MockApiError(Exception):
+- def __init__(self, msg):
+- super(MockApiError, self).__init__(msg)
++def snapshotName(vtype, vid, more=None):
++ return 'os--{t}--{m}--snapshot-{id}'.format(
++ t=vtype,
++ m="none" if more is None else more,
++ id=vid
++ )
+
+
+ class MockAPI(object):
+- def __init__(self):
+- self._disks = {diskId: MockDisk(diskId) for diskId in (1, 2, 3, 4)}
+- self._disks[3].generationLeft = 42
+-
+- self._templates = [MockTemplate(name) for name in ('ssd', 'hdd')]
+-
+- def setlog(self, log):
+- self._log = log
+-
+- def disksList(self):
++ def __init__(self, *args):
++ self._disks = {}
++ for disk_id in [1, 2, 3, 4]:
++ self._disks[disk_id] = {
++ 'id': disk_id,
++ 'generationLeft': -1,
++ 'agCount': 14,
++ 'agFree': 12,
++ 'agAllocated': 1
++ }
++ self._disks[3]['generationLeft'] = 42
++
++ self._templates = [{'name': name} for name in ('ssd', 'hdd')]
++
++ def disks_list(self):
+ return self._disks
+
+- def snapshotCreate(self, vname, snap):
++ def snapshot_create(self, vname, snap):
+ snapshots[snap['name']] = dict(volumes[vname])
+
+- def snapshotUpdate(self, snap, data):
++ def snapshot_update(self, snap, data):
+ sdata = snapshots[snap]
+ sdata.update(data)
+
+- def snapshotDelete(self, name):
++ def snapshot_delete(self, name):
+ del snapshots[name]
+
+- def volumeCreate(self, vol):
++ def volume_create(self, vol):
+ name = vol['name']
+ if name in volumes:
+- raise MockApiError('volume already exists')
++ raise storpool_utils.StorPoolAPIError(
++ 'none',
++ {'error': {
++ 'descr': 'volume already exists'}})
+ data = dict(vol)
+
+ if 'parent' in vol and 'template' not in vol:
+@@ -139,19 +119,22 @@ class MockAPI(object):
+
+ volumes[name] = data
+
+- def volumeDelete(self, name):
++ def volume_delete(self, name):
+ del volumes[name]
+
+- def volumesList(self):
+- return [MockVolume(v[1]) for v in volumes.items()]
++ def volumes_list(self):
++ the_volumes = []
++ for volume in volumes:
++ the_volumes.append({'name': volume})
++ return the_volumes
+
+- def volumeTemplatesList(self):
++ def volume_templates_list(self):
+ return self._templates
+
+- def volumesReassign(self, json):
++ def volumes_reassign(self, json):
+ pass
+
+- def volumeUpdate(self, name, data):
++ def volume_update(self, name, data):
+ if 'size' in data:
+ volumes[name]['size'] = data['size']
+
+@@ -162,54 +145,23 @@ class MockAPI(object):
+ volumes[new_name]['name'] = new_name
+ del volumes[name]
+
+- def volumeRevert(self, name, data):
++ def volume_revert(self, name, data):
+ if name not in volumes:
+- raise MockApiError('No such volume {name}'.format(name=name))
++ raise storpool_utils.StorPoolAPIError(
++ 'none',
++ {'error': {
++ 'descr': 'No such volume {name}'.format(name=name)}})
+
+ snapname = data['toSnapshot']
+ if snapname not in snapshots:
+- raise MockApiError('No such snapshot {name}'.format(name=snapname))
++ raise storpool_utils.StorPoolAPIError(
++ 'none',
++ {'error': {
++ 'descr': 'No such snapshot {name}'.format(name=snapname)}})
+
+ volumes[name] = dict(snapshots[snapname])
+
+
+-class MockAttachDB(object):
+- def __init__(self, log):
+- self._api = MockAPI()
+-
+- def api(self):
+- return self._api
+-
+- def volumeName(self, vid):
+- return volumeName(vid)
+-
+- def snapshotName(self, vtype, vid):
+- return snapshotName(vtype, vid)
+-
+-
+-def MockVolumeRevertDesc(toSnapshot):
+- return {'toSnapshot': toSnapshot}
+-
+-
+-def MockVolumeUpdateDesc(size):
+- return {'size': size}
+-
+-
+-def MockSPConfig(section = 's01'):
+- res = {}
+- m = re.match('^s0*([A-Za-z0-9]+)$', section)
+- if m:
+- res['SP_OURID'] = m.group(1)
+- return res
+-
+-
+-fakeStorPool.spapi.ApiError = MockApiError
+-fakeStorPool.spconfig.SPConfig = MockSPConfig
+-fakeStorPool.spopenstack.AttachDB = MockAttachDB
+-fakeStorPool.sptypes.VolumeRevertDesc = MockVolumeRevertDesc
+-fakeStorPool.sptypes.VolumeUpdateDesc = MockVolumeUpdateDesc
+-
+-
+ class MockVolumeDB(object):
+ """Simulate a Cinder database with a volume_get() method."""
+
+@@ -227,7 +179,16 @@ class MockVolumeDB(object):
+ }
+
+
++def MockSPConfig(section = 's01'):
++ res = {}
++ m = re.match('^s0*([A-Za-z0-9]+)$', section)
++ if m:
++ res['SP_OURID'] = m.group(1)
++ return res
++
++
+ @ddt.ddt
++@mock.patch('os_brick.initiator.storpool_utils.get_conf', MockSPConfig)
+ class StorPoolTestCase(test.TestCase):
+
+ def setUp(self):
+@@ -243,7 +204,16 @@ class StorPoolTestCase(test.TestCase):
+
+ self.driver = driver.StorPoolDriver(execute=mock_exec,
+ configuration=self.cfg)
+- self.driver.check_for_setup_error()
++
++ with (
++ mock.patch(
++ 'os_brick.initiator.storpool_utils.get_conf'
++ ) as get_conf,
++ mock.patch(
++ 'os_brick.initiator.storpool_utils.StorPoolAPI', MockAPI)
++ ):
++ get_conf.return_value = test_storpool_utils.SP_CONF
++ self.driver.check_for_setup_error()
+
+ @ddt.data(
+ (5, TypeError),
+diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py
+index a8200a7f1..2dc7bb6be 100644
+--- a/cinder/volume/drivers/storpool.py
++++ b/cinder/volume/drivers/storpool.py
+@@ -17,10 +17,10 @@
+
+ import platform
+
++from os_brick.initiator import storpool_utils
+ from oslo_config import cfg
+ from oslo_log import log as logging
+ from oslo_utils import excutils
+-from oslo_utils import importutils
+ from oslo_utils import units
+
+ from cinder.common import constants
+@@ -34,13 +34,6 @@ from cinder.volume import volume_types
+
+ LOG = logging.getLogger(__name__)
+
+-storpool = importutils.try_import('storpool')
+-if storpool:
+- from storpool import spapi
+- from storpool import spconfig
+- from storpool import spopenstack
+- from storpool import sptypes
+-
+
+ storpool_opts = [
+ cfg.StrOpt('storpool_template',
+@@ -93,9 +86,13 @@ class StorPoolDriver(driver.VolumeDriver):
+ add ignore_errors to the internal _detach_volume() method
+ 1.2.3 - Advertise some more driver capabilities.
+ 2.0.0 - Implement revert_to_snapshot().
++ 2.1.0 - Use the new API client in os-brick to communicate with the
++ StorPool API instead of packages `storpool` and
++ `storpool.spopenstack`
++
+ """
+
+- VERSION = '2.0.0'
++ VERSION = '2.1.0'
+ CI_WIKI_NAME = 'StorPool_distributed_storage_CI'
+
+ def __init__(self, *args, **kwargs):
+@@ -104,7 +101,8 @@ class StorPoolDriver(driver.VolumeDriver):
+ self._sp_config = None
+ self._ourId = None
+ self._ourIdInt = None
+- self._attach = None
++ self._sp_api = None
++ self._volume_prefix = None
+
+ @staticmethod
+ def get_driver_options():
+@@ -131,7 +129,8 @@ class StorPoolDriver(driver.VolumeDriver):
+
+ def create_volume(self, volume):
+ size = int(volume['size']) * units.Gi
+- name = self._attach.volumeName(volume['id'])
++ name = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
+ template = self._template_from_volume(volume)
+
+ create_request = {'name': name, 'size': size}
+@@ -142,8 +141,8 @@ class StorPoolDriver(driver.VolumeDriver):
+ self.configuration.storpool_replication
+
+ try:
+- self._attach.api().volumeCreate(create_request)
+- except spapi.ApiError as e:
++ self._sp_api.volume_create(create_request)
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ def _storpool_client_id(self, connector):
+@@ -151,7 +150,7 @@ class StorPoolDriver(driver.VolumeDriver):
+ if hostname == self.host or hostname == CONF.host:
+ hostname = platform.node()
+ try:
+- cfg = spconfig.SPConfig(section=hostname)
++ cfg = storpool_utils.get_conf(section=hostname)
+ return int(cfg['SP_OURID'])
+ except KeyError:
+ return 65
+@@ -174,30 +173,36 @@ class StorPoolDriver(driver.VolumeDriver):
+ pass
+
+ def create_snapshot(self, snapshot):
+- volname = self._attach.volumeName(snapshot['volume_id'])
+- name = self._attach.snapshotName('snap', snapshot['id'])
++ volname = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, snapshot['volume_id'])
++ name = storpool_utils.os_to_sp_snapshot_name(
++ self._volume_prefix, 'snap', snapshot['id'])
+ try:
+- self._attach.api().snapshotCreate(volname, {'name': name})
+- except spapi.ApiError as e:
++ self._sp_api.snapshot_create(volname, {'name': name})
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ def create_volume_from_snapshot(self, volume, snapshot):
+ size = int(volume['size']) * units.Gi
+- volname = self._attach.volumeName(volume['id'])
+- name = self._attach.snapshotName('snap', snapshot['id'])
++ volname = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
++ name = storpool_utils.os_to_sp_snapshot_name(
++ self._volume_prefix, 'snap', snapshot['id'])
+ try:
+- self._attach.api().volumeCreate({
++ self._sp_api.volume_create({
+ 'name': volname,
+ 'size': size,
+ 'parent': name
+ })
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ def create_cloned_volume(self, volume, src_vref):
+- refname = self._attach.volumeName(src_vref['id'])
++ refname = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, src_vref['id'])
+ size = int(volume['size']) * units.Gi
+- volname = self._attach.volumeName(volume['id'])
++ volname = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
+
+ src_volume = self.db.volume_get(
+ context.get_admin_context(),
+@@ -213,50 +218,51 @@ class StorPoolDriver(driver.VolumeDriver):
+ if template == src_template:
+ LOG.info('Using baseOn to clone a volume into the same template')
+ try:
+- self._attach.api().volumeCreate({
++ self._sp_api.volume_create({
+ 'name': volname,
+ 'size': size,
+ 'baseOn': refname,
+ })
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ return None
+
+- snapname = self._attach.snapshotName('clone', volume['id'])
++ snapname = storpool_utils.os_to_sp_snapshot_name(
++ self._volume_prefix, 'clone', volume['id'])
+ LOG.info(
+ 'A transient snapshot for a %(src)s -> %(dst)s template change',
+ {'src': src_template, 'dst': template})
+ try:
+- self._attach.api().snapshotCreate(refname, {'name': snapname})
+- except spapi.ApiError as e:
++ self._sp_api.snapshot_create(refname, {'name': snapname})
++ except storpool_utils.StorPoolAPIError as e:
+ if e.name != 'objectExists':
+ raise self._backendException(e)
+
+ try:
+ try:
+- self._attach.api().snapshotUpdate(
++ self._sp_api.snapshot_update(
+ snapname,
+ {'template': template},
+ )
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ try:
+- self._attach.api().volumeCreate({
++ self._sp_api.volume_create({
+ 'name': volname,
+ 'size': size,
+ 'parent': snapname
+ })
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ try:
+- self._attach.api().snapshotUpdate(
++ self._sp_api.snapshot_update(
+ snapname,
+ {'tags': {'transient': '1.0'}},
+ )
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+@@ -264,8 +270,8 @@ class StorPoolDriver(driver.VolumeDriver):
+ LOG.warning(
+ 'Something went wrong, removing the transient snapshot'
+ )
+- self._attach.api().snapshotDelete(snapname)
+- except spapi.ApiError as e:
++ self._sp_api.snapshot_delete(snapname)
++ except storpool_utils.StorPoolAPIError as e:
+ LOG.error(
+ 'Could not delete the %(name)s snapshot: %(err)s',
+ {'name': snapname, 'err': str(e)}
+@@ -278,57 +284,59 @@ class StorPoolDriver(driver.VolumeDriver):
+ pass
+
+ def delete_volume(self, volume):
+- name = self._attach.volumeName(volume['id'])
++ name = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
+ try:
+- self._attach.api().volumesReassign(
+- json=[{"volume": name, "detach": "all"}])
+- self._attach.api().volumeDelete(name)
+- except spapi.ApiError as e:
++ self._sp_api.volumes_reassign([{"volume": name, "detach": "all"}])
++ self._sp_api.volume_delete(name)
++ except storpool_utils.StorPoolAPIError as e:
+ if e.name == 'objectDoesNotExist':
+ pass
+ else:
+ raise self._backendException(e)
+
+ def delete_snapshot(self, snapshot):
+- name = self._attach.snapshotName('snap', snapshot['id'])
++ name = storpool_utils.os_to_sp_snapshot_name(
++ self._volume_prefix, 'snap', snapshot['id'])
+ try:
+- self._attach.api().volumesReassign(
+- json=[{"snapshot": name, "detach": "all"}])
+- self._attach.api().snapshotDelete(name)
+- except spapi.ApiError as e:
++ self._sp_api.volumes_reassign(
++ [{"snapshot": name, "detach": "all"}])
++ self._sp_api.snapshot_delete(name)
++ except storpool_utils.StorPoolAPIError as e:
+ if e.name == 'objectDoesNotExist':
+ pass
+ else:
+ raise self._backendException(e)
+
+ def check_for_setup_error(self):
+- if storpool is None:
+- msg = _('storpool libraries not found')
+- raise exception.VolumeBackendAPIException(data=msg)
+-
+- self._attach = spopenstack.AttachDB(log=LOG)
+ try:
+- self._attach.api()
++ self._sp_config = storpool_utils.get_conf()
++ self._sp_api = storpool_utils.StorPoolAPI(
++ self._sp_config["SP_API_HTTP_HOST"],
++ self._sp_config["SP_API_HTTP_PORT"],
++ self._sp_config["SP_AUTH_TOKEN"])
++ self._volume_prefix = self._sp_config.get(
++ "SP_OPENSTACK_VOLUME_PREFIX", "os")
+ except Exception as e:
+ LOG.error("StorPoolDriver API initialization failed: %s", e)
+ raise
+
+ def _update_volume_stats(self):
+ try:
+- dl = self._attach.api().disksList()
+- templates = self._attach.api().volumeTemplatesList()
+- except spapi.ApiError as e:
++ dl = self._sp_api.disks_list()
++ templates = self._sp_api.volume_templates_list()
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+ total = 0
+ used = 0
+ free = 0
+ agSize = 512 * units.Mi
+ for (id, desc) in dl.items():
+- if desc.generationLeft != -1:
++ if desc['generationLeft'] != -1:
+ continue
+- total += desc.agCount * agSize
+- used += desc.agAllocated * agSize
+- free += desc.agFree * agSize * 4096 / (4096 + 128)
++ total += desc['agCount'] * agSize
++ used += desc['agAllocated'] * agSize
++ free += desc['agFree'] * agSize * 4096 / (4096 + 128)
+
+ # Report the free space as if all new volumes will be created
+ # with StorPool replication 3; anything else is rare.
+@@ -347,8 +355,8 @@ class StorPoolDriver(driver.VolumeDriver):
+ pools = [dict(space, pool_name='default')]
+
+ pools += [dict(space,
+- pool_name='template_' + t.name,
+- storpool_template=t.name
++ pool_name='template_' + t['name'],
++ storpool_template=t['name']
+ ) for t in templates]
+
+ self._stats = {
+@@ -367,11 +375,11 @@ class StorPoolDriver(driver.VolumeDriver):
+
+ def extend_volume(self, volume, new_size):
+ size = int(new_size) * units.Gi
+- name = self._attach.volumeName(volume['id'])
++ name = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
+ try:
+- upd = sptypes.VolumeUpdateDesc(size=size)
+- self._attach.api().volumeUpdate(name, upd)
+- except spapi.ApiError as e:
++ self._sp_api.volume_update(name, {'size': size})
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ def ensure_export(self, context, volume):
+@@ -409,11 +417,11 @@ class StorPoolDriver(driver.VolumeDriver):
+ update['replication'] = repl
+
+ if update:
+- name = self._attach.volumeName(volume['id'])
++ name = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
+ try:
+- upd = sptypes.VolumeUpdateDesc(**update)
+- self._attach.api().volumeUpdate(name, upd)
+- except spapi.ApiError as e:
++ self._sp_api.volume_update(name, **update)
++ except storpool_utils.StorPoolAPIError as e:
+ raise self._backendException(e)
+
+ return True
+@@ -421,10 +429,12 @@ class StorPoolDriver(driver.VolumeDriver):
+ def update_migrated_volume(self, context, volume, new_volume,
+ original_volume_status):
+ orig_id = volume['id']
+- orig_name = self._attach.volumeName(orig_id)
++ orig_name = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, orig_id)
+ temp_id = new_volume['id']
+- temp_name = self._attach.volumeName(temp_id)
+- vols = {v.name: True for v in self._attach.api().volumesList()}
++ temp_name = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, temp_id)
++ vols = {v['name']: True for v in self._sp_api.volumes_list()}
+ if temp_name not in vols:
+ LOG.error('StorPool update_migrated_volume(): it seems '
+ 'that the StorPool volume "%(tid)s" was not '
+@@ -444,20 +454,17 @@ class StorPoolDriver(driver.VolumeDriver):
+ try:
+ LOG.debug('- rename "%(orig)s" to "%(int)s"',
+ {'orig': orig_name, 'int': int_name})
+- self._attach.api().volumeUpdate(orig_name,
+- {'rename': int_name})
++ self._sp_api.volume_update(orig_name, {'rename': int_name})
+
+ LOG.debug('- rename "%(temp)s" to "%(orig)s"',
+ {'temp': temp_name, 'orig': orig_name})
+- self._attach.api().volumeUpdate(temp_name,
+- {'rename': orig_name})
++ self._sp_api.volume_update(temp_name, {'rename': orig_name})
+
+ LOG.debug('- rename "%(int)s" to "%(temp)s"',
+ {'int': int_name, 'temp': temp_name})
+- self._attach.api().volumeUpdate(int_name,
+- {'rename': temp_name})
++ self._sp_api.volume_update(int_name, {'rename': temp_name})
+ return {'_name_id': None}
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ LOG.error('StorPool update_migrated_volume(): '
+ 'could not rename a volume: '
+ '%(err)s',
+@@ -465,10 +472,9 @@ class StorPoolDriver(driver.VolumeDriver):
+ return {'_name_id': new_volume['_name_id'] or new_volume['id']}
+
+ try:
+- self._attach.api().volumeUpdate(temp_name,
+- {'rename': orig_name})
++ self._sp_api.volume_update(temp_name, {'rename': orig_name})
+ return {'_name_id': None}
+- except spapi.ApiError as e:
++ except storpool_utils.StorPoolAPIError as e:
+ LOG.error('StorPool update_migrated_volume(): '
+ 'could not rename %(tname)s to %(oname)s: '
+ '%(err)s',
+@@ -476,12 +482,13 @@ class StorPoolDriver(driver.VolumeDriver):
+ return {'_name_id': new_volume['_name_id'] or new_volume['id']}
+
+ def revert_to_snapshot(self, context, volume, snapshot):
+- volname = self._attach.volumeName(volume['id'])
+- snapname = self._attach.snapshotName('snap', snapshot['id'])
++ volname = storpool_utils.os_to_sp_volume_name(
++ self._volume_prefix, volume['id'])
++ snapname = storpool_utils.os_to_sp_snapshot_name(
++ self._volume_prefix, 'snap', snapshot['id'])
+ try:
+- rev = sptypes.VolumeRevertDesc(toSnapshot=snapname)
+- self._attach.api().volumeRevert(volname, rev)
+- except spapi.ApiError as e:
++ self._sp_api.volume_revert(volname, {'toSnapshot': snapname})
++ except storpool_utils.StorPoolAPIError as e:
+ LOG.error('StorPool revert_to_snapshot(): could not revert '
+ 'the %(vol_id)s volume to the %(snap_id)s snapshot: '
+ '%(err)s',
+diff --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
+new file mode 100644
+index 000000000..13c2cbd65
+--- /dev/null
++++ b/releasenotes/notes/storpool-move-api-and-config-code-in-tree-92cfe30690b78ef1.yaml
+@@ -0,0 +1,8 @@
++---
++other:
++ - |
++ Use the new implementation in os-brick for communicating with the
++ StorPool API and reading StorPool configuration files.
++
++ The StorPool backend no longer requires the OpenStack nodes to have
++ the Python packages `storpool` and `storpool.spopenstack` installed.
+diff --git a/requirements.txt b/requirements.txt
+index c7aee22ec..fbb911648 100644
+--- a/requirements.txt
++++ b/requirements.txt
+@@ -50,7 +50,7 @@ tenacity>=6.3.1 # Apache-2.0
+ WebOb>=1.8.6 # MIT
+ oslo.i18n>=5.1.0 # Apache-2.0
+ oslo.vmware>=3.10.0 # Apache-2.0
+-os-brick>=6.0.0 # Apache-2.0
++os-brick>=6.10.0 # Apache-2.0
+ os-win>=5.5.0 # Apache-2.0
+ tooz>=2.8.0 # Apache-2.0
+ google-api-python-client>=1.11.0 # Apache-2.0
+--
+2.43.0
+
diff --git a/patches/series.experimental b/patches/series.experimental
index 125c9ca..6caca64 100644
--- a/patches/series.experimental
+++ b/patches/series.experimental
@@ -9,6 +9,7 @@
openstack/cinder/sep-sp-clone-volume.patch
openstack/cinder/sep-clone-across-pools.patch
openstack/cinder/sep-sp-clone-across-pools.patch
+openstack/cinder/use-os-brick-api-client.patch
openstack/cinder/sep-sp-iscsi.patch
openstack/cinderlib/storpool-test-20190910.patch
openstack/devstack/eatmydata.patch