Add datastore flavor association support

Trove currently supports associating flavors with datastore versions
through trove-manage, which requires direct access to the Trove control
plane. This is unsuitable for deployments where datastore version
maintenance is automated through API workflows or control plane access
is restricted.

Add --flavors and --remove-flavors options to the openstack datastore
version set command. These options allow operators to manage datastore
version flavor associations through the Trove management API.

Depends-On: https://review.opendev.org/c/openstack/trove/+/997965
Change-Id: I0507623b4fa5ce045c9808e790428590565be17a
Signed-off-by: Olessya Khussainova <freckled.cellofun@gmail.com>
This commit is contained in:
Olessya Khussainova authored and Olessya Khussainova committed 2026-09-29 09:04:09 +05:00
1 parent 44ca384d8a
commit 2e1e050276
5 files changed
+105 -2

No files matched your search

@@ -0,0 +1,6 @@
---
features:
- |
Adds ``--flavors`` and ``--remove-flavors`` options to the
``openstack datastore version set`` command for adding and removing
datastore version flavor associations.
+22 -2
View File
@@ -317,6 +317,17 @@ class UpdateDatastoreVersion(command.Command):
default=None,
action='store_const',
const='false')
flavor_group = parser.add_mutually_exclusive_group()
flavor_group.add_argument('--flavors', dest='flavor_ids',
metavar='<flavor_ids>',
nargs='+',
default=None,
help=_('Flavor IDs to associate.'))
flavor_group.add_argument('--remove-flavors',
metavar='<flavor_ids>',
nargs='+',
default=None,
help=_('Flavor IDs to disassociate.'))
volume_type_group = parser.add_mutually_exclusive_group()
volume_type_group.add_argument('--volume-types',
dest='volume_type_ids',
@@ -353,6 +364,15 @@ class UpdateDatastoreVersion(command.Command):
name=parsed_args.version_name
)
if parsed_args.flavor_ids is not None:
client.add_flavors(
parsed_args.datastore_version_id,
parsed_args.flavor_ids)
elif parsed_args.remove_flavors is not None:
for flavor_id in parsed_args.remove_flavors:
client.delete_flavor(
parsed_args.datastore_version_id, flavor_id)
if parsed_args.volume_type_ids is not None:
client.add_volume_types(
parsed_args.datastore_version_id,
@@ -360,8 +380,8 @@ class UpdateDatastoreVersion(command.Command):
elif parsed_args.remove_volume_types is not None:
for volume_type_id in parsed_args.remove_volume_types:
client.delete_volume_type(
parsed_args.datastore_version_id,
volume_type_id)
parsed_args.datastore_version_id, volume_type_id)
except Exception as e:
msg = (_("Failed to update datastore version %(version)s: %(e)s")
% {'version': parsed_args.datastore_version_id, 'e': e})
@@ -202,6 +202,29 @@ class TestUpdateDatastoreVersion(TestDatastores):
registry_ext="registry-ext",
repl_strategy="repl_strategy", name=None)
def test_update_datastore_version_flavors(self):
version_id = uuidutils.generate_uuid()
args = [version_id, '--flavors', 'flavor-1', 'flavor-2']
parsed_args = self.check_parser(self.cmd, args, [])
self.cmd.take_action(parsed_args)
self.dsversion_mgmt_client.add_flavors.assert_called_once_with(
version_id, ['flavor-1', 'flavor-2'])
def test_update_datastore_version_remove_flavors(self):
version_id = uuidutils.generate_uuid()
args = [version_id, '--remove-flavors', 'flavor-1', 'flavor-2']
parsed_args = self.check_parser(self.cmd, args, [])
self.cmd.take_action(parsed_args)
self.dsversion_mgmt_client.delete_flavor.assert_has_calls([
mock.call(version_id, 'flavor-1'),
mock.call(version_id, 'flavor-2')])
self.assertEqual(
2, self.dsversion_mgmt_client.delete_flavor.call_count)
def test_update_datastore_version_volume_types(self):
version_id = uuidutils.generate_uuid()
args = [version_id, '--volume-types', 'type-1', 'type-2']
+31
View File
@@ -294,6 +294,37 @@ class MgmtDatastoreVersionsTest(testtools.TestCase):
self.assertRaises(Exception, self.ds_version.edit, 'ds-version-1',
"new-mgr", "non-existent-image")
def test_list_flavors(self):
resp = mock.Mock(status_code=200)
self.ds_version.api.client.get.return_value = (
resp, {'flavor_ids': ['flavor-1', 'flavor-2']})
result = self.ds_version.list_flavors('ds-version-1')
self.assertEqual(['flavor-1', 'flavor-2'], result)
self.ds_version.api.client.get.assert_called_once_with(
'/mgmt/datastore-versions/ds-version-1/flavors')
def test_add_flavors(self):
self.ds_version.api.client.post = self._get_mock_method()
self._resp.status_code = 202
self.ds_version.add_flavors(
'ds-version-1', flavor_ids=['flavor-1', 'flavor-2'])
self.assertEqual(
'/mgmt/datastore-versions/ds-version-1/flavors',
self._url)
self.assertEqual({'flavor_ids': ['flavor-1', 'flavor-2']}, self._body)
def test_delete_flavor(self):
self.ds_version.api.client.delete = self._get_mock_method()
self._resp.status_code = 204
self.ds_version.delete_flavor('ds-version-1', 'flavor-1')
self.assertEqual(
'/mgmt/datastore-versions/ds-version-1/flavors/flavor-1',
self._url)
def test_list_volume_types(self):
resp = mock.Mock(status_code=200)
self.ds_version.api.client.get.return_value = (
+23
View File
@@ -347,6 +347,29 @@ class MgmtDatastoreVersions(base.ManagerWithFind):
resp, body = self.api.client.delete(url)
common.check_for_exceptions(resp, body, url)
def list_flavors(self, datastore_version_id):
"""List flavor associations for a datastore version."""
url = "/mgmt/datastore-versions/%s/flavors" % datastore_version_id
resp, body = self.api.client.get(url)
common.check_for_exceptions(resp, body, url)
return body['flavor_ids']
def add_flavors(self, datastore_version_id, flavor_ids):
"""Add flavor associations to a datastore version."""
body = {
'flavor_ids': flavor_ids
}
url = "/mgmt/datastore-versions/%s/flavors" % datastore_version_id
resp, body = self.api.client.post(url, body=body)
common.check_for_exceptions(resp, body, url)
def delete_flavor(self, datastore_version_id, flavor_id):
"""Delete a flavor association from a datastore version."""
url = "/mgmt/datastore-versions/%s/flavors/%s" % (
datastore_version_id, flavor_id)
resp, body = self.api.client.delete(url)
common.check_for_exceptions(resp, body, url)
def list_volume_types(self, datastore_version_id):
"""List volume type associations for a datastore version."""
url = "/mgmt/datastore-versions/%s/volume-types" % (