Add CLI commands for Share Networks with multiple Subnets

- Added CLI commands for subnets creation, deletion and exhibition.
- Updated commands for share server manage and added a new
  parameter to share server list.

API microverion has been bumped to 2.51.

Closes-Bug: #1588144
Partially-implements: bp share-network-multiple-subnets
Change-Id: I55c85285cbdc9aaf2c0bab2f12477212b32b799a
Depends-On: Id8814a8b26c9b9dcb1fe71d0d7e9b79e8b8a9210
Co-Authored-By: lseki <luciomitsuru.seki@fit-tecnologia.org.br>
This commit is contained in:
silvacarloss 2019-06-26 17:40:40 -03:00
parent cc401e5333
commit b7d0d0d128
21 changed files with 1068 additions and 102 deletions

View File

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

View File

@ -110,6 +110,9 @@ share_opts = [
help="Share network Name or ID, that will be used for shares. "
"Some backend drivers require a share network for share "
"creation."),
cfg.StrOpt("share_network_subnet",
help="Share network subnet ID. Some backend drivers require a "
"share network for share creation."),
cfg.StrOpt("admin_share_network",
help="Share network Name or ID, that will be used for shares "
"in admin tenant."),

View File

@ -76,7 +76,19 @@ class BaseTestCase(base.ClientTestBase):
if it is not found, assume it was deleted in test itself.
It is expected, that all resources were added as LIFO
due to restriction of deletion resources, that are in the chain.
:param resources: dict with keys 'type','id','client' and 'deleted'
:param resources: dict with keys 'type','id','client',
'deletion_params' and 'deleted'. Optional 'deletion_params'
contains additional data needed to delete some type of resources.
Ex:
params = {
'type': 'share_network_subnet',
'id': 'share-network-subnet-id',
'client': None,
'deletion_params': {
'share_network': 'share-network-id',
},
'deleted': False,
}
"""
if resources is None:
@ -89,6 +101,7 @@ class BaseTestCase(base.ClientTestBase):
if not(res["deleted"]):
res_id = res["id"]
client = res["client"]
deletion_params = res.get("deletion_params")
with handle_cleanup_exceptions():
# TODO(vponomaryov): add support for other resources
if res["type"] is "share_type":
@ -101,6 +114,15 @@ class BaseTestCase(base.ClientTestBase):
res_id, microversion=res["microversion"])
client.wait_for_share_network_deletion(
res_id, microversion=res["microversion"])
elif res["type"] is "share_network_subnet":
client.delete_share_network_subnet(
share_network_subnet=res_id,
share_network=deletion_params["share_network"],
microversion=res["microversion"])
client.wait_for_share_network_subnet_deletion(
share_network_subnet=res_id,
share_network=deletion_params["share_network"],
microversion=res["microversion"])
elif res["type"] is "share":
client.delete_share(
res_id, microversion=res["microversion"])
@ -233,7 +255,8 @@ class BaseTestCase(base.ClientTestBase):
@classmethod
def create_share_network(cls, name=None, description=None,
neutron_net_id=None,
neutron_subnet_id=None, client=None,
neutron_subnet_id=None,
availability_zone=None, client=None,
cleanup_in_class=True, microversion=None):
if client is None:
client = cls.get_admin_client()
@ -242,6 +265,7 @@ class BaseTestCase(base.ClientTestBase):
description=description,
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id,
availability_zone=availability_zone,
microversion=microversion,
)
resource = {
@ -256,6 +280,31 @@ class BaseTestCase(base.ClientTestBase):
cls.method_resources.insert(0, resource)
return share_network
@classmethod
def add_share_network_subnet(cls, share_network,
neutron_net_id=None, neutron_subnet_id=None,
availability_zone=None, client=None,
cleanup_in_class=True, microversion=None):
if client is None:
client = cls.get_admin_client()
share_network_subnet = client.add_share_network_subnet(
share_network, neutron_net_id, neutron_subnet_id,
availability_zone)
resource = {
"type": "share_network_subnet",
"id": share_network_subnet["id"],
"client": client,
"deletion_params": {
"share_network": share_network,
},
"microversion": microversion,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
return share_network_subnet
@classmethod
def create_share(cls, share_protocol=None, size=None, share_network=None,
share_type=None, name=None, description=None,

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ast
import re
import time
@ -33,6 +34,7 @@ MESSAGE = 'message'
SHARE = 'share'
SHARE_TYPE = 'share_type'
SHARE_NETWORK = 'share_network'
SHARE_NETWORK_SUBNET = 'share_network_subnet'
SHARE_SERVER = 'share_server'
SNAPSHOT = 'snapshot'
SHARE_REPLICA = 'share_replica'
@ -115,20 +117,21 @@ class ManilaCLIClient(base.CLIClient):
'manila', action, flags, params, fail_ok, merge_stderr)
def wait_for_resource_deletion(self, res_type, res_id, interval=3,
timeout=180, microversion=None):
timeout=180, microversion=None, **kwargs):
"""Resource deletion waiter.
:param res_type: text -- type of resource. Supported only 'share_type'.
Other types support is TODO.
:param res_type: text -- type of resource
:param res_id: text -- ID of resource to use for deletion check
:param interval: int -- interval between requests in seconds
:param timeout: int -- total time in seconds to wait for deletion
:param args: dict -- additional keyword arguments for deletion func
"""
# TODO(vponomaryov): add support for other resource types
if res_type == SHARE_TYPE:
func = self.is_share_type_deleted
elif res_type == SHARE_NETWORK:
func = self.is_share_network_deleted
elif res_type == SHARE_NETWORK_SUBNET:
func = self.is_share_network_subnet_deleted
elif res_type == SHARE_SERVER:
func = self.is_share_server_deleted
elif res_type == SHARE:
@ -143,11 +146,11 @@ class ManilaCLIClient(base.CLIClient):
raise exceptions.InvalidResource(message=res_type)
end_loop_time = time.time() + timeout
deleted = func(res_id, microversion=microversion)
deleted = func(res_id, microversion=microversion, **kwargs)
while not (deleted or time.time() > end_loop_time):
time.sleep(interval)
deleted = func(res_id, microversion=microversion)
deleted = func(res_id, microversion=microversion, **kwargs)
if not deleted:
raise exceptions.ResourceReleaseFailed(
@ -451,7 +454,8 @@ class ManilaCLIClient(base.CLIClient):
def create_share_network(self, name=None, description=None,
nova_net_id=None, neutron_net_id=None,
neutron_subnet_id=None, microversion=None):
neutron_subnet_id=None, availability_zone=None,
microversion=None):
"""Creates share network.
:param name: text -- desired name of new share network
@ -468,7 +472,9 @@ class ManilaCLIClient(base.CLIClient):
description=description,
nova_net_id=nova_net_id,
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id)
neutron_subnet_id=neutron_subnet_id,
availability_zone=availability_zone
)
share_network_raw = self.manila(
'share-network-create %s' % params, microversion=microversion)
share_network = output_parser.details(share_network_raw)
@ -476,7 +482,8 @@ class ManilaCLIClient(base.CLIClient):
def _combine_share_network_data(self, name=None, description=None,
nova_net_id=None, neutron_net_id=None,
neutron_subnet_id=None):
neutron_subnet_id=None,
availability_zone=None):
"""Combines params for share network operations 'create' and 'update'.
:returns: text -- set of CLI parameters
@ -492,9 +499,11 @@ class ManilaCLIClient(base.CLIClient):
data['--neutron_net_id'] = neutron_net_id
if neutron_subnet_id is not None:
data['--neutron_subnet_id'] = neutron_subnet_id
if availability_zone is not None:
data['--availability_zone'] = availability_zone
cmd = ''
for key, value in data.items():
cmd += "%(k)s=%(v)s " % dict(k=key, v=value)
cmd += "%(k)s=%(v)s " % {'k': key, 'v': value}
return cmd
@not_found_wrapper
@ -598,6 +607,106 @@ class ManilaCLIClient(base.CLIClient):
SHARE_NETWORK, res_id=share_network, interval=2, timeout=6,
microversion=microversion)
# Share Network Subnets
def _combine_share_network_subnet_data(self, neutron_net_id=None,
neutron_subnet_id=None,
availability_zone=None):
"""Combines params for share network subnet 'create' operation.
:returns: text -- set of CLI parameters
"""
data = dict()
if neutron_net_id is not None:
data['--neutron_net_id'] = neutron_net_id
if neutron_subnet_id is not None:
data['--neutron_subnet_id'] = neutron_subnet_id
if availability_zone is not None:
data['--availability_zone'] = availability_zone
cmd = ''
for key, value in data.items():
cmd += "%(k)s=%(v)s " % dict(k=key, v=value)
return cmd
def add_share_network_subnet(self, share_network,
neutron_net_id=None, neutron_subnet_id=None,
availability_zone=None, microversion=None):
"""Create new share network subnet for the given share network."""
params = self._combine_share_network_subnet_data(
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id,
availability_zone=availability_zone)
share_network_subnet_raw = self.manila(
'share-network-subnet-create %(sn)s %(params)s' %
{'sn': share_network, 'params': params}, microversion=microversion)
share_network_subnet = output_parser.details(share_network_subnet_raw)
return share_network_subnet
def get_share_network_subnet(self, share_network, share_network_subnet,
microversion=None):
"""Returns share network subnet by share network ID and subnet ID."""
share_network_subnet_raw = self.manila(
'share-network-subnet-show %(share_net)s %(share_subnet)s' % {
'share_net': share_network,
'share_subnet': share_network_subnet,
})
share_network_subnet = output_parser.details(share_network_subnet_raw)
return share_network_subnet
def get_share_network_subnets(self, share_network, microversion=None):
share_network = self.get_share_network(share_network,
microversion=microversion)
raw_subnets = share_network.get('share_network_subnets')
subnets = ast.literal_eval(raw_subnets)
# NOTE(lseki): convert literal None to string 'None'
for subnet in subnets:
for k, v in subnet.items():
subnet[k] = str(v) if v is None else v
return subnets
@not_found_wrapper
def delete_share_network_subnet(self, share_network, share_network_subnet,
microversion=None):
"""Delete a share_network."""
self.manila(
'share-network-subnet-delete %(share_net)s %(share_subnet)s' % {
'share_net': share_network,
'share_subnet': share_network_subnet,
}, microversion=microversion)
def is_share_network_subnet_deleted(self, share_network_subnet,
share_network, microversion=None):
# NOTE(lseki): the parameter share_network_subnet comes before
# share_network, because the wrapper method wait_for_resource_deletion
# expects the resource id in first place (subnet ID in this case).
"""Says whether share network subnet is deleted or not.
:param share_network_subnet: text -- Name or ID of share network subnet
:param share_network: text -- Name or ID of share network the subnet
belongs to
"""
subnets = self.get_share_network_subnets(share_network)
return not any(subnet['id'] == share_network_subnet
for subnet in subnets)
def wait_for_share_network_subnet_deletion(self, share_network_subnet,
share_network,
microversion=None):
# NOTE(lseki): the parameter share_network_subnet comes before
# share_network, because the wrapper method wait_for_resource_deletion
# expects the resource id in first place (subnet ID in this case).
"""Wait for share network subnet deletion by its Name or ID.
:param share_network_subnet: text -- Name or ID of share network subnet
:param share_network: text -- Name or ID of share network the subnet
belongs to
"""
args = {'share_network': share_network}
self.wait_for_resource_deletion(
SHARE_NETWORK_SUBNET, res_id=share_network_subnet,
interval=2, timeout=6, microversion=microversion, **args)
# Shares
def create_share(self, share_protocol, size, share_network=None,

View File

@ -0,0 +1,120 @@
# Copyright 2019 NetApp
# 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.
import ddt
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
@ddt.ddt
@utils.skip_if_microversion_not_supported('2.51')
class ShareNetworkSubnetsReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(ShareNetworkSubnetsReadWriteTest, cls).setUpClass()
cls.name = data_utils.rand_name('autotest')
cls.description = 'fake_description'
cls.neutron_net_id = 'fake_neutron_net_id'
cls.neutron_subnet_id = 'fake_neutron_subnet_id'
cls.sn = cls.create_share_network(
name=cls.name,
description=cls.description,
neutron_net_id=cls.neutron_net_id,
neutron_subnet_id=cls.neutron_subnet_id,
)
def test_get_share_network_subnet(self):
default_subnet = utils.get_default_subnet(self.user_client,
self.sn['id'])
subnet = self.user_client.get_share_network_subnet(
self.sn['id'], default_subnet['id'])
self.assertEqual(self.neutron_net_id, subnet['neutron_net_id'])
self.assertEqual(self.neutron_subnet_id, subnet['neutron_subnet_id'])
def test_get_invalid_share_network_subnet(self):
self.assertRaises(
exceptions.CommandFailed,
self.user_client.get_share_network_subnet,
self.sn['id'], 'invalid_subnet_id')
def _get_availability_zone(self):
availability_zones = self.user_client.list_availability_zones()
return availability_zones[0]['Name']
def test_add_share_network_subnet_to_share_network(self):
neutron_net_id = 'new_neutron_net_id'
neutron_subnet_id = 'new_neutron_subnet_id'
availability_zone = self._get_availability_zone()
subnet = self.add_share_network_subnet(
self.sn['id'],
neutron_net_id, neutron_subnet_id,
availability_zone,
cleanup_in_class=False)
self.assertEqual(neutron_net_id, subnet['neutron_net_id'])
self.assertEqual(neutron_subnet_id, subnet['neutron_subnet_id'])
self.assertEqual(availability_zone, subnet['availability_zone'])
@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_add_invalid_share_network_subnet_to_share_network(self, params):
self.assertRaises(
exceptions.CommandFailed,
self.add_share_network_subnet,
self.sn['id'],
**params)
def test_add_share_network_subnet_to_invalid_share_network(self):
self.assertRaises(
exceptions.CommandFailed,
self.add_share_network_subnet,
'invalid_share_network',
self.neutron_net_id,
self.neutron_subnet_id)
def test_add_delete_share_network_subnet_from_share_network(self):
neutron_net_id = 'new_neutron_net_id'
neutron_subnet_id = 'new_neutron_subnet_id'
availability_zone = self._get_availability_zone()
subnet = self.add_share_network_subnet(
self.sn['id'],
neutron_net_id, neutron_subnet_id,
availability_zone,
cleanup_in_class=False)
self.user_client.delete_share_network_subnet(
share_network_subnet=subnet['id'],
share_network=self.sn['id'])
self.user_client.wait_for_share_network_subnet_deletion(
share_network_subnet=subnet['id'],
share_network=self.sn['id'])
def test_delete_invalid_share_network_subnet(self):
self.assertRaises(
exceptions.NotFound,
self.user_client.delete_share_network_subnet,
share_network_subnet='invalid_subnet_id',
share_network=self.sn['id'])

View File

@ -14,11 +14,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import ast
import ddt
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as tempest_lib_exc
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
@ddt.ddt
@ -46,7 +48,14 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
'neutron_subnet_id': 'fake_neutron_subnet_id'},
)
def test_create_delete_share_network(self, net_data):
share_subnet_support = utils.share_network_subnets_are_supported()
share_subnet_fields = (
['neutron_net_id', 'neutron_subnet_id', 'availability_zone']
if share_subnet_support else [])
sn = self.create_share_network(cleanup_in_class=False, **net_data)
default_subnet = (utils.get_default_subnet(self.user_client, sn['id'])
if share_subnet_support
else None)
expected_data = {
'name': 'None',
@ -55,9 +64,54 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
'neutron_subnet_id': 'None',
}
expected_data.update(net_data)
share_network_expected_data = [
(k, v) for k, v in expected_data.items()
if k not in share_subnet_fields]
share_subnet_expected_data = [
(k, v) for k, v in expected_data.items()
if k in share_subnet_fields]
for k, v in expected_data.items():
for k, v in share_network_expected_data:
self.assertEqual(v, sn[k])
for k, v in share_subnet_expected_data:
self.assertEqual(v, default_subnet[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
@utils.skip_if_microversion_not_supported('2.51')
def test_create_delete_share_network_with_az(self):
share_subnet_fields = (
['neutron_net_id', 'neutron_subnet_id', 'availability_zone'])
az = self.user_client.list_availability_zones()[0]
net_data = {
'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id',
'availability_zone': az['Name']
}
sn = self.create_share_network(cleanup_in_class=False, **net_data)
default_subnet = utils.get_subnet_by_availability_zone_name(
self.user_client, sn['id'], az['Name'])
expected_data = {
'name': 'None',
'description': 'None',
'neutron_net_id': 'None',
'neutron_subnet_id': 'None',
'availability_zone': 'None',
}
expected_data.update(net_data)
share_network_expected_data = [
(k, v) for k, v in expected_data.items()
if k not in share_subnet_fields]
share_subnet_expected_data = [
(k, v) for k, v in expected_data.items()
if k in share_subnet_fields]
for k, v in share_network_expected_data:
self.assertEqual(v, sn[k])
for k, v in share_subnet_expected_data:
self.assertEqual(v, default_subnet[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
@ -67,8 +121,9 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
self.assertEqual(self.name, get['name'])
self.assertEqual(self.description, get['description'])
self.assertEqual(self.neutron_net_id, get['neutron_net_id'])
self.assertEqual(self.neutron_subnet_id, get['neutron_subnet_id'])
if not utils.share_network_subnets_are_supported():
self.assertEqual(self.neutron_net_id, get['neutron_net_id'])
self.assertEqual(self.neutron_subnet_id, get['neutron_subnet_id'])
@ddt.data(
{'name': data_utils.rand_name('autotest_share_network_name')},
@ -91,12 +146,22 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
'neutron_net_id': 'None',
'neutron_subnet_id': 'None',
}
subnet_keys = []
if utils.share_network_subnets_are_supported():
subnet_keys = ['neutron_net_id', 'neutron_subnet_id']
subnet = ast.literal_eval(update['share_network_subnets'])
expected_data['neutron_net_id'] = None
expected_data['neutron_subnet_id'] = None
update_values = dict([(k, v) for k, v in net_data.items()
if v != '""'])
expected_data.update(update_values)
for k, v in expected_data.items():
self.assertEqual(v, update[k])
if k in subnet_keys:
self.assertEqual(v, subnet[0][k])
else:
self.assertEqual(v, update[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
@ -119,6 +184,14 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
self.assertTrue(all('name' not in s for s in share_networks))
def _list_share_networks_with_filters(self, filters):
assert_subnet_fields = utils.share_network_subnets_are_supported()
share_subnet_fields = (['neutron_subnet_id', 'neutron_net_id']
if assert_subnet_fields
else [])
share_network_filters = [(k, v) for k, v in filters.items()
if k not in share_subnet_fields]
share_network_subnet_filters = [(k, v) for k, v in filters.items()
if k in share_subnet_fields]
share_networks = self.admin_client.list_share_networks(filters=filters)
self.assertGreater(len(share_networks), 0)
@ -126,14 +199,21 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
any(self.sn['id'] == sn['id'] for sn in share_networks))
for sn in share_networks:
try:
get = self.admin_client.get_share_network(sn['id'])
share_network = self.admin_client.get_share_network(sn['id'])
default_subnet = (
utils.get_default_subnet(self.user_client, sn['id'])
if assert_subnet_fields
else None)
except tempest_lib_exc.NotFound:
# NOTE(vponomaryov): Case when some share network was deleted
# between our 'list' and 'get' requests. Skip such case.
continue
for k, v in filters.items():
self.assertIn(k, get)
self.assertEqual(v, get[k])
for k, v in share_network_filters:
self.assertIn(k, share_network)
self.assertEqual(v, share_network[k])
for k, v in share_network_subnet_filters:
self.assertIn(k, default_subnet)
self.assertEqual(v, default_subnet[k])
def test_list_share_networks_filter_by_project_id(self):
project_id = self.admin_client.get_project_id(

View File

@ -89,13 +89,18 @@ class ShareServersReadWriteBase(base.BaseTestCase):
common_share_network = self.client.get_share_network(
self.client.share_network)
share_net_info = (
utils.get_default_subnet(self.user_client,
common_share_network['id'])
if utils.share_network_subnets_are_supported()
else common_share_network)
neutron_net_id = (
common_share_network['neutron_net_id']
if 'none' not in common_share_network['neutron_net_id'].lower()
share_net_info['neutron_net_id']
if 'none' not in share_net_info['neutron_net_id'].lower()
else None)
neutron_subnet_id = (
common_share_network['neutron_subnet_id']
if 'none' not in common_share_network['neutron_subnet_id'].lower()
share_net_info['neutron_subnet_id']
if 'none' not in share_net_info['neutron_subnet_id'].lower()
else None)
share_network = self.client.create_share_network(
neutron_net_id=neutron_net_id,
@ -142,6 +147,7 @@ class ShareServersReadWriteBase(base.BaseTestCase):
self.assertIn(key, server)
self._delete_share_and_share_server(self.share['id'], share_server_id)
self.client.delete_share_network(share_network['id'])
@testtools.skipUnless(
CONF.run_manage_tests, 'Share Manage/Unmanage tests are disabled.')
@ -189,6 +195,7 @@ class ShareServersReadWriteBase(base.BaseTestCase):
self._delete_share_and_share_server(managed_share_id,
managed_share_server_id)
self.client.delete_share_network(share_network['id'])
class ShareServersReadWriteNFSTest(ShareServersReadWriteBase):

View File

@ -113,9 +113,14 @@ class SharesTestMigration(base.BaseTestCase):
cls.old_share_net = cls.get_user_client().get_share_network(
cls.get_user_client().share_network)
share_net_info = (
utils.get_default_subnet(cls.get_user_client(),
cls.old_share_net['id'])
if utils.share_network_subnets_are_supported()
else cls.old_share_net)
cls.new_share_net = cls.create_share_network(
neutron_net_id=cls.old_share_net['neutron_net_id'],
neutron_subnet_id=cls.old_share_net['neutron_subnet_id'])
neutron_net_id=share_net_info['neutron_net_id'],
neutron_subnet_id=share_net_info['neutron_subnet_id'])
@utils.skip_if_microversion_not_supported('2.22')
@ddt.data('migration_error', 'migration_success', 'None')

View File

@ -138,3 +138,18 @@ def choose_matching_backend(share, pools, share_type):
None)
return selected_pool['Name']
def share_network_subnets_are_supported():
return is_microversion_supported('2.51')
def get_subnet_by_availability_zone_name(client, share_network_id, az_name):
subnets = client.get_share_network_subnets(share_network_id)
return next((subnet for subnet in subnets
if subnet['availability_zone'] == az_name), None)
def get_default_subnet(client, share_network_id):
return get_subnet_by_availability_zone_name(client, share_network_id,
'None')

View File

@ -579,6 +579,9 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
def post_share_networks(self, **kwargs):
return (202, {}, {'share_network': {}})
def post_share_networks_1234_subnets(self, **kwargs):
return (202, {}, {'share_network_subnet': {}})
def post_shares(self, **kwargs):
return (202, {}, {'share': {}})
@ -609,6 +612,12 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
def delete_share_networks_fake_share_network2(self, **kwargs):
return (202, {}, None)
def delete_share_networks_1234_subnets_fake_subnet1(self, **kwargs):
return (202, {}, None)
def delete_share_networks_1234_subnets_fake_subnet2(self, **kwargs):
return (202, {}, None)
def delete_snapshots_fake_snapshot1(self, **kwargs):
return (202, {}, None)
@ -663,6 +672,14 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
}
return (200, {}, share_nw)
def get_share_networks_1234_subnets_fake_subnet_id(self, **kw):
subnet = {
'share_network_subnet': {
'id': 'fake_subnet_id',
},
}
return (200, {}, subnet)
def get_security_services(self, **kw):
security_services = {
'security_services': [

View File

@ -0,0 +1,82 @@
# Copyright 2019 NetApp
# 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.
import ddt
import mock
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import share_network_subnets
@ddt.ddt
class ShareNetworkSubnetTest(utils.TestCase):
def setUp(self):
super(ShareNetworkSubnetTest, self).setUp()
self.manager = share_network_subnets.ShareNetworkSubnetManager(
fakes.FakeClient())
def test_create(self):
share_network_id = 'fake_share_net_id'
expected_url = share_network_subnets.RESOURCES_PATH % {
'share_network_id': share_network_id
}
expected_values = {
'neutron_net_id': 'fake_net_id',
'neutron_subnet_id': 'fake_subnet_id',
'availability_zone': 'fake_availability_zone',
}
expected_body = {'share-network-subnet': expected_values}
payload = expected_values.copy()
payload.update({'share_network_id': share_network_id})
with mock.patch.object(self.manager, '_create', fakes.fake_create):
result = self.manager.create(**payload)
self.assertEqual(expected_url, result['url'])
self.assertEqual(
share_network_subnets.RESOURCE_NAME,
result['resp_key'])
self.assertEqual(
expected_body,
result['body'])
def test_get(self):
share_network = 'fake_share_network'
share_subnet = 'fake_share_subnet'
with mock.patch.object(self.manager, '_get', mock.Mock()):
self.manager.get(share_network, share_subnet)
self.manager._get.assert_called_once_with(
share_network_subnets.RESOURCE_PATH % {
'share_network_id': share_network,
'share_network_subnet_id': share_subnet
},
share_network_subnets.RESOURCE_NAME)
def test_delete(self):
share_network = 'fake_share_network'
share_subnet = 'fake_share_subnet'
with mock.patch.object(self.manager, '_delete', mock.Mock()):
self.manager.delete(share_network, share_subnet)
self.manager._delete.assert_called_once_with(
share_network_subnets.RESOURCE_PATH % {
'share_network_id': share_network,
'share_network_subnet_id': share_subnet
})

View File

@ -42,14 +42,6 @@ class ShareReplicasTest(utils.TestCase):
}
self._create_common(values)
def test_create_with_share_network(self):
values = {
'availability_zone': 'az1',
'share': 's1',
'share_network': 'sn1',
}
self._create_common(values)
def _create_common(self, values):
with mock.patch.object(self.manager, '_create', fakes.fake_create):

View File

@ -87,6 +87,7 @@ class ShareServerManagerTest(utils.TestCase):
def test_manage(self, driver_options):
host = 'fake_host'
share_network_id = 'fake_share_net_id'
share_network_subnet_id = 'fake_share_network_subnet_id'
identifier = 'ff-aa-kk-ee-00'
if driver_options is None:
driver_options = {}
@ -94,12 +95,15 @@ class ShareServerManagerTest(utils.TestCase):
'host': host,
'share_network_id': share_network_id,
'identifier': identifier,
'driver_options': driver_options
'driver_options': driver_options,
'share_network_subnet_id': share_network_subnet_id,
}
with mock.patch.object(self.manager, '_create',
mock.Mock(return_value='fake')):
result = self.manager.manage(host, share_network_id, identifier,
driver_options)
result = self.manager.manage(
host, share_network_id, identifier,
driver_options=driver_options,
share_network_subnet_id=share_network_subnet_id)
self.manager._create.assert_called_once_with(
share_servers.RESOURCES_PATH + '/manage',
{'share_server': expected_body}, 'share_server'

View File

@ -35,6 +35,7 @@ from manilaclient import utils
from manilaclient.v2 import messages
from manilaclient.v2 import security_services
from manilaclient.v2 import share_instances
from manilaclient.v2 import share_network_subnets
from manilaclient.v2 import share_networks
from manilaclient.v2 import share_servers
from manilaclient.v2 import share_snapshots
@ -734,36 +735,96 @@ class ShellTest(test_utils.TestCase):
+ ' --share_type fake_share_type'
+ ' --share_server_id fake_server')
def test_share_server_manage_unsupported_version(self):
self.assertRaises(
exceptions.UnsupportedVersion,
self.run_command,
'--os-share-api-version 2.48 ' +
'share-server-manage fake_host fake_share_net_id fake_id')
def test_share_server_manage_invalid_param_subnet_id(self):
self.assertRaises(
exceptions.CommandError,
self.run_command,
'--os-share-api-version 2.49 ' +
'share-server-manage fake_host fake_share_net_id fake_id ' +
'--share-network-subnet fake_subnet_id')
@ddt.data({'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
},
'version': '--os-share-api-version 2.49',
},
}},
{'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'subnet_id': 'fake_subnet_1',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
}},
{'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
},
'version': '2.51',
},
{'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'subnet_id': 'fake_subnet_1',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
},
'version': '2.51',
},
{'driver_args': "",
'valid_params': {
'driver_options': {}
},
'version': '--os-share-api-version 2.49',
})
'version': '2.51',
},
{'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
},
'version': '2.49',
},
{'driver_args': '',
'valid_params': {
'driver_options': {},
},
'network_id': 'fake_network_id',
'version': '2.49',
},
{'driver_args': "",
'valid_params': {
'driver_options': {}
},
'version': '2.49',
},
)
@ddt.unpack
def test_share_server_manage(self, driver_args, valid_params,
version=None):
version=None, network_id=None,
subnet_id=None):
subnet_support = (version is None or
api_versions.APIVersion(version) >=
api_versions.APIVersion('2.51'))
network_id = '3456' if network_id is None else network_id
fake_share_network = type(
'FakeShareNetwork', (object,), {'id': '3456'})
'FakeShareNetwork', (object,), {'id': network_id})
self.mock_object(
shell_v2, '_find_share_network',
mock.Mock(return_value=fake_share_network))
command = "" if version is None else version
command += (' share-server-manage fake_host fake_share_net_id '
+ ' 88-as-23-f3-45 ' + driver_args)
command = ('share-server-manage '
'%(host)s '
'%(share_network_id)s '
'%(identifier)s '
'%(driver_args)s ' % {
'host': 'fake_host',
'share_network_id': fake_share_network.id,
'identifier': '88-as-23-f3-45',
'driver_args': driver_args,
})
command += '--share-network-subnet %s' % subnet_id if subnet_id else ''
self.run_command(command)
self.run_command(command, version=version)
expected = {
'share_server': {
@ -773,6 +834,8 @@ class ShellTest(test_utils.TestCase):
'driver_options': driver_args
}
}
if subnet_support:
expected['share_server']['share_network_subnet_id'] = subnet_id
expected['share_server'].update(valid_params)
self.assert_called('POST', '/share-servers/manage', body=expected)
@ -1425,25 +1488,39 @@ class ShellTest(test_utils.TestCase):
self.assert_called('POST', '/share-networks')
@ddt.unpack
@ddt.data(
{'--name': 'fake_name'},
{'--description': 'fake_description'},
{'--neutron_net_id': 'fake_neutron_net_id'},
{'--neutron_subnet_id': 'fake_neutron_subnet_id'},
{'--description': 'fake_description',
'--name': 'fake_name',
'--neutron_net_id': 'fake_neutron_net_id',
'--neutron_subnet_id': 'fake_neutron_subnet_id'},
{'--name': '""'},
{'--description': '""'},
{'--neutron_net_id': '""'},
{'--neutron_subnet_id': '""'},
{'--description': '""',
'--name': '""',
'--neutron_net_id': '""',
'--neutron_subnet_id': '""',
},)
def test_share_network_update(self, data):
{'data': {'--name': 'fake_name'}},
{'data': {'--description': 'fake_description'}},
{'data': {'--neutron_net_id': 'fake_neutron_net_id'},
'version': '2.49',
},
{'data': {'--neutron_subnet_id': 'fake_neutron_subnet_id'},
'version': '2.49',
},
{'data': {
'--description': 'fake_description',
'--name': 'fake_name',
'--neutron_net_id': 'fake_neutron_net_id',
'--neutron_subnet_id': 'fake_neutron_subnet_id'},
'version': '2.49',
},
{'data': {'--name': '""'}},
{'data': {'--description': '""'}},
{'data': {'--neutron_net_id': '""'},
'version': '2.49',
},
{'data': {'--neutron_subnet_id': '""'},
'version': '2.49',
},
{'data': {
'--description': '""',
'--name': '""',
'--neutron_net_id': '""',
'--neutron_subnet_id': '""'},
'version': '2.49',
})
def test_share_network_update(self, data, version=None):
cmd = 'share-network-update 1111'
expected = dict()
for k, v in data.items():
@ -1451,7 +1528,7 @@ class ShellTest(test_utils.TestCase):
expected[k[2:]] = v
expected = dict(share_network=expected)
self.run_command(cmd)
self.run_command(cmd, version=version)
self.assert_called('PUT', '/share-networks/1111', body=expected)
@ -1723,6 +1800,143 @@ class ShellTest(test_utils.TestCase):
'/security-services/detail?share_network_id=1111',
)
@ddt.data(
{},
{'--neutron_net_id': 'fake_neutron_net_id',
'--neutron_subnet_id': 'fake_neutron_subnet_id'},
{'--availability-zone': 'fake_availability_zone_id'},
{'--neutron_net_id': 'fake_neutron_net_id',
'--neutron_subnet_id': 'fake_neutron_subnet_id',
'--availability-zone': 'fake_availability_zone_id'})
def test_share_network_subnet_add(self, data):
fake_share_network = type(
'FakeShareNetwork', (object,), {'id': '1234'})
self.mock_object(
shell_v2, '_find_share_network',
mock.Mock(return_value=fake_share_network))
cmd = 'share-network-subnet-create'
for k, v in data.items():
cmd += ' ' + k + ' ' + v
cmd += ' ' + fake_share_network.id
self.run_command(cmd)
shell_v2._find_share_network.assert_called_once_with(
mock.ANY, fake_share_network.id)
self.assert_called('POST', '/share-networks/1234/subnets')
@ddt.data(
{'--neutron_net_id': 'fake_neutron_net_id'},
{'--neutron_subnet_id': 'fake_neutron_subnet_id'},
{'--neutron_net_id': 'fake_neutron_net_id',
'--availability-zone': 'fake_availability_zone_id'},
{'--neutron_subnet_id': 'fake_neutron_subnet_id',
'--availability-zone': 'fake_availability_zone_id'})
def test_share_network_subnet_add_invalid_param(self, data):
cmd = 'share-network-subnet-create'
for k, v in data.items():
cmd += ' ' + k + ' ' + v
cmd += ' fake_network_id'
self.assertRaises(
exceptions.CommandError,
self.run_command,
cmd)
def test_share_network_subnet_add_invalid_share_network(self):
cmd = 'share-network-subnet-create not-found-id'
self.assertRaises(
exceptions.CommandError,
self.run_command,
cmd)
@ddt.data(('fake_subnet1', ),
('fake_subnet1', 'fake_subnet2'))
def test_share_network_subnet_delete(self, subnet_ids):
fake_share_network = type(
'FakeShareNetwork', (object,), {'id': '1234'})
self.mock_object(
shell_v2, '_find_share_network',
mock.Mock(return_value=fake_share_network))
fake_share_network_subnets = [
share_network_subnets.ShareNetworkSubnet(
'fake', {'id': subnet_id}, True)
for subnet_id in subnet_ids
]
self.run_command(
'share-network-subnet-delete %(network_id)s %(subnet_ids)s' % {
'network_id': fake_share_network.id,
'subnet_ids': ' '.join(subnet_ids)
})
shell_v2._find_share_network.assert_called_once_with(
mock.ANY, fake_share_network.id)
for subnet in fake_share_network_subnets:
self.assert_called_anytime(
'DELETE', '/share-networks/1234/subnets/%s' % subnet.id,
clear_callstack=False)
def test_share_network_subnet_delete_invalid_share_network(self):
command = 'share-network-subnet-delete %(net_id)s %(subnet_id)s' % {
'net_id': 'not-found-id',
'subnet_id': '1234',
}
self.assertRaises(
exceptions.CommandError,
self.run_command,
command)
def test_share_network_subnet_delete_invalid_share_network_subnet(self):
fake_share_network = type(
'FakeShareNetwork', (object,), {'id': '1234'})
self.mock_object(
shell_v2, '_find_share_network',
mock.Mock(return_value=fake_share_network))
command = 'share-network-subnet-delete %(net_id)s %(subnet_id)s' % {
'net_id': fake_share_network.id,
'subnet_id': 'not-found-id',
}
self.assertRaises(
exceptions.CommandError,
self.run_command,
command)
@mock.patch.object(cliutils, 'print_dict', mock.Mock())
def test_share_network_subnet_show(self):
fake_share_network = type(
'FakeShareNetwork', (object,), {'id': '1234'})
self.mock_object(
shell_v2, '_find_share_network',
mock.Mock(return_value=fake_share_network))
args = {
'share_net_id': fake_share_network.id,
'subnet_id': 'fake_subnet_id',
}
self.run_command(
'share-network-subnet-show %(share_net_id)s %(subnet_id)s' % args)
self.assert_called(
'GET',
'/share-networks/%(share_net_id)s/subnets/%(subnet_id)s' % args,
)
cliutils.print_dict.assert_called_once_with(mock.ANY)
def test_share_network_subnet_show_invalid_share_network(self):
command = 'share-network-subnet-show %(net_id)s %(subnet_id)s' % {
'net_id': 'not-found-id',
'subnet_id': 1234,
}
self.assertRaises(
exceptions.CommandError,
self.run_command,
command)
@mock.patch.object(cliutils, 'print_list', mock.Mock())
def test_share_server_list_select_column(self):
self.run_command('share-server-list --columns id,host,status')
@ -2848,19 +3062,14 @@ class ShellTest(test_utils.TestCase):
@ddt.data(
'fake-share-id --az fake-az',
'fake-share-id --availability-zone fake-az --share-network '
'fake-network',
'fake-share-id --availability-zone fake-az',
)
@mock.patch.object(shell_v2, '_find_share_network', mock.Mock())
@mock.patch.object(shell_v2, '_find_share', mock.Mock())
def test_share_replica_create(self, data):
fshare = type('FakeShare', (object,), {'id': 'fake-share-id'})
shell_v2._find_share.return_value = fshare
fnetwork = type('FakeShareNetwork', (object,), {'id': 'fake-network'})
shell_v2._find_share_network.return_value = fnetwork
cmd = 'share-replica-create' + ' ' + data
self.run_command(cmd)

View File

@ -36,6 +36,7 @@ from manilaclient.v2 import share_group_types
from manilaclient.v2 import share_groups
from manilaclient.v2 import share_instance_export_locations
from manilaclient.v2 import share_instances
from manilaclient.v2 import share_network_subnets
from manilaclient.v2 import share_networks
from manilaclient.v2 import share_replica_export_locations
from manilaclient.v2 import share_replicas
@ -207,6 +208,8 @@ class Client(object):
self.services = services.ServiceManager(self)
self.security_services = security_services.SecurityServiceManager(self)
self.share_networks = share_networks.ShareNetworkManager(self)
self.share_network_subnets = (
share_network_subnets.ShareNetworkSubnetManager(self))
self.quota_classes = quota_classes.QuotaClassSetManager(self)
self.quotas = quotas.QuotaSetManager(self)

View File

@ -0,0 +1,92 @@
# Copyright 2019 NetApp
# 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 base
from manilaclient.common.apiclient import base as common_base
RESOURCES_PATH = '/share-networks/%(share_network_id)s/subnets'
RESOURCE_PATH = RESOURCES_PATH + '/%(share_network_subnet_id)s'
RESOURCE_NAME = 'share_network_subnet'
class ShareNetworkSubnet(common_base.Resource):
"""Network subnet info for Manila share networks."""
def __repr__(self):
return "<ShareNetworkSubnet: %s>" % self.id
def __getitem__(self, key):
return self._info[key]
def delete(self):
"""Delete this share network subnet."""
self.manager.delete(self)
class ShareNetworkSubnetManager(base.ManagerWithFind):
"""Manage :class:`ShareNetworkSubnet` resources."""
resource_class = ShareNetworkSubnet
def create(self, neutron_net_id=None, neutron_subnet_id=None,
availability_zone=None, share_network_id=None):
"""Create share network subnet.
: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
: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
body = {'share-network-subnet': values}
url = '/share-networks/%(share_network_id)s/subnets' % {
'share_network_id': share_network_id
}
return self._create(url, body, RESOURCE_NAME)
def get(self, share_network, share_network_subnet):
"""Get a share network subnet.
:param policy: share network subnet to get.
:rtype: :class:`NetworkSubnetInfo`
"""
share_network_id = common_base.getid(share_network)
share_network_subnet_id = common_base.getid(share_network_subnet)
url = ('/share-networks/%(share_network_id)s/subnets'
'/%(share_network_subnet)s') % {
'share_network_id': share_network_id,
'share_network_subnet': share_network_subnet_id
}
return self._get(url, "share_network_subnet")
def delete(self, share_network, share_network_subnet):
"""Delete a share network subnet.
:param share_network: share network that owns the subnet.
:param share_network_subnet: share network subnet to be deleted.
"""
url = ('/share-networks/%(share_network_id)s/subnets'
'/%(share_network_subnet)s') % {
'share_network_id': common_base.getid(share_network),
'share_network_subnet': share_network_subnet
}
self._delete(url)

