Human readable export location CLI changes

OpenStack CLI option is added to support user friently export location.

example command:
$ openstack share create nfs 1 --name share_name --mount-point-name custom_export_path

partially-implements: bp human-readable-export-locations
Depends-On: I72ac7e24ddd4330d76cafd5e7f78bac2b0174883
Change-Id: If896065a74fc4ebabd09b677e5eb0329cecfaab9
This commit is contained in:
jayaanand.borra@netapp.com 2024-02-13 20:41:05 +05:30 committed by jayaanand borra
parent 5000f1cb1f
commit b097cec84f
7 changed files with 123 additions and 8 deletions

View File

@ -27,7 +27,7 @@ from manilaclient import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
MAX_VERSION = '2.83' MAX_VERSION = '2.84'
MIN_VERSION = '2.0' MIN_VERSION = '2.0'
DEPRECATED_VERSION = '1.0' DEPRECATED_VERSION = '1.0'
_VERSIONED_METHOD_MAP = {} _VERSIONED_METHOD_MAP = {}

View File

@ -188,6 +188,13 @@ class CreateShare(command.ShowOne):
"possible keys are same_host, different_host." "possible keys are same_host, different_host."
"(repeat option to set multiple hints)"), "(repeat option to set multiple hints)"),
) )
parser.add_argument(
'--mount-point-name',
metavar="<mount_point_name>",
default=None,
help=_('Optional custom export location. Available for '
'microversion >= 2.84')
)
return parser return parser
@ -232,6 +239,15 @@ class CreateShare(command.ShowOne):
snapshot_id = snapshot.id snapshot_id = snapshot.id
size = max(size or 0, snapshot.size) size = max(size or 0, snapshot.size)
mount_point_name = None
if parsed_args.mount_point_name:
if share_client.api_version < api_versions.APIVersion('2.84'):
raise exceptions.CommandError(
'Setting share mount point name is '
'available only for API microversion >= 2.84')
else:
mount_point_name = parsed_args.mount_point_name
scheduler_hints = {} scheduler_hints = {}
if parsed_args.scheduler_hint: if parsed_args.scheduler_hint:
if share_client.api_version < api_versions.APIVersion('2.65'): if share_client.api_version < api_versions.APIVersion('2.65'):
@ -271,7 +287,8 @@ class CreateShare(command.ShowOne):
'is_public': parsed_args.public, 'is_public': parsed_args.public,
'availability_zone': parsed_args.availability_zone, 'availability_zone': parsed_args.availability_zone,
'share_group_id': share_group, 'share_group_id': share_group,
'scheduler_hints': scheduler_hints 'scheduler_hints': scheduler_hints,
'mount_point_name': mount_point_name,
} }
share = share_client.shares.create(**body) share = share_client.shares.create(**body)

View File

@ -139,6 +139,7 @@ class FakeShare(object):
"revert_to_snapshot_support": False, "revert_to_snapshot_support": False,
"source_share_group_snapshot_member_id": None, "source_share_group_snapshot_member_id": None,
"scheduler_hints": {}, "scheduler_hints": {},
"mount_point_name": None,
} }
# Overwrite default attributes. # Overwrite default attributes.

View File

