Biser Milanov | 8458562 | 2025-01-27 15:11:47 +0200 | [diff] [blame^] | 1 | From e22f4c612183d4552ea3f4917537dd50963a5d26 Mon Sep 17 00:00:00 2001 |
| 2 | From: Biser Milanov <biser.milanov@storpool.com> |
| 3 | Date: Mon, 14 Oct 2024 14:25:22 +0300 |
| 4 | Subject: [PATCH] StorPool: Use os-brick instead of packages `storpool` and |
| 5 | `storpool.spopenstack` |
| 6 | |
| 7 | Stop depending on modules `storpool` and `storpool.spopenstack` for |
| 8 | access to the StorPool API and reading the StorPool configuration from |
| 9 | files. Use the new new in-tree implementation introduced in `os-brick`. |
| 10 | |
| 11 | Change-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 | |
| 20 | diff --git a/cinder/tests/unit/volume/drivers/test_storpool.py b/cinder/tests/unit/volume/drivers/test_storpool.py |
| 21 | index 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), |
| 271 | diff --git a/cinder/volume/drivers/storpool.py b/cinder/volume/drivers/storpool.py |
| 272 | index 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', |
| 676 | 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 |
| 677 | new file mode 100644 |
| 678 | index 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. |
| 690 | diff --git a/requirements.txt b/requirements.txt |
| 691 | index 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 | -- |
| 704 | 2.43.0 |
| 705 | |