Add 'share-network' option for share replica create.

Partial-Bug: #1925486

Depends-On: I9049dcd418fbb16d663ab8ed27b90c765fafc5d3
Change-Id: Ie1ace4aca5d5384feb311421afa13bf51e24aebe
This commit is contained in:
kpdev 2021-08-14 19:32:14 +02:00 committed by Kiran Pawar
parent 3d052e9859
commit db1bba7cde
12 changed files with 288 additions and 15 deletions

View File

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

View File

@ -56,6 +56,13 @@ class CreateShareReplica(command.ShowOne):
"Supported key is only_host. Available for microversion "
">= 2.67."),
)
parser.add_argument(
'--share-network',
metavar='<share-network-name-or-id>',
default=None,
help=_('Optional network info ID or name. Available for '
'microversion >= 2.72')
)
return parser
def take_action(self, parsed_args):
@ -84,6 +91,17 @@ class CreateShareReplica(command.ShowOne):
if scheduler_hints:
body['scheduler_hints'] = scheduler_hints
share_network_id = None
if parsed_args.share_network:
if share_client.api_version < api_versions.APIVersion("2.72"):
raise exceptions.CommandError(
"'share-network' option is available only starting "
"with '2.72' API microversion.")
share_network_id = osc_utils.find_resource(
share_client.share_networks,
parsed_args.share_network).id
body['share_network'] = share_network_id
share_replica = share_client.share_replicas.create(**body)
if parsed_args.wait:
if not osc_utils.wait_for_status(

View File

@ -480,11 +480,15 @@ class BaseTestCase(base.ClientTestBase):
@classmethod
def create_share_replica(cls, share_id, client=None,
wait_for_creation=True, cleanup_in_class=False,
availability_zone=None, share_network=None,
microversion=None):
client = client or cls.get_user_client()
share_replica = client.create_share_replica(
share_id, microversion=microversion)
share_id,
availability_zone=availability_zone,
share_network=share_network,
microversion=microversion)
if wait_for_creation:
share_replica = client.wait_for_share_replica_status(
share_replica['id'])

View File

@ -1907,12 +1907,18 @@ class ManilaCLIClient(base.CLIClient):
# Share replicas
def create_share_replica(self, share, microversion=None):
def create_share_replica(self, share, availability_zone=None,
share_network=None, microversion=None):
"""Create a share replica.
:param share: str -- Name or ID of a share to create a replica of
"""
cmd = "share-replica-create %s" % share
if availability_zone is not None:
cmd += " --availability_zone " + availability_zone
if share_network is not None:
cmd += " --share_network " + share_network
replica = self.manila(cmd, microversion=microversion)
return output_parser.details(replica)

View File