@ -132,7 +132,8 @@ class TestShareCreate(TestShare):
share_type=self.share_type.id, share_type=self.share_type.id,
size=self.new_share.size, size=self.new_share.size,
snapshot_id=None, snapshot_id=None,
scheduler_hints={} scheduler_hints={},
mount_point_name=None,
) )
self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.columns, columns)
@ -180,7 +181,8 @@ class TestShareCreate(TestShare):
share_type=self.share_type.id, share_type=self.share_type.id,
size=self.new_share.size, size=self.new_share.size,
snapshot_id=None, snapshot_id=None,
scheduler_hints={} scheduler_hints={},
mount_point_name=None,
) )
self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.columns, columns)
@ -227,6 +229,49 @@ class TestShareCreate(TestShare):
snapshot_id=None, snapshot_id=None,
scheduler_hints={'same_host': shares[0].id, scheduler_hints={'same_host': shares[0].id,
'different_host': shares[1].id}, 'different_host': shares[1].id},
mount_point_name=None,
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.datalist, data)
def test_share_create_mount_point_name(self):
"""Verifies that the mount point name has been passed correctly."""
self.app.client_manager.share.api_version = api_versions.APIVersion(
"2.84")
mount_point_name = "fake_mp"
arglist = [
self.new_share.share_proto,
str(self.new_share.size),
'--share-type', self.share_type.id,
'--mount-point-name', mount_point_name,
]
verifylist = [
('share_proto', self.new_share.share_proto),
('size', self.new_share.size),
('mount_point_name', mount_point_name),
('share_type', self.share_type.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.shares_mock.create.assert_called_with(
availability_zone=None,
description=None,
is_public=False,
metadata={},
name=None,
share_group_id=None,
share_network=None,
share_proto=self.new_share.share_proto,
share_type=self.share_type.id,
size=self.new_share.size,
snapshot_id=None,
scheduler_hints={},
mount_point_name='fake_mp',
) )
self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.columns, columns)
@ -269,7 +314,8 @@ class TestShareCreate(TestShare):
share_type=None, share_type=None,
size=self.new_share.size, size=self.new_share.size,
snapshot_id=self.share_snapshot.id, snapshot_id=self.share_snapshot.id,
scheduler_hints={} scheduler_hints={},
mount_point_name=None,
) )
self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.datalist, data) self.assertCountEqual(self.datalist, data)
@ -305,7 +351,8 @@ class TestShareCreate(TestShare):
share_type=self.share_type.id, share_type=self.share_type.id,
size=self.new_share.size, size=self.new_share.size,
snapshot_id=None, snapshot_id=None,
scheduler_hints={} scheduler_hints={},
mount_point_name=None,
) )
self.shares_mock.get.assert_called_with(self.new_share.id) self.shares_mock.get.assert_called_with(self.new_share.id)
@ -345,7 +392,8 @@ class TestShareCreate(TestShare):
share_type=self.share_type.id, share_type=self.share_type.id,
size=self.new_share.size, size=self.new_share.size,
snapshot_id=None, snapshot_id=None,
scheduler_hints={} scheduler_hints={},
mount_point_name=None,
) )
mock_logger.error.assert_called_with( mock_logger.error.assert_called_with(

View File

@ -140,6 +140,29 @@ class SharesTest(utils.TestCase):
availability_zone=availability_zone) availability_zone=availability_zone)
cs.assert_called('POST', '/shares', body) cs.assert_called('POST', '/shares', body)
@ddt.data({'mount_point_name': 'fake_mount_pt1'},
{'mount_point_name': 'fake_mount_pt2'})
@ddt.unpack
def test_create_share_with_mount_point_name(self, mount_point_name):
body = {
'share': {
'is_public': False,
'share_type': None,
'name': None,
'snapshot_id': None,
'description': None,
'metadata': {},
'share_proto': 'nfs',
'share_network_id': None,
'size': 1,
'availability_zone': None,
'scheduler_hints': {},
'mount_point_name': mount_point_name,
}
}
cs.shares.create('nfs', 1, mount_point_name=mount_point_name)
cs.assert_called('POST', '/shares', body)
@ddt.data( @ddt.data(
type('ShareUUID', (object, ), {'uuid': '1234'}), type('ShareUUID', (object, ), {'uuid': '1234'}),
type('ShareID', (object, ), {'id': '1234'}), type('ShareID', (object, ), {'id': '1234'}),

View File

@ -124,7 +124,8 @@ class ShareManager(base.MetadataCapableManager):
def create(self, share_proto, size, snapshot_id=None, name=None, def create(self, share_proto, size, snapshot_id=None, name=None,
description=None, metadata=None, share_network=None, description=None, metadata=None, share_network=None,
share_type=None, is_public=False, availability_zone=None, share_type=None, is_public=False, availability_zone=None,
share_group_id=None, scheduler_hints=None, return_raw=False): share_group_id=None, scheduler_hints=None, return_raw=False,
mount_point_name=None):
"""Create a share. """Create a share.
:param share_proto: text - share protocol for new share available :param share_proto: text - share protocol for new share available
@ -142,6 +143,9 @@ class ShareManager(base.MetadataCapableManager):
:param scheduler_hints: dict - hints for the scheduler to place share :param scheduler_hints: dict - hints for the scheduler to place share
on most appropriate host e.g. keys are same_host for affinity and on most appropriate host e.g. keys are same_host for affinity and
different_host for anti-affinity different_host for anti-affinity
:param mount_point_name: text - share human-readable mount point name.
This name will be reflected in export location once
share is created.
:rtype: :class:`Share` :rtype: :class:`Share`
""" """
share_metadata = metadata if metadata is not None else dict() share_metadata = metadata if metadata is not None else dict()
@ -163,6 +167,9 @@ class ShareManager(base.MetadataCapableManager):
if share_group_id: if share_group_id:
body['share_group_id'] = share_group_id body['share_group_id'] = share_group_id
if mount_point_name:
body['mount_point_name'] = mount_point_name
return self._create('/shares', {'share': body}, 'share', return self._create('/shares', {'share': body}, 'share',
return_raw=return_raw) return_raw=return_raw)

View File

@ -0,0 +1,19 @@
---
features:
- In the 2.84 API version, a new option, ``mount_point_name``, has been
introduced to the share creation command. This option allow users
to specify a more intuitive ``mount_point_name`` that will be reflected
in the share's export location. However, for this feature to be available
to users, administrators must first enable an extra-spec in the
share type. In addition, administrators need to set an extra-spec named
``provisioning:mount_point_prefix``. The Manila service will combine
this prefix with the mount point name that user provides during share
creation. If the ``provisioning:mount_point_prefix`` is not set for
a share type, but ``mount_point_name_support`` is enabled, the share's
export location will default to using the project_id as a prefix.
Please note that shares created with a project_id prefix cannot be
transferred. To move these shares to a different project,
an admin must manually unmount them from the current project and mount
them to the target project. A new capability, ``mount_point_name_support``,
allows the driver to inform the scheduler about its support for the
mount_point_name feature.