View File

@ -70,7 +70,7 @@ class ShareNetworkManager(base.ManagerWithFind):
return self._create(RESOURCES_PATH, body, RESOURCE_NAME)
@api_versions.wraps("2.26") # noqa
@api_versions.wraps("2.26", "2.50") # noqa
def create(self, neutron_net_id=None, neutron_subnet_id=None,
name=None, description=None):
"""Create share network.
@ -95,6 +95,26 @@ class ShareNetworkManager(base.ManagerWithFind):
return self._create(RESOURCES_PATH, body, RESOURCE_NAME)
@api_versions.wraps("2.51") # noqa
def create(self, neutron_net_id=None, neutron_subnet_id=None,
name=None, description=None, availability_zone=None):
values = {}
if neutron_net_id:
values['neutron_net_id'] = neutron_net_id
if neutron_subnet_id:
values['neutron_subnet_id'] = neutron_subnet_id
if name:
values['name'] = name
if description:
values['description'] = description
if availability_zone:
values['availability_zone'] = availability_zone
body = {RESOURCE_NAME: values}
return self._create(RESOURCES_PATH, body, RESOURCE_NAME)
def add_security_service(self, share_network, security_service):
"""Associate given security service with a share network.

View File

@ -89,13 +89,12 @@ class ShareReplicaManager(base.ManagerWithFind):
@api_versions.wraps("2.11")
@api_versions.experimental_api
def create(self, share, availability_zone=None, share_network=None):
def create(self, share, availability_zone=None):
"""Create a replica for a share.
:param share: The share to create the replica of. Can be the share
object or its UUID.
:param availability_zone: The 'availability_zone' object or its UUID.
:param share_network: either share network object or its UUID.
"""
share_id = common_base.getid(share)
@ -104,9 +103,6 @@ class ShareReplicaManager(base.ManagerWithFind):
if availability_zone:
body['availability_zone'] = common_base.getid(availability_zone)
if share_network:
body['share_network'] = common_base.getid(share_network)
return self._create(RESOURCES_PATH,
{RESOURCE_NAME: body},
RESOURCE_NAME)