@ -326,13 +326,16 @@ class OSCClientTestBase(base.ClientTestBase):
return share_network_obj
def create_share_replica(self, share, availability_zone=None,
wait=None, add_cleanup=True):
share_network=None, wait=None,
add_cleanup=True):
cmd = (f'replica create {share}')
if availability_zone:
cmd = cmd + f' --availability-zone {availability_zone}'
if wait:
cmd = cmd + ' --wait'
if share_network:
cmd = cmd + ' --share-network %s' % share_network
replica_object = self.dict_result('share', cmd)
self._wait_for_object_status(

View File

@ -0,0 +1,53 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from manilaclient import config
from manilaclient.tests.functional.osc import base
from tempest.lib.common.utils import data_utils
CONF = config.CONF
class ShareReplicasCLITest(base.OSCClientTestBase):
def _create_share_and_replica(self, add_cleanup=True):
replication_type = CONF.replication_type
share_type = self.create_share_type(
data_utils.rand_name('test_share_type'),
dhss=True,
extra_specs={'replication_type': replication_type})
share_network = self.create_share_network(name='test_share_network')
share = self.create_share(share_type=share_type['name'],
share_network=share_network['id'])
replica = self.create_share_replica(share['id'],
share_network=share_network['id'],
wait=True,
add_cleanup=add_cleanup)
return replica
def test_share_replica_create(self):
share_replica = self._create_share_and_replica()
share_replica_list = self.listing_result('share replica', 'list')
self.assertTableStruct(share_replica_list, [
'ID',
'Status',
'Share ID',
])
self.assertIn(share_replica['id'],
[item['ID'] for item in share_replica_list])
def test_share_replica_delete(self):
share_replica = self._create_share_and_replica(add_cleanup=False)
self.openstack(
f'share replica delete {share_replica["id"]}'
)
self.check_object_deleted('share replica', share_replica["id"])

View File

@ -0,0 +1,49 @@
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
@utils.skip_if_microversion_not_supported('2.72')
class ShareReplicasTest(base.BaseTestCase):
def _create_share_and_replica(self):
replication_type = CONF.replication_type
share_type = self.create_share_type(
driver_handles_share_servers=True,
extra_specs={'replication_type': replication_type})
share_network = self.create_share_network()
share = self.create_share(
share_type=share_type['ID'],
share_network=share_network['id'],
client=self.get_user_client())
share_replica = self.create_share_replica(
share['id'],
share_network=share_network['id'],
wait_for_creation=True,
client=self.get_user_client())
return share, share_replica
def test_share_replica_create(self):
share, share_replica = self._create_share_and_replica()
self.assertEqual(share['id'], share_replica['share_id'])
def test_share_replica_delete(self):
share, share_replica = self._create_share_and_replica()
self.user_client.delete_share_replica(share_replica['id'])
self.user_client.wait_for_share_replica_deletion(share_replica['id'])

View File

@ -16,7 +16,6 @@ from osc_lib import exceptions
from osc_lib import utils as oscutils
from manilaclient import api_versions
from manilaclient.api_versions import MAX_VERSION
from manilaclient.common import cliutils
from manilaclient.osc import utils
@ -37,7 +36,7 @@ class TestShareReplica(manila_fakes.TestShare):
self.replicas_mock = self.app.client_manager.share.share_replicas
self.replicas_mock.reset_mock()
self.app.client_manager.share.api_version = api_versions.APIVersion(
MAX_VERSION)
api_versions.MAX_VERSION)
self.replica_el_mock = (
self.app.client_manager
@ -179,6 +178,39 @@ class TestShareReplicaCreate(TestShareReplica):
self.cmd.take_action,
parsed_args)
def test_share_replica_create_share_network(self):
self.app.client_manager.share.api_version = api_versions.APIVersion(
"2.72")
arglist = [
self.share.id,
'--availability-zone', self.share.availability_zone,
'--share-network', self.share.share_network_id
]
verifylist = [
('share', self.share.id),
('availability_zone', self.share.availability_zone),
('share_network', self.share.share_network_id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
if self.share.share_network_id:
self.replicas_mock.create.assert_called_with(
share=self.share,
availability_zone=self.share.availability_zone,
share_network=self.share.share_network_id
)
else:
self.replicas_mock.create.assert_called_with(
share=self.share,
availability_zone=self.share.availability_zone,
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_share_replica_create_wait(self):
arglist = [
self.share.id,

View File

@ -18,6 +18,7 @@ from unittest import mock
import ddt
from manilaclient import api_versions
from manilaclient.common import constants
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import share_replicas
@ -37,17 +38,20 @@ class ShareReplicasTest(utils.TestCase):
self.manager = share_replicas.ShareReplicaManager(
fakes.FakeClient(api_version=microversion))
def test_create(self):
@ddt.data("2.11",
constants.REPLICA_PRE_GRADUATION_VERSION,
constants.REPLICA_GRADUATION_VERSION)
def test_create(self, microversion):
api_version = api_versions.APIVersion(microversion)
values = {
'availability_zone': 'az1',
'share': 's1',
}
self._create_common(values)
def _create_common(self, values):
with mock.patch.object(self.manager, '_create', fakes.fake_create):
result = self.manager.create(**values)
manager = share_replicas.ShareReplicaManager(
fakes.FakeClient(api_version=api_version))
with mock.patch.object(manager, '_create', fakes.fake_create):
result = manager.create(**values)
values['share_id'] = values.pop('share')
body_expected = {share_replicas.RESOURCE_NAME: values}
@ -55,6 +59,27 @@ class ShareReplicasTest(utils.TestCase):
self.assertEqual(share_replicas.RESOURCE_NAME, result['resp_key'])
self.assertEqual(body_expected, result['body'])
@ddt.data("2.72")
def test_create_with_share_network(self, microversion):
api_version = api_versions.APIVersion(microversion)
values = {
'availability_zone': 'az1',
'share': 's1',
'share_network': 'sn1',
}
manager = share_replicas.ShareReplicaManager(
fakes.FakeClient(api_version=api_version))
with mock.patch.object(manager, '_create', fakes.fake_create):
result = manager.create(**values)
values['share_id'] = values.pop('share')
values['share_network_id'] = values.pop('share_network')
body_expected = {share_replicas.RESOURCE_NAME: values}
self.assertEqual(share_replicas.RESOURCES_PATH, result['url'])
self.assertEqual(share_replicas.RESOURCE_NAME, result['resp_key'])
self.assertEqual(body_expected, result['body'])
def test_delete_str(self):
with mock.patch.object(self.manager, '_delete', mock.Mock()):
self.manager.delete(FAKE_REPLICA)

View File

@ -120,14 +120,23 @@ class ShareReplicaManager(base.ManagerWithFind):
return self._create_share_replica(
share, availability_zone=availability_zone)
@api_versions.wraps("2.67") # noqa
@api_versions.wraps("2.67", "2.71") # noqa
def create(self, share, availability_zone=None, scheduler_hints=None): # noqa F811
return self._create_share_replica(
share, availability_zone=availability_zone,
scheduler_hints=scheduler_hints)
@api_versions.wraps("2.72") # noqa
def create(self, share, # pylint: disable=function-redefined # noqa F811
availability_zone=None, scheduler_hints=None,
share_network=None):
return self._create_share_replica(
share, availability_zone=availability_zone,
scheduler_hints=scheduler_hints,
share_network=share_network)
def _create_share_replica(self, share, availability_zone=None,
scheduler_hints=None):
scheduler_hints=None, share_network=None):
"""Create a replica for a share.
:param share: The share to create the replica of. Can be the share
@ -135,6 +144,7 @@ class ShareReplicaManager(base.ManagerWithFind):
:param availability_zone: The 'availability_zone' object or its UUID.
:param scheduler_hints: The scheduler_hints as key=value pair. Only
supported key is 'only_host'.
:param share_network: either share network object or its UUID.
"""
share_id = base.getid(share)
@ -145,6 +155,10 @@ class ShareReplicaManager(base.ManagerWithFind):
if scheduler_hints:
body['scheduler_hints'] = scheduler_hints
if share_network:
body['share_network_id'] = base.getid(share_network)
return self._create(RESOURCES_PATH,
{RESOURCE_NAME: body},
RESOURCE_NAME)

View File

@ -6162,7 +6162,7 @@ def do_share_replica_create(cs, args):
_print_share_replica(cs, replica)
@api_versions.wraps("2.67")
@api_versions.wraps("2.67", "2.71")
@cliutils.arg(
'share',
metavar='<share>',
@ -6208,6 +6208,67 @@ def do_share_replica_create(cs, args): # noqa
_print_share_replica(cs, replica)
@api_versions.wraps("2.72")
@cliutils.arg(
'share',
metavar='<share>',
help='Name or ID of the share to replicate.')
@cliutils.arg(
'--availability-zone',
'--availability_zone',
'--az',
default=None,
action='single_alias',
metavar='<availability-zone>',
help='Optional Availability zone in which replica should be created.')
@cliutils.arg(
'--scheduler-hints',
'--scheduler_hints',
'--sh',
metavar='<key=value>',
nargs='*',
help='Scheduler hints for the share replica as key=value pairs, '
'Supported key is only_host. Available for microversion >= 2.67. ',
default=None)
@cliutils.arg(
'--share-network',
'--share_network',
metavar='<network-info>',
default=None,
action='single_alias',
help='Optional network info ID or name. '
'Available only for microversion >= 2.72')
def do_share_replica_create(cs, args): # noqa
"""Create a share replica."""
share = _find_share(cs, args.share)
scheduler_hints = {}
if args.scheduler_hints:
hints = _extract_key_value_options(args, 'scheduler_hints')
if 'only_host' not in hints.keys() or len(hints) > 1:
raise exceptions.CommandError(
"The only valid key supported with the --scheduler-hints "
"argument is 'only_host'.")
scheduler_hints['only_host'] = hints.get('only_host')
share_network = None
if args.share_network:
share_network = _find_share_network(cs, args.share_network)
body = {
'share': share,
'availability_zone': args.availability_zone,
}
if scheduler_hints:
body['scheduler_hints'] = scheduler_hints
if share_network:
body['share_network'] = share_network
replica = cs.share_replicas.create(**body)
_print_share_replica(cs, replica)
@cliutils.arg(
'replica',
metavar='<replica>',

View File

@ -0,0 +1,8 @@
---
fixes:
- |
`Bug #1925486 <https://bugs.launchpad.net/manila/+bug/1925486>`_
Share replica create command does not support share network option and
manila internally uses parent share's share network. Fixed it to allow any
share network by providing option ``share-network`` starting with microversion
'2.72'.