Support multiple subnets per AZ
Add new command ``share-network-subnet-create-check`` to check if a new subnet configuration can be allocated to a share-network. Bump CLI version to support the multiple subnets feature and fix the list operation. The equivalent OSC command is also added. Run the subnet create command using the new option --check-only. Depends-On: I7de9de4ae509182e9494bba604979cce03acceec Partially-Implements: blueprint multiple-share-network-subnets Signed-off-by: Andre Beltrami <debeltrami@gmail.com> Co-Authored-by: Felipe Rodrigues <felipefuty01@gmail.com> Change-Id: I7702f458f6bca5e3947479a13a843f30873597a7
This commit is contained in:
parent
27bf8ec4e7
commit
586c6030ed
|
@ -17,6 +17,7 @@ from osc_lib.command import command
|
|||
from osc_lib import exceptions
|
||||
from osc_lib import utils as oscutils
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient.common._i18n import _
|
||||
|
||||
|
||||
|
@ -59,11 +60,38 @@ class CreateShareNetworkSubnet(command.ShowOne):
|
|||
"considered as being available across all availability "
|
||||
"zones.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--check-only',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=_("Run a dry-run of a share network subnet create. "
|
||||
"Available only for microversion >= 2.70.")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--restart-check',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=_("Restart a dry-run of a share network subnet create. "
|
||||
"Helpful when check results are stale. "
|
||||
"Available only for microversion >= 2.70.")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
share_client = self.app.client_manager.share
|
||||
|
||||
# check and restart check during create is only available from 2.70.
|
||||
if (parsed_args.check_only and
|
||||
share_client.api_version < api_versions.APIVersion("2.70")):
|
||||
raise exceptions.CommandError(
|
||||
"Check only can be specified only with manila API "
|
||||
"version >= 2.70.")
|
||||
if (parsed_args.restart_check and
|
||||
share_client.api_version < api_versions.APIVersion("2.70")):
|
||||
raise exceptions.CommandError(
|
||||
"Restart check can be specified only with manila API "
|
||||
"version >= 2.70.")
|
||||
|
||||
if xor(bool(parsed_args.neutron_net_id),
|
||||
bool(parsed_args.neutron_subnet_id)):
|
||||
raise exceptions.CommandError(
|
||||
|
@ -75,13 +103,24 @@ class CreateShareNetworkSubnet(command.ShowOne):
|
|||
share_client.share_networks,
|
||||
parsed_args.share_network).id
|
||||
|
||||
share_network_subnet = share_client.share_network_subnets.create(
|
||||
neutron_net_id=parsed_args.neutron_net_id,
|
||||
neutron_subnet_id=parsed_args.neutron_subnet_id,
|
||||
availability_zone=parsed_args.availability_zone,
|
||||
share_network_id=share_network_id
|
||||
)
|
||||
subnet_data = share_network_subnet._info
|
||||
if parsed_args.check_only or parsed_args.restart_check:
|
||||
subnet_create_check = (
|
||||
share_client.share_networks.share_network_subnet_create_check(
|
||||
neutron_net_id=parsed_args.neutron_net_id,
|
||||
neutron_subnet_id=parsed_args.neutron_subnet_id,
|
||||
availability_zone=parsed_args.availability_zone,
|
||||
reset_operation=parsed_args.restart_check,
|
||||
share_network_id=share_network_id)
|
||||
)
|
||||
subnet_data = subnet_create_check[1]
|
||||
else:
|
||||
share_network_subnet = share_client.share_network_subnets.create(
|
||||
neutron_net_id=parsed_args.neutron_net_id,
|
||||
neutron_subnet_id=parsed_args.neutron_subnet_id,
|
||||
availability_zone=parsed_args.availability_zone,
|
||||
share_network_id=share_network_id
|
||||
)
|
||||
subnet_data = share_network_subnet._info
|
||||
|
||||
return self.dict2columns(subnet_data)
|
||||
|
||||
|
|
|
@ -669,6 +669,26 @@ class ManilaCLIClient(base.CLIClient):
|
|||
SHARE_NETWORK, res_id=share_network, interval=2, timeout=6,
|
||||
microversion=microversion)
|
||||
|
||||
def share_network_subnet_create_check(
|
||||
self, share_network_id, neutron_net_id=None,
|
||||
neutron_subnet_id=None, availability_zone=None, reset=False,
|
||||
microversion=None):
|
||||
params = self._combine_share_network_subnet_data(
|
||||
neutron_net_id=neutron_net_id,
|
||||
neutron_subnet_id=neutron_subnet_id,
|
||||
availability_zone=availability_zone)
|
||||
cmd = (
|
||||
'share-network-subnet-create-check %(network_id)s '
|
||||
'%(params)s' % {
|
||||
'network_id': share_network_id,
|
||||
'params': params})
|
||||
|
||||
if reset:
|
||||
cmd += '--reset %s' % reset
|
||||
|
||||
return output_parser.details(
|
||||
self.manila(cmd, microversion=microversion))
|
||||
|
||||
# Share Network Subnets
|
||||
|
||||
def _combine_share_network_subnet_data(self, neutron_net_id=None,
|
||||
|
|
|
@ -390,3 +390,21 @@ class OSCClientTestBase(base.ClientTestBase):
|
|||
f'{access_id} ')
|
||||
|
||||
self.dict_result('share', cmd)
|
||||
|
||||
def check_create_network_subnet(self, share_network, neutron_net_id=None,
|
||||
neutron_subnet_id=None,
|
||||
availability_zone=None,
|
||||
restart_check=None):
|
||||
cmd = f'network subnet create {share_network} --check-only'
|
||||
|
||||
if neutron_net_id:
|
||||
cmd += f' --neutron-net-id {neutron_net_id}'
|
||||
if neutron_subnet_id:
|
||||
cmd += f' --neutron-subnet-id {neutron_subnet_id}'
|
||||
if availability_zone:
|
||||
cmd += f' --availability-zone {availability_zone}'
|
||||
if restart_check:
|
||||
cmd += f' --restart-check'
|
||||
|
||||
check_result = self.dict_result('share', cmd)
|
||||
return check_result
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright 2022 NetApp, Inc.
|
||||
# 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.tests.functional.osc import base
|
||||
|
||||
|
||||
class ShareNetworkSubnetsCLITest(base.OSCClientTestBase):
|
||||
|
||||
def test_openstack_share_network_create_check(self):
|
||||
share_network = self.create_share_network()
|
||||
|
||||
check_result = self.check_create_network_subnet(
|
||||
share_network['id'])
|
||||
|
||||
self.assertEqual('True', check_result['compatible'])
|
||||
self.assertEqual('{}', check_result['hosts_check_result'])
|
||||
|
||||
def test_openstack_share_network_create_check_restart(self):
|
||||
share_network = self.create_share_network()
|
||||
|
||||
check_result = self.check_create_network_subnet(
|
||||
share_network['id'], restart_check=True)
|
||||
|
||||
self.assertEqual('True', check_result['compatible'])
|
||||
self.assertEqual('{}', check_result['hosts_check_result'])
|
|
@ -397,6 +397,39 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
|
|||
self.assertEqual(len(network_services), 1)
|
||||
self.assertEqual(network_services[0]['name'], new_name)
|
||||
|
||||
def test_share_network_subnet_create_check(self):
|
||||
share_network = self.create_share_network(
|
||||
client=self.user_client,
|
||||
description='fakedescription',
|
||||
)
|
||||
|
||||
check_result = (
|
||||
self.user_client.share_network_subnet_create_check(
|
||||
share_network['id'], neutron_net_id='fake_neutron_net_id',
|
||||
neutron_subnet_id='fake_neutron_subnet_id'))
|
||||
|
||||
self.assertEqual(check_result['compatible'], 'True')
|
||||
|
||||
@ddt.data(
|
||||
{'neutron_net_id': None, 'neutron_subnet_id': 'fake_subnet_id'},
|
||||
{'neutron_net_id': 'fake_net_id', 'neutron_subnet_id': None},
|
||||
{'availability_zone': 'invalid_availability_zone'},
|
||||
)
|
||||
def test_check_add_share_network_subnet_with_invalid_params(self, params):
|
||||
self.assertRaises(
|
||||
tempest_lib_exc.CommandFailed,
|
||||
self.user_client.share_network_subnet_create_check,
|
||||
self.sn['id'],
|
||||
**params)
|
||||
|
||||
def test_check_add_share_network_subnet_to_invalid_share_network(self):
|
||||
self.assertRaises(
|
||||
tempest_lib_exc.CommandFailed,
|
||||
self.user_client.share_network_subnet_create_check,
|
||||
'invalid_share_network',
|
||||
self.neutron_net_id,
|
||||
self.neutron_subnet_id)
|
||||
|
||||
|
||||
class ShareNetworkSecurityServiceCheckReadWriteTests(base.BaseTestCase):
|
||||
protocol = None
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
||||
import ddt
|
||||
from osc_lib import exceptions
|
||||
|
||||
from manilaclient import api_versions
|
||||
from manilaclient.osc.v2 import share_network_subnets as osc_share_subnets
|
||||
from manilaclient.tests.unit.osc import osc_utils
|
||||
from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes
|
||||
|
@ -32,6 +35,7 @@ class TestShareNetworkSubnet(manila_fakes.TestShare):
|
|||
self.share_subnets_mock.reset_mock()
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestShareNetworkSubnetCreate(TestShareNetworkSubnet):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -105,6 +109,60 @@ class TestShareNetworkSubnetCreate(TestShareNetworkSubnet):
|
|||
self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
@ddt.data({'check_only': False, 'restart_check': True},
|
||||
{'check_only': True, 'restart_check': True},
|
||||
{'check_only': True, 'restart_check': False})
|
||||
@ddt.unpack
|
||||
def test_share_network_subnet_create_check_api_version_exception(
|
||||
self, check_only, restart_check):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
'2.69'
|
||||
)
|
||||
arglist = [
|
||||
self.share_network.id,
|
||||
]
|
||||
verifylist = [
|
||||
('share_network', self.share_network.id),
|
||||
]
|
||||
if check_only:
|
||||
arglist.append('--check-only')
|
||||
verifylist.append(('check_only', True))
|
||||
if restart_check:
|
||||
arglist.append('--restart-check')
|
||||
verifylist.append(('restart_check', True))
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandError, self.cmd.take_action, parsed_args)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_share_network_subnet_create_check(self, restart_check):
|
||||
self.app.client_manager.share.api_version = api_versions.APIVersion(
|
||||
'2.70'
|
||||
)
|
||||
self.share_networks_mock.share_network_subnet_create_check = (
|
||||
mock.Mock(return_value=(200, {'compatible': True})))
|
||||
arglist = [
|
||||
self.share_network.id,
|
||||
'--check-only'
|
||||
]
|
||||
verifylist = [
|
||||
('share_network', self.share_network.id),
|
||||
('check_only', True),
|
||||
]
|
||||
if restart_check:
|
||||
arglist.append('--restart-check')
|
||||
verifylist.append(('restart_check', True))
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
(self.share_networks_mock.share_network_subnet_create_check
|
||||
.assert_called_once_with(
|
||||
share_network_id=self.share_network.id, neutron_net_id=None,
|
||||
neutron_subnet_id=None, availability_zone=None,
|
||||
reset_operation=restart_check))
|
||||
|
||||
|
||||
class TestShareNetworkSubnetDelete(TestShareNetworkSubnet):
|
||||
|
||||
|
|
|
@ -279,3 +279,23 @@ class ShareNetworkTest(utils.TestCase):
|
|||
expected_path,
|
||||
share_nw,
|
||||
expected_body)
|
||||
|
||||
def test_share_network_subnet_create_check(self):
|
||||
share_nw = self._FakeShareNetwork()
|
||||
|
||||
expected_path = 'share_network_subnet_create_check'
|
||||
|
||||
expected_body = {
|
||||
'neutron_net_id': self.values['neutron_net_id'],
|
||||
'neutron_subnet_id': self.values['neutron_subnet_id'],
|
||||
'reset_operation': False
|
||||
}
|
||||
|
||||
with mock.patch.object(self.manager, '_action', mock.Mock()):
|
||||
self.manager.share_network_subnet_create_check(
|
||||
share_nw, self.values['neutron_net_id'],
|
||||
self.values['neutron_subnet_id'])
|
||||
self.manager._action.assert_called_once_with(
|
||||
expected_path,
|
||||
share_nw,
|
||||
expected_body)
|
||||
|
|
|
@ -321,3 +321,30 @@ class ShareNetworkManager(base.ManagerWithFind):
|
|||
"""
|
||||
return self._action('reset_status', share_network,
|
||||
{"status": state})
|
||||
|
||||
@api_versions.wraps("2.70")
|
||||
def share_network_subnet_create_check(self, share_network_id,
|
||||
neutron_net_id=None,
|
||||
neutron_subnet_id=None,
|
||||
availability_zone=None,
|
||||
reset_operation=False):
|
||||
"""Check if share network subnet can be created in given share network.
|
||||
|
||||
:param share_network_id: ID of the Share network
|
||||
:param neutron_net_id: ID of Neutron network
|
||||
:param neutron_subnet_id: ID of Neutron subnet
|
||||
:param availability_zone: Name of the target availability zone
|
||||
:param reset_operation: Start over the check operation
|
||||
:rtype: :class:`ShareNetworkSubnet`
|
||||
"""
|
||||
values = {}
|
||||
if neutron_net_id:
|
||||
values['neutron_net_id'] = neutron_net_id
|
||||
if neutron_subnet_id:
|
||||
values['neutron_subnet_id'] = neutron_subnet_id
|
||||
if availability_zone:
|
||||
values['availability_zone'] = availability_zone
|
||||
values['reset_operation'] = reset_operation
|
||||
|
||||
return self._action('share_network_subnet_create_check',
|
||||
share_network_id, values)
|
||||
|
|
|
@ -4006,6 +4006,67 @@ def do_share_network_subnet_create(cs, args):
|
|||
cliutils.print_dict(info)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_network',
|
||||
metavar='<share-network>',
|
||||
help='Share network name or ID.')
|
||||
@cliutils.arg(
|
||||
'--neutron-net-id',
|
||||
'--neutron-net_id', '--neutron_net_id', '--neutron_net-id',
|
||||
metavar='<neutron-net-id>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help="Neutron network ID. Used to set up network for share servers. "
|
||||
"Optional, Default = None.")
|
||||
@cliutils.arg(
|
||||
'--neutron-subnet-id',
|
||||
'--neutron-subnet_id', '--neutron_subnet_id', '--neutron_subnet-id',
|
||||
metavar='<neutron-subnet-id>',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
help="Neutron subnet ID. Used to set up network for share servers. "
|
||||
"This subnet should belong to specified neutron network. "
|
||||
"Optional, Default = None.")
|
||||
@cliutils.arg(
|
||||
'--availability-zone',
|
||||
'--availability_zone',
|
||||
'--az',
|
||||
default=None,
|
||||
action='single_alias',
|
||||
metavar='<availability-zone>',
|
||||
help='Optional availability zone that the subnet is available within '
|
||||
'(Default=None). If None, the subnet will be considered as being '
|
||||
'available across all availability zones.')
|
||||
@cliutils.arg(
|
||||
'--reset',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=False,
|
||||
default=False,
|
||||
help='Reset and start again the check operation.'
|
||||
'(Optional, Default=False)')
|
||||
@api_versions.wraps("2.70")
|
||||
def do_share_network_subnet_create_check(cs, args):
|
||||
"""Check if a new subnet can be added to a share network."""
|
||||
if xor(bool(args.neutron_net_id), bool(args.neutron_subnet_id)):
|
||||
raise exceptions.CommandError(
|
||||
"Both neutron_net_id and neutron_subnet_id should be specified. "
|
||||
"Alternatively, neither of them should be specified.")
|
||||
|
||||
share_network = _find_share_network(cs, args.share_network)
|
||||
values = {
|
||||
'neutron_net_id': args.neutron_net_id,
|
||||
'neutron_subnet_id': args.neutron_subnet_id,
|
||||
'availability_zone': args.availability_zone,
|
||||
'reset_operation': args.reset,
|
||||
}
|
||||
subnet_create_check = (
|
||||
cs.share_networks.share_network_subnet_create_check(
|
||||
share_network.id, **values))
|
||||
# result[0] is response code, result[1] is dict body
|
||||
cliutils.print_dict(subnet_create_check[1])
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_network',
|
||||
metavar='<share-network>',
|
||||
|
@ -4434,10 +4495,14 @@ def do_share_server_list(cs, args):
|
|||
raise exceptions.CommandError(
|
||||
"Share network subnet option is only available with manila "
|
||||
"API version >= 2.51")
|
||||
else:
|
||||
elif cs.api_version < api_versions.APIVersion("2.70"):
|
||||
search_opts.update({
|
||||
'share_network_subnet_id': args.share_network_subnet})
|
||||
fields.append("Share Network Subnet Id")
|
||||
else:
|
||||
search_opts.update({
|
||||
'share_network_subnet_id': args.share_network_subnet})
|
||||
fields.append("Share Network Subnet IDs")
|
||||
|
||||
if args.columns is not None:
|
||||
fields = _split_columns(columns=args.columns)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
features:
|
||||
- The command `share-network-subnet-create-check` was added. This command
|
||||
will check if the share network subnet can be created in a specific share
|
||||
network based on the cloud support. The OSC equivalent command is also
|
||||
added, run the subnet create with the new option --check-only.
|
Loading…
Reference in New Issue