View File

@ -97,7 +97,7 @@ class ShareServerManager(base.ManagerWithFind):
query_string = self._build_query_string(search_opts)
return self._list(RESOURCES_PATH + query_string, RESOURCES_NAME)
@api_versions.wraps("2.49")
@api_versions.wraps("2.49", "2.50")
def manage(self, host, share_network_id, identifier, driver_options=None):
driver_options = driver_options or {}
@ -112,6 +112,23 @@ class ShareServerManager(base.ManagerWithFind):
return self._create(resource_path, {'share_server': body},
'share_server')
@api_versions.wraps("2.51") # noqa
def manage(self, host, share_network_id, identifier,
share_network_subnet_id=None, driver_options=None):
driver_options = driver_options or {}
body = {
'host': host,
'share_network_id': share_network_id,
'identifier': identifier,
'share_network_subnet_id': share_network_subnet_id,
'driver_options': driver_options,
}
resource_path = RESOURCE_PATH % 'manage'
return self._create(resource_path, {'share_server': body},
'share_server')
@api_versions.wraps("2.49")
def unmanage(self, share_server, force=False):
return self._action("unmanage", share_server, {'force': force})

View File

@ -16,6 +16,7 @@
from __future__ import print_function
from operator import xor
import os
import sys
import time
@ -1178,15 +1179,32 @@ def do_manage(cs, args):
help='One or more driver-specific key=value pairs that may be necessary to'
' manage the share server (Optional, Default=None).',
default=None)
@cliutils.arg(
'--share-network-subnet', '--share_network_subnet',
type=str,
metavar='<share_network_subnet>',
help="Share network subnet where share server has network allocations in. "
"The default subnet will be used if it's not specified. Available "
"for microversion >= 2.51 (Optional, Default=None).",
default=None)
def do_share_server_manage(cs, args):
"""Manage share server not handled by Manila (Admin only)."""
driver_options = _extract_key_value_options(args, 'driver_options')
share_network = _find_share_network(cs, args.share_network)
manage_kwargs = {
'driver_options': driver_options,
}
if cs.api_version < api_versions.APIVersion("2.51"):
if getattr(args, 'share_network_subnet'):
raise exceptions.CommandError(
"Share network subnet option is only available with manila "
"API version >= 2.51")
else:
manage_kwargs['share_network_subnet_id'] = args.share_network_subnet
share_server = cs.share_servers.manage(
args.host, share_network.id, args.identifier,
driver_options=driver_options)
args.host, args.share_network, args.identifier,
**manage_kwargs)
cliutils.print_dict(share_server._info)
@ -2693,6 +2711,16 @@ def do_share_network_create(cs, args):
metavar='<description>',
default=None,
help="Share network description.")
@cliutils.arg(
'--availability-zone', '--availability_zone', '--az',
metavar='<availability_zone>',
default=None,
action='single_alias',
help="Availability zone in which the subnet should be created. Share "
"networks can have one or more subnets in different availability "
"zones when the driver is operating with "
"'driver_handles_share_servers' extra_spec set to True. Available "
"only for microversion >= 2.51. (Default=None)")
def do_share_network_create(cs, args):
"""Create description for network used by the tenant."""
values = {
@ -2701,6 +2729,12 @@ def do_share_network_create(cs, args):
'name': args.name,
'description': args.description,
}
if cs.api_version >= api_versions.APIVersion("2.51"):
values['availability_zone'] = args.availability_zone
elif args.availability_zone:
raise exceptions.CommandError(
"Creating share networks with a given az is only "
"available with manila API version >= 2.51")
share_network = cs.share_networks.create(**values)
info = share_network._info.copy()
cliutils.print_dict(info)
@ -2771,9 +2805,8 @@ def do_share_network_update(cs, args):
metavar='<neutron-net-id>',
default=None,
action='single_alias',
help="Neutron network ID. Used to set up network for share servers. This "
"option is deprecated and will be rejected in newer releases of "
"OpenStack Manila.")
help="Neutron network ID. Used to set up network for share servers. "
"This option is deprecated for microversion >= 2.51.")
@cliutils.arg(
'--neutron-subnet-id',
'--neutron-subnet_id', '--neutron_subnet_id', '--neutron_subnet-id',
@ -2781,7 +2814,8 @@ def do_share_network_update(cs, args):
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.")
"This subnet should belong to specified neutron network. "
"This option is deprecated for microversion >= 2.51.")
@cliutils.arg(
'--name',
metavar='<name>',
@ -2794,6 +2828,7 @@ def do_share_network_update(cs, args):
help="Share network description.")
def do_share_network_update(cs, args):
"""Update share network data."""
values = {
'neutron_net_id': args.neutron_net_id,
'neutron_subnet_id': args.neutron_subnet_id,
@ -3189,6 +3224,100 @@ def do_share_network_security_service_list(cs, args):
cliutils.print_list(security_services, fields=fields)
@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.')
def do_share_network_subnet_create(cs, args):
"""Add a new subnet into 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 = {
'share_network_id': share_network.id,
'neutron_net_id': args.neutron_net_id,
'neutron_subnet_id': args.neutron_subnet_id,
'availability_zone': args.availability_zone,
}
share_network_subnet = cs.share_network_subnets.create(**values)
info = share_network_subnet._info.copy()
cliutils.print_dict(info)
@cliutils.arg(
'share_network',
metavar='<share-network>',
help='Share network name or ID.')
@cliutils.arg(
'share_network_subnet',
metavar='<share-network-subnet>',
nargs='+',
help='Name or ID of share network subnet(s) to be deleted.')
def do_share_network_subnet_delete(cs, args):
"""Delete one or more share network subnets."""
failure_count = 0
share_network_ref = _find_share_network(cs, args.share_network)
for subnet in args.share_network_subnet:
try:
cs.share_network_subnets.delete(share_network_ref, subnet)
except Exception as e:
failure_count += 1
print("Deletion of share network subnet %s failed: %s" % (
subnet, e), file=sys.stderr)
if failure_count == len(args.share_network_subnet):
raise exceptions.CommandError("Unable to delete any of the specified "
"share network subnets.")
@cliutils.arg(
'share_network',
metavar='<share-network>',
help='Name or ID of share network(s) to which the subnet belongs.')
@cliutils.arg(
'share_network_subnet',
metavar='<share-network-subnet>',
help='Share network subnet ID to show.')
def do_share_network_subnet_show(cs, args):
"""Show share network subnet."""
share_network = _find_share_network(cs, args.share_network)
share_network_subnet = cs.share_network_subnets.get(
share_network.id, args.share_network_subnet)
view_data = share_network_subnet._info.copy()
cliutils.print_dict(view_data)
@cliutils.arg(
'share_network',
metavar='<share-network>',
@ -3538,6 +3667,14 @@ def do_security_service_delete(cs, args):
default=None,
help='Comma separated list of columns to be displayed '
'example --columns "id,host,status".')
@cliutils.arg(
'--share-network-subnet', '--share_network_subnet',
type=str,
metavar='<share_network_subnet>',
help="Filter results by share network subnet that the share server's "
"network allocation exists whithin. Available for micro version "
">= 2.51 (Optional, Default=None).",
default=None)
def do_share_server_list(cs, args):
"""List all share servers (Admin only)."""
search_opts = {
@ -3555,6 +3692,15 @@ def do_share_server_list(cs, args):
"Updated_at",
]
if cs.api_version < api_versions.APIVersion("2.51"):
if getattr(args, 'share_network_subnet'):
raise exceptions.CommandError(
"Share network subnet option is only available with manila "
"API version >= 2.51")
else:
search_opts.update({'share_network_subnet': args.share_network_subnet})
fields.append("Share Network Subnet Id")
if args.columns is not None:
fields = _split_columns(columns=args.columns)
@ -5197,25 +5343,12 @@ def do_share_replica_list(cs, args):
action='single_alias',
metavar='<availability-zone>',
help='Optional Availability zone in which replica should be created.')
@cliutils.arg(
'--share-network',
'--share_network',
metavar='<network-info>',
default=None,
action='single_alias',
help='Optional network info ID or name.')
@api_versions.wraps("2.11")
def do_share_replica_create(cs, args):
"""Create a share replica (Experimental)."""
share = _find_share(cs, args.share)
share_network = None
if args.share_network:
share_network = _find_share_network(cs, args.share_network)
replica = cs.share_replicas.create(share,
args.availability_zone,
share_network)
replica = cs.share_replicas.create(share, args.availability_zone)
_print_share_replica(cs, replica)

View File

@ -0,0 +1,13 @@
---
features:
- Added CLI commands to get, add and delete share network subnets.
- Updated CLI command for managing share servers to accept
``share_network_subnet`` parameter.
- Deprecated ``neutron_subnet_id`` parameter from CLI command to update a
share network.
- Updated CLI command for listing share servers to show a new column
``Share Network Subnet Id``, and to accept a filter parameter
``share_network_subnet``.
fixes:
- Fixed share replica create API to make the replica inherit parent share's
share network.