Add rw functional tests for share networks

Add base client methods for share networks and use them in rw functional tests.

Partially implements bp rw-functional-tests

Change-Id: I469f3101ead4ce47ea68370aa7f29137696f01ec
This commit is contained in:
Valeriy Ponomaryov 2015-04-28 17:47:21 +03:00
parent c6eace1d34
commit 17f76edfa4
4 changed files with 365 additions and 9 deletions

View File

@ -92,6 +92,9 @@ class BaseTestCase(base.ClientTestBase):
if res["type"] is "share_type": if res["type"] is "share_type":
client.delete_share_type(res_id) client.delete_share_type(res_id)
client.wait_for_share_type_deletion(res_id) client.wait_for_share_type_deletion(res_id)
elif res["type"] is "share_network":
client.delete_share_network(res_id)
client.wait_for_share_network_deletion(res_id)
else: else:
LOG.warn("Provided unsupported resource type for " LOG.warn("Provided unsupported resource type for "
"cleanup '%s'. Skipping." % res["type"]) "cleanup '%s'. Skipping." % res["type"])
@ -148,3 +151,27 @@ class BaseTestCase(base.ClientTestBase):
else: else:
self.method_resources.insert(0, resource) self.method_resources.insert(0, resource)
return share_type return share_type
@classmethod
def create_share_network(cls, name=None, description=None,
nova_net_id=None, neutron_net_id=None,
neutron_subnet_id=None, client=None,
cleanup_in_class=True):
if client is None:
client = cls.get_admin_client()
share_network = client.create_share_network(
name=name,
description=description,
nova_net_id=nova_net_id,
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id)
resource = {
"type": "share_network",
"id": share_network["id"],
"client": client,
}
if cleanup_in_class:
cls.class_resources.insert(0, resource)
else:
cls.method_resources.insert(0, resource)
return share_network

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import re
import time import time
import six import six
@ -25,6 +26,21 @@ from manilaclient.tests.functional import exceptions
from manilaclient.tests.functional import utils from manilaclient.tests.functional import utils
SHARE_TYPE = 'share_type' SHARE_TYPE = 'share_type'
SHARE_NETWORK = 'share_network'
def not_found_wrapper(f):
def wrapped_func(self, *args, **kwargs):
try:
return f(self, *args, **kwargs)
except tempest_lib_exc.CommandFailed as e:
if re.search('No (\w+) with a name or ID', e.stderr):
# Raise appropriate 'NotFound' error
raise tempest_lib_exc.NotFound()
raise
return wrapped_func
class ManilaCLIClient(base.CLIClient): class ManilaCLIClient(base.CLIClient):
@ -64,6 +80,8 @@ class ManilaCLIClient(base.CLIClient):
# TODO(vponomaryov): add support for other resource types # TODO(vponomaryov): add support for other resource types
if res_type == SHARE_TYPE: if res_type == SHARE_TYPE:
func = self.is_share_type_deleted func = self.is_share_type_deleted
elif res_type == SHARE_NETWORK:
func = self.is_share_network_deleted
else: else:
raise exceptions.InvalidResource(message=res_type) raise exceptions.InvalidResource(message=res_type)
@ -78,6 +96,8 @@ class ManilaCLIClient(base.CLIClient):
raise exceptions.ResourceReleaseFailed( raise exceptions.ResourceReleaseFailed(
res_type=res_type, res_id=res_id) res_type=res_type, res_id=res_id)
# Share types
def create_share_type(self, name=None, driver_handles_share_servers=True, def create_share_type(self, name=None, driver_handles_share_servers=True,
is_public=True): is_public=True):
"""Creates share type. """Creates share type.
@ -112,16 +132,10 @@ class ManilaCLIClient(base.CLIClient):
share_type = output_parser.listing(share_type_raw)[0] share_type = output_parser.listing(share_type_raw)[0]
return share_type return share_type
@not_found_wrapper
def delete_share_type(self, share_type): def delete_share_type(self, share_type):
"""Deletes share type by its Name or ID.""" """Deletes share type by its Name or ID."""
try: return self.manila('type-delete %s' % share_type)
return self.manila('type-delete %s' % share_type)
except tempest_lib_exc.CommandFailed as e:
not_found_msg = 'No sharetype with a name or ID'
if not_found_msg in e.stderr:
# Assuming it was deleted in tests
raise tempest_lib_exc.NotFound()
raise
def list_share_types(self, list_all=True): def list_share_types(self, list_all=True):
"""List share types. """List share types.
@ -161,20 +175,24 @@ class ManilaCLIClient(base.CLIClient):
'project show -f value -c id %s' % name_or_id) 'project show -f value -c id %s' % name_or_id)
return project_id.strip() return project_id.strip()
@not_found_wrapper
def add_share_type_access(self, share_type_name_or_id, project_id): def add_share_type_access(self, share_type_name_or_id, project_id):
data = dict(st=share_type_name_or_id, project=project_id) data = dict(st=share_type_name_or_id, project=project_id)
self.manila('type-access-add %(st)s %(project)s' % data) self.manila('type-access-add %(st)s %(project)s' % data)
@not_found_wrapper
def remove_share_type_access(self, share_type_name_or_id, project_id): def remove_share_type_access(self, share_type_name_or_id, project_id):
data = dict(st=share_type_name_or_id, project=project_id) data = dict(st=share_type_name_or_id, project=project_id)
self.manila('type-access-remove %(st)s %(project)s' % data) self.manila('type-access-remove %(st)s %(project)s' % data)
@not_found_wrapper
def list_share_type_access(self, share_type_id): def list_share_type_access(self, share_type_id):
projects_raw = self.manila('type-access-list %s' % share_type_id) projects_raw = self.manila('type-access-list %s' % share_type_id)
projects = output_parser.listing(projects_raw) projects = output_parser.listing(projects_raw)
project_ids = [pr['Project_ID'] for pr in projects] project_ids = [pr['Project_ID'] for pr in projects]
return project_ids return project_ids
@not_found_wrapper
def set_share_type_extra_specs(self, share_type_name_or_id, extra_specs): def set_share_type_extra_specs(self, share_type_name_or_id, extra_specs):
"""Set key-value pair for share type.""" """Set key-value pair for share type."""
if not (isinstance(extra_specs, dict) and extra_specs): if not (isinstance(extra_specs, dict) and extra_specs):
@ -185,6 +203,7 @@ class ManilaCLIClient(base.CLIClient):
cmd += '%(key)s=%(value)s ' % {'key': key, 'value': value} cmd += '%(key)s=%(value)s ' % {'key': key, 'value': value}
return self.manila(cmd) return self.manila(cmd)
@not_found_wrapper
def unset_share_type_extra_specs(self, share_type_name_or_id, def unset_share_type_extra_specs(self, share_type_name_or_id,
extra_specs_keys): extra_specs_keys):
"""Unset key-value pair for share type.""" """Unset key-value pair for share type."""
@ -209,3 +228,144 @@ class ManilaCLIClient(base.CLIClient):
if share_type_name_or_id in (share_type['ID'], share_type['Name']): if share_type_name_or_id in (share_type['ID'], share_type['Name']):
return share_type['all_extra_specs'] return share_type['all_extra_specs']
raise exceptions.ShareTypeNotFound(share_type=share_type_name_or_id) raise exceptions.ShareTypeNotFound(share_type=share_type_name_or_id)
# Share networks
def create_share_network(self, name=None, description=None,
nova_net_id=None, neutron_net_id=None,
neutron_subnet_id=None):
"""Creates share network.
:param name: text -- desired name of new share network
:param description: text -- desired description of new share network
:param nova_net_id: text -- ID of Nova network
:param neutron_net_id: text -- ID of Neutron network
:param neutron_subnet_id: text -- ID of Neutron subnet
NOTE: 'nova_net_id' and 'neutron_net_id'/'neutron_subnet_id' are
mutually exclusive.
"""
params = self._combine_share_network_data(
name=name,
description=description,
nova_net_id=nova_net_id,
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id)
share_network_raw = self.manila('share-network-create %s' % params)
share_network = output_parser.details(share_network_raw)
return share_network
def _combine_share_network_data(self, name=None, description=None,
nova_net_id=None, neutron_net_id=None,
neutron_subnet_id=None):
"""Combines params for share network operations 'create' and 'update'.
:returns: text -- set of CLI parameters
"""
data = dict()
if name is not None:
data['--name'] = name
if description is not None:
data['--description'] = description
if nova_net_id is not None:
data['--nova_net_id'] = nova_net_id
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
cmd = ''
for key, value in data.items():
cmd += "%(k)s='%(v)s' " % dict(k=key, v=value)
return cmd
@not_found_wrapper
def get_share_network(self, share_network):
"""Returns share network by its Name or ID."""
share_network_raw = self.manila(
'share-network-show %s' % share_network)
share_network = output_parser.details(share_network_raw)
return share_network
@not_found_wrapper
def update_share_network(self, share_network, name=None, description=None,
nova_net_id=None, neutron_net_id=None,
neutron_subnet_id=None):
"""Updates share-network by its name or ID.
:param name: text -- new name for share network
:param description: text -- new description for share network
:param nova_net_id: text -- ID of some Nova network
:param neutron_net_id: text -- ID of some Neutron network
:param neutron_subnet_id: text -- ID of some Neutron subnet
NOTE: 'nova_net_id' and 'neutron_net_id'/'neutron_subnet_id' are
mutually exclusive.
"""
sn_params = self._combine_share_network_data(
name=name,
description=description,
nova_net_id=nova_net_id,
neutron_net_id=neutron_net_id,
neutron_subnet_id=neutron_subnet_id)
share_network_raw = self.manila(
'share-network-update %(sn)s %(params)s' % dict(
sn=share_network, params=sn_params))
share_network = output_parser.details(share_network_raw)
return share_network
@not_found_wrapper
def delete_share_network(self, share_network):
"""Deletes share network by its Name or ID."""
return self.manila('share-network-delete %s' % share_network)
@staticmethod
def _stranslate_to_cli_optional_param(param):
if len(param) < 1 or not isinstance(param, six.string_types):
raise exceptions.InvalidData(
'Provided wrong parameter for translation.')
while not param[0:2] == '--':
param = '-' + param
return param.replace('_', '-')
def list_share_networks(self, all_tenants=False, filters=None):
"""List share networks.
:param all_tenants: bool -- whether to list share-networks that belong
only to current project or for all projects.
:param filters: dict -- filters for listing of share networks.
Example, input:
{'project_id': 'foo'}
{'-project_id': 'foo'}
{'--project_id': 'foo'}
{'project-id': 'foo'}
will be transformed to filter parameter "--project-id=foo"
"""
cmd = 'share-network-list '
if all_tenants:
cmd += '--all-tenants '
if filters and isinstance(filters, dict):
for k, v in filters.items():
cmd += '%(k)s=%(v)s ' % {
'k': self._stranslate_to_cli_optional_param(k), 'v': v}
share_networks_raw = self.manila(cmd)
share_networks = utils.listing(share_networks_raw)
return share_networks
def is_share_network_deleted(self, share_network):
"""Says whether share network is deleted or not.
:param share_network: text -- Name or ID of share network
"""
share_types = self.list_share_networks(True)
for list_element in share_types:
if share_network in (list_element['id'], list_element['name']):
return False
return True
def wait_for_share_network_deletion(self, share_network):
"""Wait for share network deletion by its Name or ID.
:param share_network: text -- Name or ID of share network
"""
self.wait_for_resource_deletion(
SHARE_NETWORK, res_id=share_network, interval=2, timeout=6)

