Add --scheduler_hints to share create command
This commit add --scheduler_hints option to share create command. Users can specify affinity/anti-affinity share ids to share create command as value in <key=value> pairs of scheduler hints. The possible keys are same_host and different_host. Available from microversion 2.65. Partially-implements: bp affinity-antiaffinity-filter Change-Id: I8a9598eb16f08ed6539e8996e28cfc6e19586483
This commit is contained in:
parent
cc440609bd
commit
9a2bcb8d87
@ -27,7 +27,7 @@ from manilaclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_VERSION = '2.63'
|
||||
MAX_VERSION = '2.65'
|
||||
MIN_VERSION = '2.0'
|
||||
DEPRECATED_VERSION = '1.0'
|
||||
_VERSIONED_METHOD_MAP = {}
|
||||
|
@ -179,6 +179,16 @@ class CreateShare(command.ShowOne):
|
||||
default=False,
|
||||
help=_('Wait for share creation')
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scheduler-hint",
|
||||
metavar="<key=value>",
|
||||
default={},
|
||||
action=parseractions.KeyValueAction,
|
||||
help=_("Set Scheduler hints for the share as key=value pairs, "
|
||||
"possible keys are same_host, different_host."
|
||||
"(repeat option to set multiple hints)"),
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -210,6 +220,33 @@ class CreateShare(command.ShowOne):
|
||||
snapshot_id = snapshot.id
|
||||
size = max(size or 0, snapshot.size)
|
||||
|
||||
scheduler_hints = {}
|
||||
if parsed_args.scheduler_hint:
|
||||
if share_client.api_version < api_versions.APIVersion('2.65'):
|
||||
raise exceptions.CommandError(
|
||||
'Setting share scheduler hints for a share is '
|
||||
'available only for API microversion >= 2.65')
|
||||
else:
|
||||
scheduler_hints = utils.extract_key_value_options(
|
||||
parsed_args.scheduler_hint)
|
||||
same_host_hint_shares = scheduler_hints.get('same_host')
|
||||
different_host_hint_shares = scheduler_hints.get(
|
||||
'different_host')
|
||||
if same_host_hint_shares:
|
||||
same_host_hint_shares = [
|
||||
apiutils.find_resource(share_client.shares, sh).id
|
||||
for sh in same_host_hint_shares.split(',')
|
||||
]
|
||||
scheduler_hints['same_host'] = (
|
||||
','.join(same_host_hint_shares))
|
||||
if different_host_hint_shares:
|
||||
different_host_hint_shares = [
|
||||
apiutils.find_resource(share_client.shares, sh).id
|
||||
for sh in different_host_hint_shares.split(',')
|
||||
]
|
||||
scheduler_hints['different_host'] = (
|
||||
','.join(different_host_hint_shares))
|
||||
|
||||
body = {
|
||||
'share_proto': parsed_args.share_proto,
|
||||
'size': size,
|
||||
@ -221,7 +258,8 @@ class CreateShare(command.ShowOne):
|
||||
'share_type': share_type,
|
||||
'is_public': parsed_args.public,
|
||||
'availability_zone': parsed_args.availability_zone,
|
||||
'share_group_id': share_group
|
||||
'share_group_id': share_group,
|
||||
'scheduler_hints': scheduler_hints
|
||||
}
|
||||
|
||||
share = share_client.shares.create(**body)
|
||||
|
@ -126,6 +126,7 @@ class FakeShare(object):
|
||||
"mount_snapshot_support": False,
|
||||
"revert_to_snapshot_support": False,
|
||||
"source_share_group_snapshot_member_id": None,
|
||||
"scheduler_hints": {},
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
|
@ -116,7 +116,8 @@ class TestShareCreate(TestShare):
|
||||
share_proto=self.new_share.share_proto,
|
||||
share_type=None,
|
||||
size=self.new_share.size,
|
||||
snapshot_id=None
|
||||
snapshot_id=None,
|
||||
scheduler_hints={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
@ -161,7 +162,52 @@ class TestShareCreate(TestShare):
|
||||
share_proto=self.new_share.share_proto,
|
||||
share_type=None,
|
||||
size=self.new_share.size,
|
||||
snapshot_id=None
|
||||
snapshot_id=None,
|
||||
scheduler_hints={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
self.assertCountEqual(self.datalist, data)
|
||||
|
||||
def test_share_create_scheduler_hints(self):
|
||||
"""Verifies scheduler hints are parsed correctly."""
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
"2.65")
|
||||
|
||||
shares = self.setup_shares_mock(count=2)
|
||||
share1_name = shares[0].name
|
||||
share2_name = shares[1].name
|
||||
|
||||
arglist = [
|
||||
self.new_share.share_proto,
|
||||
str(self.new_share.size),
|
||||
'--scheduler-hint', ('same_host=%s' % share1_name),
|
||||
'--scheduler-hint', ('different_host=%s' % share2_name),
|
||||
]
|
||||
verifylist = [
|
||||
('share_proto', self.new_share.share_proto),
|
||||
('size', self.new_share.size),
|
||||
('scheduler_hint',
|
||||
{'same_host': share1_name, 'different_host': share2_name}),
|
||||
]
|
||||
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=None,
|
||||
size=self.new_share.size,
|
||||
snapshot_id=None,
|
||||
scheduler_hints={'same_host': shares[0].id,
|
||||
'different_host': shares[1].id},
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
@ -197,7 +243,8 @@ class TestShareCreate(TestShare):
|
||||
share_proto=self.new_share.share_proto,
|
||||
share_type=None,
|
||||
size=self.new_share.size,
|
||||
snapshot_id=self.share_snapshot.id
|
||||
snapshot_id=self.share_snapshot.id,
|
||||
scheduler_hints={}
|
||||
)
|
||||
|
||||
self.assertCountEqual(self.columns, columns)
|
||||
@ -231,7 +278,8 @@ class TestShareCreate(TestShare):
|
||||
share_proto=self.new_share.share_proto,
|
||||
share_type=None,
|
||||
size=self.new_share.size,
|
||||
snapshot_id=None
|
||||
snapshot_id=None,
|
||||
scheduler_hints={}
|
||||
)
|
||||
|
||||
self.shares_mock.get.assert_called_with(self.new_share.id)
|
||||
@ -268,7 +316,8 @@ class TestShareCreate(TestShare):
|
||||
share_proto=self.new_share.share_proto,
|
||||
share_type=None,
|
||||
size=self.new_share.size,
|
||||
snapshot_id=None
|
||||
snapshot_id=None,
|
||||
scheduler_hints={}
|
||||
)
|
||||
|
||||
mock_logger.error.assert_called_with(
|
||||
|
@ -67,6 +67,7 @@ class SharesTest(utils.TestCase):
|
||||
'share_type': None,
|
||||
'is_public': False,
|
||||
'availability_zone': None,
|
||||
'scheduler_hints': dict(),
|
||||
}
|
||||
cs.shares.create(protocol, 1)
|
||||
cs.assert_called('POST', '/shares', {'share': expected})
|
||||
@ -87,6 +88,7 @@ class SharesTest(utils.TestCase):
|
||||
'share_type': None,
|
||||
'is_public': False,
|
||||
'availability_zone': None,
|
||||
'scheduler_hints': dict(),
|
||||
}
|
||||
cs.shares.create('nfs', 1, share_network=share_network)
|
||||
cs.assert_called('POST', '/shares', {'share': expected})
|
||||
@ -107,6 +109,7 @@ class SharesTest(utils.TestCase):
|
||||
'share_type': 'fake_st',
|
||||
'is_public': False,
|
||||
'availability_zone': None,
|
||||
'scheduler_hints': dict(),
|
||||
}
|
||||
cs.shares.create('nfs', 1, share_type=share_type)
|
||||
cs.assert_called('POST', '/shares', {'share': expected})
|
||||
@ -130,6 +133,7 @@ class SharesTest(utils.TestCase):
|
||||
'share_network_id': None,
|
||||
'size': 1,
|
||||
'availability_zone': availability_zone,
|
||||
'scheduler_hints': {},
|
||||
}
|
||||
}
|
||||
cs.shares.create('nfs', 1, is_public=is_public,
|
||||
|
@ -86,6 +86,7 @@ class ShellTest(test_utils.TestCase):
|
||||
"size": 1,
|
||||
"is_public": False,
|
||||
"availability_zone": None,
|
||||
"scheduler_hints": {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ class ShareManager(base.ManagerWithFind):
|
||||
def create(self, share_proto, size, snapshot_id=None, name=None,
|
||||
description=None, metadata=None, share_network=None,
|
||||
share_type=None, is_public=False, availability_zone=None,
|
||||
share_group_id=None):
|
||||
share_group_id=None, scheduler_hints=None):
|
||||
"""Create a share.
|
||||
|
||||
:param share_proto: text - share protocol for new share available
|
||||
@ -135,9 +135,14 @@ class ShareManager(base.ManagerWithFind):
|
||||
:param is_public: bool, whether to set share as public or not.
|
||||
:param share_group_id: text - ID of the share group to which the share
|
||||
should belong
|
||||
:param scheduler_hints: dict - hints for the scheduler to place share
|
||||
on most appropriate host e.g. keys are same_host for affinity and
|
||||
different_host for anti-affinity
|
||||
:rtype: :class:`Share`
|
||||
"""
|
||||
share_metadata = metadata if metadata is not None else dict()
|
||||
scheduler_hints = (scheduler_hints if scheduler_hints is not None
|
||||
else dict())
|
||||
body = {
|
||||
'size': size,
|
||||
'snapshot_id': snapshot_id,
|
||||
@ -149,6 +154,7 @@ class ShareManager(base.ManagerWithFind):
|
||||
'share_type': common_base.getid(share_type),
|
||||
'is_public': is_public,
|
||||
'availability_zone': availability_zone,
|
||||
'scheduler_hints': scheduler_hints,
|
||||
}
|
||||
if share_group_id:
|
||||
body['share_group_id'] = share_group_id
|
||||
|
@ -904,6 +904,14 @@ def do_rate_limits(cs, args):
|
||||
'--wait',
|
||||
action='store_true',
|
||||
help='Wait for share creation')
|
||||
@cliutils.arg(
|
||||
'--scheduler-hints', '--scheduler_hints', '--sh',
|
||||
metavar='<key=value>',
|
||||
nargs='*',
|
||||
help='Scheduler hints for the share as key=value pairs, '
|
||||
'possible keys are same_host, different_host, '
|
||||
'value must be share_name or share_id.',
|
||||
default=None)
|
||||
@cliutils.service_type('sharev2')
|
||||
def do_create(cs, args):
|
||||
"""Creates a new share (NFS, CIFS, CephFS, GlusterFS, HDFS or MAPRFS)."""
|
||||
@ -928,6 +936,25 @@ def do_create(cs, args):
|
||||
raise exceptions.CommandError(
|
||||
"Share name cannot be with the value 'None'")
|
||||
|
||||
scheduler_hints = {}
|
||||
if args.scheduler_hints:
|
||||
scheduler_hints = _extract_key_value_options(args, 'scheduler_hints')
|
||||
same_host_hint_shares = scheduler_hints.get('same_host')
|
||||
different_host_hint_shares = scheduler_hints.get('different_host')
|
||||
if same_host_hint_shares:
|
||||
same_host_hint_shares = [
|
||||
_find_share(cs, sh).id
|
||||
for sh in same_host_hint_shares.split(',')
|
||||
]
|
||||
scheduler_hints['same_host'] = ','.join(same_host_hint_shares)
|
||||
if different_host_hint_shares:
|
||||
different_host_hint_shares = [
|
||||
_find_share(cs, sh).id
|
||||
for sh in different_host_hint_shares.split(',')
|
||||
]
|
||||
scheduler_hints['different_host'] = ','.join(
|
||||
different_host_hint_shares)
|
||||
|
||||
share = cs.shares.create(args.share_protocol, args.size, snapshot,
|
||||
args.name, args.description,
|
||||
metadata=share_metadata,
|
||||
@ -935,7 +962,8 @@ def do_create(cs, args):
|
||||
share_type=args.share_type,
|
||||
is_public=args.public,
|
||||
availability_zone=args.availability_zone,
|
||||
share_group_id=share_group)
|
||||
share_group_id=share_group,
|
||||
scheduler_hints=scheduler_hints)
|
||||
|
||||
if args.wait:
|
||||
share = _wait_for_share_status(cs, share)
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- Added --scheduler_hints to the share create command
|
||||
upgrade:
|
||||
- Scheduler hints in the share create command allow scheduler to select
|
||||
appropriate host using hard affinity and anti-affinity filters. User needs
|
||||
to specify affinity/anti-affinity share ids using keys "same_host" or
|
||||
"different_host" when creating a manila share.
|
Loading…
Reference in New Issue
Block a user