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:
parent
cc401e5333
commit
b7d0d0d128
@ -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 = {}
|
||||
|
@ -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."),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
120
manilaclient/tests/functional/test_share_network_subnets.py
Normal file
120
manilaclient/tests/functional/test_share_network_subnets.py
Normal 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'])
|
@ -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,6 +121,7 @@ class ShareNetworksReadWriteTest(base.BaseTestCase):
|
||||
|
||||
self.assertEqual(self.name, get['name'])
|
||||
self.assertEqual(self.description, get['description'])
|
||||
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'])
|
||||
|
||||
@ -91,11 +146,21 @@ 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():
|
||||
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'])
|
||||
@ -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(
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
@ -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')
|
||||
|
@ -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': [
|
||||
|
82
manilaclient/tests/unit/v2/test_share_network_subnets.py
Normal file
82
manilaclient/tests/unit/v2/test_share_network_subnets.py
Normal 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
|
||||
})
|
@ -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):
|
||||
|
@ -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'
|
||||
|
@ -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',
|
||||
{'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'},
|
||||
{'--name': '""'},
|
||||
{'--description': '""'},
|
||||
{'--neutron_net_id': '""'},
|
||||
{'--neutron_subnet_id': '""'},
|
||||
{'--description': '""',
|
||||
'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': '""',
|
||||
},)
|
||||
def test_share_network_update(self, data):
|
||||
'--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)
|
||||
|
@ -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)
|
||||
|
92
manilaclient/v2/share_network_subnets.py
Normal file
92
manilaclient/v2/share_network_subnets.py
Normal 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)
|
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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})
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user