View File

@ -32,5 +32,5 @@ class InvalidData(exceptions.TempestException):
message = "Provided invalid data: %(message)s" message = "Provided invalid data: %(message)s"
class ShareTypeNotFound(exceptions.TempestException): class ShareTypeNotFound(exceptions.NotFound):
message = "Share type '%(share_type)s' was not found" message = "Share type '%(share_type)s' was not found"

View File

@ -0,0 +1,169 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
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
@ddt.ddt
class ShareNetworksReadWriteTest(base.BaseTestCase):
@classmethod
def setUpClass(cls):
super(ShareNetworksReadWriteTest, 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,
)
@ddt.data(
{'name': data_utils.rand_name('autotest_share_network_name')},
{'description': 'fake_description'},
{'nova_net_id': 'fake_nova_net_id'},
{'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id'},
)
def test_create_delete_share_network(self, net_data):
sn = self.create_share_network(cleanup_in_class=False, **net_data)
expected_data = {
'name': 'None',
'description': 'None',
'nova_net_id': 'None',
'neutron_net_id': 'None',
'neutron_subnet_id': 'None',
}
expected_data.update(net_data)
for k, v in expected_data.items():
self.assertEqual(v, sn[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
def test_get_share_network_with_neutron_data(self):
get = self.admin_client.get_share_network(self.sn['id'])
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'])
# We did not set Nova data, so, we expect these fields to be set
# to None.
self.assertEqual('None', get['nova_net_id'])
def test_get_share_network_with_nova_data(self):
name = data_utils.rand_name('autotest')
description = 'fake_description'
nova_net_id = 'fake_nova_net_id'
create = self.create_share_network(
name=name,
description=description,
nova_net_id=nova_net_id,
cleanup_in_class=False)
self.assertEqual(name, create['name'])
self.assertEqual(description, create['description'])
self.assertEqual(nova_net_id, create['nova_net_id'])
# We did not set Neutron data, so, we expect these fields to be set
# to None.
self.assertEqual('None', create['neutron_net_id'])
self.assertEqual('None', create['neutron_subnet_id'])
@ddt.data(
{'name': data_utils.rand_name('autotest_share_network_name')},
{'description': 'fake_description'},
{'nova_net_id': 'fake_nova_net_id'},
{'neutron_net_id': 'fake_neutron_net_id',
'neutron_subnet_id': 'fake_neutron_subnet_id'},
)
def test_create_update_share_network(self, net_data):
sn = self.create_share_network(cleanup_in_class=False)
update = self.admin_client.update_share_network(sn['id'], **net_data)
expected_data = {
'name': 'None',
'description': 'None',
'nova_net_id': 'None',
'neutron_net_id': 'None',
'neutron_subnet_id': 'None',
}
expected_data.update(net_data)
for k, v in expected_data.items():
self.assertEqual(v, update[k])
self.admin_client.delete_share_network(sn['id'])
self.admin_client.wait_for_share_network_deletion(sn['id'])
@ddt.data(True, False)
def test_list_share_networks(self, all_tenants):
share_networks = self.admin_client.list_share_networks(all_tenants)
self.assertTrue(
any(self.sn['id'] == sn['id'] for sn in share_networks))
for sn in share_networks:
self.assertEqual(2, len(sn))
self.assertIn('id', sn)
self.assertIn('name', sn)
def _list_share_networks_with_filters(self, filters):
share_networks = self.admin_client.list_share_networks(filters=filters)
self.assertTrue(len(share_networks) > 0)
self.assertTrue(
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'])
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])
def test_list_share_networks_filter_by_project_id(self):
project_id = self.admin_client.get_project_id(
self.admin_client.tenant_name)
filters = {'project_id': project_id}
self._list_share_networks_with_filters(filters)
def test_list_share_networks_filter_by_name(self):
filters = {'name': self.name}
self._list_share_networks_with_filters(filters)
def test_list_share_networks_filter_by_neutron_net_id(self):
filters = {'neutron_net_id': self.neutron_net_id}
self._list_share_networks_with_filters(filters)
def test_list_share_networks_filter_by_neutron_subnet_id(self):
filters = {'neutron_subnet_id': self.neutron_subnet_id}
self._list_share_networks_with_filters(filters)