Support Manila pools in NetApp Clustered Data ONTAP driver

The Cinder pools feature is being ported to Manila, and drivers must be
updated to report pools in order to use this feature.  This patch adds
support for pools to the NetApp Clustered Data ONTAP driver for Manila.

Implements blueprint support-pools-in-netapp-cdot-driver

Change-Id: Iba327a745227e6219e6e1fc77af407f097905d8f
This commit is contained in:
Clinton Knight 2015-01-26 16:30:31 -05:00
parent d361c666b5
commit dc2aa75e7c
12 changed files with 374 additions and 124 deletions

View File

@ -149,6 +149,10 @@ class InvalidContentType(Invalid):
message = _("Invalid content type %(content_type)s.")
class InvalidHost(Invalid):
message = _("Invalid host: %(reason)s")
# Cannot be templated as the error syntax varies.
# msg needs to be constructed when raised.
class InvalidParameterValue(Invalid):

View File

@ -127,8 +127,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
try:
root_volume_name = vserver_info.get_child_by_name(
'attributes-list').get_child_by_name('vserver-info')\
.get_child_content('root-volume')
'attributes-list').get_child_by_name(
'vserver-info').get_child_content('root-volume')
except AttributeError:
msg = _('Could not determine root volume name '
'for Vserver %s.') % vserver_name
@ -264,9 +264,9 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
}
port_info = self.send_request('net-port-get-iter', api_args)
try:
port = port_info.get_child_by_name('attributes-list')\
.get_child_by_name('net-port-info')\
.get_child_content('port')
port = port_info.get_child_by_name(
'attributes-list').get_child_by_name(
'net-port-info').get_child_content('port')
except AttributeError:
msg = _("Data port does not exist for node %s.")
raise exception.NetAppException(msg % node)
@ -414,35 +414,48 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
self.send_request('net-interface-delete', api_args)
@na_utils.trace
def calculate_aggregate_capacity(self, aggregate_names):
"""Calculates capacity of one or more aggregates
def get_cluster_aggregate_capacities(self, aggregate_names):
"""Calculates capacity of one or more aggregates.
Returns tuple (total, free) in bytes.
Returns dictionary of aggregate capacity metrics.
'size-used' is the actual space consumed on the aggregate.
'size-available' is the actual space remaining.
'size-total' is the defined total aggregate size, such that
used + available = total.
"""
desired_attributes = {
'aggr-attributes': {
'aggregate-name': None,
'aggr-space-attributes': {
'size-total': None,
'size-available': None,
'size-total': None,
'size-used': None,
},
},
}
aggrs = self._get_aggregates(aggregate_names=aggregate_names,
desired_attributes=desired_attributes)
aggr_space_attrs = [aggr.get_child_by_name('aggr-space-attributes')
for aggr in aggrs]
total = sum([int(aggr.get_child_content('size-total'))
for aggr in aggr_space_attrs]) if aggr_space_attrs else 0
free = max([int(aggr.get_child_content('size-available'))
for aggr in aggr_space_attrs]) if aggr_space_attrs else 0
return total, free
aggr_space_dict = dict()
for aggr in aggrs:
aggr_name = aggr.get_child_content('aggregate-name')
aggr_space_attrs = aggr.get_child_by_name('aggr-space-attributes')
aggr_space_dict[aggr_name] = {
'available':
int(aggr_space_attrs.get_child_content('size-available')),
'total':
int(aggr_space_attrs.get_child_content('size-total')),
'used':
int(aggr_space_attrs.get_child_content('size-used')),
}
return aggr_space_dict
@na_utils.trace
def get_aggregates_for_vserver(self, vserver_name):
"""Returns aggregate list and size info for a Vserver.
def get_vserver_aggregate_capacities(self, vserver_name):
"""Calculates capacity of one or more aggregates for a vserver.
Must be called against a Vserver API client.
Returns dictionary of aggregate capacity metrics. This must
be called against a Vserver LIF.
"""
LOG.debug('Finding available aggregates for Vserver %s', vserver_name)
@ -466,19 +479,19 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
vserver_aggr_info_list = vserver_aggr_info_element.get_children()
if not vserver_aggr_info_list:
msg = _("No aggregates assigned to Vserver %s")
raise exception.NetAppException(msg % vserver_name)
LOG.warning(_LW('No aggregates assigned to Vserver %s.'),
vserver_name)
# Return dict of key-value pair of aggr_name:aggr_size_available.
aggr_dict = {}
aggr_space_dict = {}
for aggr_info in vserver_aggr_info_list:
aggr_name = aggr_info.get_child_content('aggr-name')
aggr_size = int(aggr_info.get_child_content('aggr-availsize'))
aggr_dict[aggr_name] = aggr_size
aggr_space_dict[aggr_name] = {'available': aggr_size}
LOG.debug('Found available aggregates: %s', aggr_dict)
return aggr_dict
LOG.debug('Found available Vserver aggregates: %s', aggr_space_dict)
return aggr_space_dict
def _get_aggregates(self, aggregate_names=None, desired_attributes=None):
@ -701,6 +714,45 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
result = self.send_request('volume-get-iter', api_args)
return self._has_records(result)
@na_utils.trace
def get_aggregate_for_volume(self, volume_name):
"""Get the name of the aggregate containing a volume."""
api_args = {
'query': {
'volume-attributes': {
'volume-id-attributes': {
'name': volume_name,
},
},
},
'desired-attributes': {
'volume-attributes': {
'volume-id-attributes': {
'containing-aggregate-name': None,
'name': None,
},
},
},
}
result = self.send_request('volume-get-iter', api_args)
attributes_list = result.get_child_by_name(
'attributes-list') or netapp_api.NaElement('none')
volume_attributes = attributes_list.get_child_by_name(
'volume-attributes') or netapp_api.NaElement('none')
volume_id_attributes = volume_attributes.get_child_by_name(
'volume-id-attributes') or netapp_api.NaElement('none')
aggregate = volume_id_attributes.get_child_content(
'containing-aggregate-name')
if not aggregate:
msg = _('Could not find aggregate for volume %s.')
raise exception.NetAppException(msg % volume_name)
return aggregate
@na_utils.trace
def create_volume_clone(self, volume_name, parent_volume_name,
parent_snapshot_name=None):

View File

@ -40,6 +40,9 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
def check_for_setup_error(self):
self.library.check_for_setup_error()
def get_pool(self, share):
return self.library.get_pool(share)
def create_share(self, context, share, **kwargs):
return self.library.create_share(context, share, **kwargs)

View File

@ -35,6 +35,7 @@ from manila.share.drivers.netapp.dataontap.protocols import cifs_cmode
from manila.share.drivers.netapp.dataontap.protocols import nfs_cmode
from manila.share.drivers.netapp import options as na_opts
from manila.share.drivers.netapp import utils as na_utils
from manila.share import utils as share_utils
from manila import utils
@ -73,7 +74,7 @@ class NetAppCmodeFileStorageLibrary(object):
AUTOSUPPORT_INTERVAL_SECONDS = 3600 # hourly
def __init__(self, db, driver_name, **kwargs):
na_utils.validate_instantiation(**kwargs)
na_utils.validate_driver_instantiation(**kwargs)
self.db = db
self.driver_name = driver_name
@ -157,17 +158,39 @@ class NetAppCmodeFileStorageLibrary(object):
@na_utils.trace
def get_share_stats(self):
"""Retrieve stats info from Cluster Mode backend."""
total, free = self._client.calculate_aggregate_capacity(
aggr_space = self._client.get_cluster_aggregate_capacities(
self._find_matching_aggregates())
data = dict(
share_backend_name=self.backend_name,
driver_name=self.driver_name,
vendor_name='NetApp',
driver_version='1.0',
storage_protocol='NFS_CIFS',
total_capacity_gb=(total / units.Gi),
free_capacity_gb=(free / units.Gi))
data = {
'share_backend_name': self.backend_name,
'driver_name': self.driver_name,
'vendor_name': 'NetApp',
'driver_version': '1.0',
'storage_protocol': 'NFS_CIFS',
'total_capacity_gb': 0.0,
'free_capacity_gb': 0.0,
}
pools = []
for aggr_name in sorted(aggr_space.keys()):
total_capacity_gb = na_utils.round_down(
float(aggr_space[aggr_name]['total']) / units.Gi, '0.01')
free_capacity_gb = na_utils.round_down(
float(aggr_space[aggr_name]['available']) / units.Gi, '0.01')
allocated_capacity_gb = na_utils.round_down(
float(aggr_space[aggr_name]['used']) / units.Gi, '0.01')
pools.append({
'pool_name': aggr_name,
'total_capacity_gb': total_capacity_gb,
'free_capacity_gb': free_capacity_gb,
'allocated_capacity_gb': allocated_capacity_gb,
'QoS_support': 'False',
'reserved_percentage': 0,
})
data['pools'] = pools
self._handle_ems_logging()
@ -317,6 +340,15 @@ class NetAppCmodeFileStorageLibrary(object):
ip, netmask, vlan, node, port, vserver_name, allocation_id,
self.configuration.netapp_lif_name_template)
@na_utils.trace
def get_pool(self, share):
pool = share_utils.extract_host(share['host'], level='pool')
if pool:
return pool
volume_name = self._get_valid_share_name(share['id'])
return self._client.get_aggregate_for_volume(volume_name)
@ensure_vserver
@na_utils.trace
def create_share(self, context, share, share_server):
@ -340,12 +372,17 @@ class NetAppCmodeFileStorageLibrary(object):
def _allocate_container(self, share, vserver, vserver_client):
"""Create new share on aggregate."""
share_name = self._get_valid_share_name(share['id'])
aggregates = vserver_client.get_aggregates_for_vserver(vserver)
aggregate = max(aggregates, key=lambda m: aggregates[m])
# Get Data ONTAP aggregate name as pool name.
aggregate_name = share_utils.extract_host(share['host'], level='pool')
if aggregate_name is None:
msg = _("Pool is not available in the share host field.")
raise exception.InvalidHost(reason=msg)
LOG.debug('Creating volume %(share_name)s on aggregate %(aggregate)s',
{'share_name': share_name, 'aggregate': aggregate})
vserver_client.create_volume(aggregate, share_name, share['size'])
{'share_name': share_name, 'aggregate': aggregate_name})
vserver_client.create_volume(aggregate_name, share_name, share['size'])
@na_utils.trace
def _allocate_container_from_snapshot(self, share, snapshot,

View File

@ -15,10 +15,12 @@
# under the License.
"""Utilities for NetApp drivers."""
import decimal
import platform
from oslo_concurrency import processutils as putils
from oslo_log import log
import six
from manila import exception
from manila.i18n import _, _LI, _LW
@ -32,6 +34,36 @@ TRACE_METHOD = False
TRACE_API = False
def check_flags(required_flags, configuration):
"""Ensure that the flags we care about are set."""
for flag in required_flags:
if not getattr(configuration, flag, None):
msg = _('Configuration value %s is not set.') % flag
raise exception.InvalidInput(reason=msg)
def validate_driver_instantiation(**kwargs):
"""Checks if a driver is instantiated other than by the unified driver.
Helps check direct instantiation of netapp drivers.
Call this function in every netapp block driver constructor.
"""
if kwargs and kwargs.get('netapp_mode') == 'proxy':
return
LOG.warning(_LW('Please use NetAppDriver in the configuration file '
'to load the driver instead of directly specifying '
'the driver module name.'))
def round_down(value, precision):
"""Round a number downward using a specified level of precision.
Example: round_down(float(total_space_in_bytes) / units.Gi, '0.01')
"""
return float(decimal.Decimal(six.text_type(value)).quantize(
decimal.Decimal(precision), rounding=decimal.ROUND_DOWN))
def setup_tracing(trace_flags_string):
global TRACE_METHOD
global TRACE_API
@ -57,27 +89,6 @@ def trace(f):
return trace_wrapper
def check_flags(required_flags, configuration):
"""Ensure that the flags we care about are set."""
for flag in required_flags:
if not getattr(configuration, flag, None):
msg = _('Configuration value %s is not set.') % flag
raise exception.InvalidInput(reason=msg)
def validate_instantiation(**kwargs):
"""Checks if a driver is instantiated other than by the unified driver.
Helps check direct instantiation of netapp drivers.
Call this function in every netapp block driver constructor.
"""
if kwargs and kwargs.get('netapp_mode') == 'proxy':
return
LOG.warning(_LW('Please use NetAppDriver in the configuration file '
'to load the driver instead of directly specifying '
'the driver module name.'))
class OpenStackInfo(object):
"""OS/distribution, release, and version.

View File

@ -123,7 +123,14 @@ VSERVER_DATA_LIST_RESPONSE = etree.XML("""
</results>
""" % {'vserver': VSERVER_NAME})
VSERVER_AGGREGATES = {'aggr0': 45678592, 'manila': 6448431104}
VSERVER_AGGREGATES = {
'aggr0': {
'available': 45678592,
},
'manila': {
'available': 6448431104,
},
}
VSERVER_GET_RESPONSE_NO_AGGREGATES = etree.XML("""
<results status="passed">
@ -577,8 +584,9 @@ AGGR_GET_SPACE_RESPONSE = etree.XML("""
</plexes>
</aggr-raid-attributes>
<aggr-space-attributes>
<size-available>45678592</size-available>
<size-available>45670400</size-available>
<size-total>943718400</size-total>
<size-used>898048000</size-used>
</aggr-space-attributes>
<aggregate-name>aggr0</aggregate-name>
</aggr-attributes>
@ -599,8 +607,9 @@ AGGR_GET_SPACE_RESPONSE = etree.XML("""
</plexes>
</aggr-raid-attributes>
<aggr-space-attributes>
<size-available>6448435200</size-available>
<size-available>4267659264</size-available>
<size-total>7549747200</size-total>
<size-used>3282087936</size-used>
</aggr-space-attributes>
<aggregate-name>manila</aggregate-name>
</aggr-attributes>
@ -965,3 +974,21 @@ NFS_EXPORTFS_LIST_RULES_2_RESPONSE = etree.XML("""
'host1': NFS_EXPORT_RULES[0],
'host2': NFS_EXPORT_RULES[1],
})
GET_AGGREGATE_FOR_VOLUME_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<volume-attributes>
<volume-id-attributes>
<containing-aggregate-name>%(aggr)s</containing-aggregate-name>
<name>%(share)s</name>
<owning-vserver-name>os_aa666789-5576-4835-87b7-868069856459</owning-vserver-name>
</volume-id-attributes>
</volume-attributes>
</attributes-list>
<num-records>1</num-records>
</results>
""" % {
'aggr': SHARE_AGGREGATE_NAME,
'share': SHARE_NAME
})

View File

@ -81,11 +81,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
'desired-attributes': {'vserver-info': {'vserver-name': None}}
}
response = self.client.vserver_exists(fake.VSERVER_NAME)
result = self.client.vserver_exists(fake.VSERVER_NAME)
self.client.send_request.assert_has_calls([
mock.call('vserver-get-iter', vserver_get_args)])
self.assertTrue(response)
self.assertTrue(result)
def test_vserver_exists_not_found(self):
@ -137,11 +137,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
'desired-attributes': {'vserver-info': {'root-volume': None}}
}
response = self.client.get_vserver_root_volume_name(fake.VSERVER_NAME)
result = self.client.get_vserver_root_volume_name(fake.VSERVER_NAME)
self.client.send_request.assert_has_calls([
mock.call('vserver-get-iter', vserver_get_args)])
self.assertEqual(fake.ROOT_VOLUME_NAME, response)
self.assertEqual(fake.ROOT_VOLUME_NAME, result)
def test_get_vserver_root_volume_name_not_found(self):
@ -587,13 +587,13 @@ class NetAppClientCmodeTestCase(test.TestCase):
}
}
}
response = self.client.network_interface_exists(
result = self.client.network_interface_exists(
fake.VSERVER_NAME, fake.NODE_NAME, fake.PORT, fake.IP_ADDRESS,
fake.NETMASK, fake.VLAN)
self.client.send_request.assert_has_calls([
mock.call('net-interface-get-iter', net_interface_get_args)])
self.assertTrue(response)
self.assertTrue(result)
def test_network_interface_exists_not_found(self):
@ -602,11 +602,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
'send_request',
mock.Mock(return_value=api_response))
response = self.client.network_interface_exists(
result = self.client.network_interface_exists(
fake.VSERVER_NAME, fake.NODE_NAME, fake.PORT, fake.IP_ADDRESS,
fake.NETMASK, fake.VLAN)
self.assertFalse(response)
self.assertFalse(result)
def test_list_network_interfaces(self):
@ -669,7 +669,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('net-interface-delete', net_interface_get_args)])
def test_calculate_aggregate_capacity(self):
def test_get_cluster_aggregate_capacities(self):
api_response = netapp_api.NaElement(
fake.AGGR_GET_SPACE_RESPONSE).get_child_by_name(
@ -678,14 +678,15 @@ class NetAppClientCmodeTestCase(test.TestCase):
'_get_aggregates',
mock.Mock(return_value=api_response))
result = self.client.calculate_aggregate_capacity(fake.AGGR_NAMES)
result = self.client.get_cluster_aggregate_capacities(fake.AGGR_NAMES)
desired_attributes = {
'aggr-attributes': {
'aggregate-name': None,
'aggr-space-attributes': {
'size-total': None,
'size-available': None,
'size-total': None,
'size-used': None,
}
}
}
@ -694,27 +695,40 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.call(
aggregate_names=fake.AGGR_NAMES,
desired_attributes=desired_attributes)])
self.assertEqual((8493465600, 6448435200), result)
def test_calculate_aggregate_capacity_not_found(self):
expected = {
'manila': {
'available': 4267659264,
'total': 7549747200,
'used': 3282087936,
},
'aggr0': {
'available': 45670400,
'total': 943718400,
'used': 898048000,
}
}
self.assertDictEqual(expected, result)
def test_get_cluster_aggregate_capacities_not_found(self):
api_response = netapp_api.NaElement('none').get_children()
self.mock_object(self.client,
'_get_aggregates',
mock.Mock(return_value=api_response))
result = self.client.calculate_aggregate_capacity(fake.AGGR_NAMES)
result = self.client.get_cluster_aggregate_capacities(fake.AGGR_NAMES)
self.assertEqual((0, 0), result)
self.assertEqual({}, result)
def test_get_aggregates_for_vserver(self):
def test_get_vserver_aggregate_capacities(self):
api_response = netapp_api.NaElement(fake.VSERVER_GET_RESPONSE)
self.mock_object(self.vserver_client,
'send_request',
mock.Mock(return_value=api_response))
result = self.vserver_client.get_aggregates_for_vserver(
result = self.vserver_client.get_vserver_aggregate_capacities(
fake.VSERVER_NAME)
vserver_args = {
@ -734,7 +748,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.call('vserver-get', vserver_args)])
self.assertDictEqual(fake.VSERVER_AGGREGATES, result)
def test_get_aggregates_for_vserver_not_found(self):
def test_get_vserver_aggregate_capacities_not_found(self):
api_response = netapp_api.NaElement(
fake.VSERVER_GET_RESPONSE_NO_AGGREGATES)
@ -742,9 +756,11 @@ class NetAppClientCmodeTestCase(test.TestCase):
'send_request',
mock.Mock(return_value=api_response))
self.assertRaises(exception.NetAppException,
self.vserver_client.get_aggregates_for_vserver,
fake.VSERVER_NAME)
result = self.vserver_client.get_vserver_aggregate_capacities(
fake.VSERVER_NAME)
self.assertDictEqual({}, result)
self.assertEqual(1, client_cmode.LOG.warning.call_count)
def test_get_aggregates(self):
@ -1181,7 +1197,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
'send_request',
mock.Mock(return_value=api_response))
response = self.client.volume_exists(fake.SHARE_NAME)
result = self.client.volume_exists(fake.SHARE_NAME)
volume_get_iter_args = {
'query': {
@ -1202,7 +1218,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('volume-get-iter', volume_get_iter_args)])
self.assertTrue(response)
self.assertTrue(result)
def test_volume_exists_not_found(self):
@ -1213,6 +1229,49 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.assertFalse(self.client.volume_exists(fake.SHARE_NAME))
def test_get_aggregate_for_volume(self):
api_response = netapp_api.NaElement(
fake.GET_AGGREGATE_FOR_VOLUME_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.get_aggregate_for_volume(fake.SHARE_NAME)
volume_get_iter_args = {
'query': {
'volume-attributes': {
'volume-id-attributes': {
'name': fake.SHARE_NAME
}
}
},
'desired-attributes': {
'volume-attributes': {
'volume-id-attributes': {
'containing-aggregate-name': None,
'name': None
}
}
}
}
self.client.send_request.assert_has_calls([
mock.call('volume-get-iter', volume_get_iter_args)])
self.assertEqual(fake.SHARE_AGGREGATE_NAME, result)
def test_get_aggregate_for_volume_not_found(self):
api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
self.assertRaises(exception.NetAppException,
self.client.get_aggregate_for_volume,
fake.SHARE_NAME)
def test_create_volume_clone(self):
self.mock_object(self.client, 'send_request')

View File

@ -21,7 +21,6 @@ import socket
import mock
from oslo_utils import timeutils
from oslo_utils import units
from manila import context
from manila import exception
@ -99,7 +98,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def setUp(self):
super(NetAppFileStorageLibraryTestCase, self).setUp()
self.mock_object(na_utils, 'validate_instantiation')
self.mock_object(na_utils, 'validate_driver_instantiation')
self.mock_object(na_utils, 'setup_tracing')
self.mock_object(lib_base, 'LOG')
@ -126,8 +125,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
config.netapp_server_port = fake.CLIENT_KWARGS['port']
config.netapp_vserver = fake.VSERVER1
config.netapp_volume_name_template = fake.VOLUME_NAME_TEMPLATE
config.netapp_aggregate_name_search_pattern = \
fake.AGGREGATE_NAME_SEARCH_PATTERN
config.netapp_aggregate_name_search_pattern = (
fake.AGGREGATE_NAME_SEARCH_PATTERN)
config.netapp_vserver_name_template = fake.VSERVER_NAME_TEMPLATE
config.netapp_root_volume_aggregate = fake.ROOT_VOLUME_AGGREGATE
config.netapp_root_volume = fake.ROOT_VOLUME
@ -137,7 +136,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_init(self):
self.assertEqual(fake.DRIVER_NAME, self.library.driver_name)
self.assertEqual(self.mock_db, self.library.db)
self.assertEqual(1, na_utils.validate_instantiation.call_count)
self.assertEqual(1, na_utils.validate_driver_instantiation.call_count)
self.assertEqual(1, na_utils.setup_tracing.call_count)
self.assertIsNone(self.library._helpers)
self.assertListEqual([], self.library._licenses)
@ -263,9 +262,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(self.library, '_find_matching_aggregates')
self.mock_object(self.client,
'calculate_aggregate_capacity',
mock.Mock(return_value=(fake.TOTAL_CAPACITY,
fake.FREE_CAPACITY)))
'get_cluster_aggregate_capacities',
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES))
mock_handle_ems_logging = self.mock_object(self.library,
'_handle_ems_logging')
@ -277,10 +275,27 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'vendor_name': 'NetApp',
'driver_version': '1.0',
'storage_protocol': 'NFS_CIFS',
'total_capacity_gb': fake.TOTAL_CAPACITY / units.Gi,
'free_capacity_gb': fake.FREE_CAPACITY / units.Gi
'total_capacity_gb': 0.0,
'free_capacity_gb': 0.0,
'pools': [
{'pool_name': 'manila1',
'total_capacity_gb': 3.3,
'free_capacity_gb': 1.1,
'allocated_capacity_gb': 2.2,
'QoS_support': 'False',
'reserved_percentage': 0,
},
{'pool_name': 'manila2',
'total_capacity_gb': 6.0,
'free_capacity_gb': 2.0,
'allocated_capacity_gb': 4.0,
'QoS_support': 'False',
'reserved_percentage': 0,
},
]
}
self.maxDiff = None
self.assertDictEqual(expected, result)
self.assertTrue(mock_handle_ems_logging.called)
@ -339,13 +354,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'list_aggregates',
mock.Mock(return_value=fake.AGGREGATES))
self.library.configuration.netapp_aggregate_name_search_pattern =\
fake.AGGREGATE_NAME_SEARCH_PATTERN
self.library.configuration.netapp_aggregate_name_search_pattern = (
fake.AGGREGATE_NAME_SEARCH_PATTERN)
result = self.library._find_matching_aggregates()
self.assertListEqual(result, fake.AGGREGATES)
self.library.configuration.netapp_aggregate_name_search_pattern =\
'aggr.*'
self.library.configuration.netapp_aggregate_name_search_pattern = (
'aggr.*')
result = self.library._find_matching_aggregates()
self.assertListEqual(result, ['aggr0'])
@ -618,6 +633,35 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertFalse(self.library._client.create_network_interface.called)
def test_get_pool_has_pool(self):
result = self.library.get_pool(fake.SHARE)
self.assertEqual(fake.POOL_NAME, result)
self.assertFalse(self.client.get_aggregate_for_volume.called)
def test_get_pool_no_pool(self):
fake_share = copy.deepcopy(fake.SHARE)
fake_share['host'] = '%(host)s@%(backend)s' % {
'host': fake.HOST_NAME, 'backend': fake.BACKEND_NAME}
self.client.get_aggregate_for_volume.return_value = fake.POOL_NAME
result = self.library.get_pool(fake_share)
self.assertEqual(fake.POOL_NAME, result)
self.assertTrue(self.client.get_aggregate_for_volume.called)
def test_get_pool_raises(self):
fake_share = copy.deepcopy(fake.SHARE)
fake_share['host'] = '%(host)s@%(backend)s' % {
'host': fake.HOST_NAME, 'backend': fake.BACKEND_NAME}
self.client.get_aggregate_for_volume.side_effect = (
exception.NetAppException)
self.assertRaises(exception.NetAppException,
self.library.get_pool,
fake_share)
def test_create_share(self):
vserver_client = mock.Mock()
@ -674,16 +718,14 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_allocate_container(self):
aggregates = {'aggr0': 10000000000, 'aggr1': 20000000000}
vserver_client = mock.Mock()
vserver_client.get_aggregates_for_vserver.return_value = aggregates
self.library._allocate_container(fake.SHARE,
fake.VSERVER1,
vserver_client)
share_name = self.library._get_valid_share_name(fake.SHARE['id'])
vserver_client.create_volume.assert_called_with('aggr1',
vserver_client.create_volume.assert_called_with(fake.POOL_NAME,
share_name,
fake.SHARE['size'])
@ -938,8 +980,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_get_network_allocations_number(self):
self.library._client.list_cluster_nodes.return_value = \
fake.CLUSTER_NODES
self.library._client.list_cluster_nodes.return_value = (
fake.CLUSTER_NODES)
result = self.library.get_network_allocations_number()

View File

@ -12,11 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
BACKEND_NAME = 'fake_backend_name'
DRIVER_NAME = 'fake_driver_name'
APP_VERSION = 'fake_app_vsersion'
HOST_NAME = 'fake_host'
POOL_NAME = 'fake_pool'
VSERVER1 = 'fake_vserver_1'
VSERVER2 = 'fake_vserver_2'
LICENSES = ['base', 'cifs', 'fcp', 'flexclone', 'iscsi', 'nfs', 'snapmirror',
@ -51,6 +51,8 @@ CLIENT_KWARGS = {
SHARE = {
'id': SHARE_ID,
'host': '%(host)s@%(backend)s#%(pool)s' % {
'host': HOST_NAME, 'backend': BACKEND_NAME, 'pool': POOL_NAME},
'project_id': TENANT_ID,
'name': SHARE_NAME,
'size': SHARE_SIZE,
@ -124,3 +126,16 @@ EMS_MESSAGE = {
'log-level': '6',
'auto-support': 'false'
}
AGGREGATE_CAPACITIES = {
'manila1': {
'available': 1181116007, # 1.1 GB
'total': 3543348020, # 3.3 GB
'used': 2362232013, # 2.2 GB
},
'manila2': {
'available': 2147483648, # 2.0 GB
'total': 6442450944, # 6.0 GB
'used': 4294967296, # 4.0 GB
}
}

View File

@ -65,8 +65,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
def test_allow_access_preexisting(self):
self.mock_client.add_cifs_share_access.side_effect = \
netapp_api.NaApiError(code=netapp_api.EDUPLICATEENTRY)
self.mock_client.add_cifs_share_access.side_effect = (
netapp_api.NaApiError(code=netapp_api.EDUPLICATEENTRY))
self.assertRaises(exception.ShareAccessExists,
self.helper.allow_access,
@ -76,8 +76,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
def test_allow_access_api_error(self):
self.mock_client.add_cifs_share_access.side_effect = \
netapp_api.NaApiError()
self.mock_client.add_cifs_share_access.side_effect = (
netapp_api.NaApiError())
self.assertRaises(netapp_api.NaApiError,
self.helper.allow_access,
@ -105,8 +105,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
def test_deny_access_nonexistent_user(self):
self.mock_client.remove_cifs_share_access.side_effect = \
netapp_api.NaApiError(code=netapp_api.EONTAPI_EINVAL)
self.mock_client.remove_cifs_share_access.side_effect = (
netapp_api.NaApiError(code=netapp_api.EONTAPI_EINVAL))
self.helper.deny_access(self.mock_context, fake.CIFS_SHARE,
fake.ACCESS)
@ -117,8 +117,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
def test_deny_access_nonexistent_rule(self):
self.mock_client.remove_cifs_share_access.side_effect = \
netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND)
self.mock_client.remove_cifs_share_access.side_effect = (
netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND))
self.helper.deny_access(self.mock_context, fake.CIFS_SHARE,
fake.ACCESS)
@ -129,8 +129,8 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
def test_deny_access_api_error(self):
self.mock_client.remove_cifs_share_access.side_effect = \
netapp_api.NaApiError()
self.mock_client.remove_cifs_share_access.side_effect = (
netapp_api.NaApiError())
self.assertRaises(netapp_api.NaApiError,
self.helper.deny_access,

View File

@ -40,8 +40,8 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
def test_create_share(self):
self.mock_client.get_volume_junction_path.return_value = \
fake.NFS_SHARE_PATH
self.mock_client.get_volume_junction_path.return_value = (
fake.NFS_SHARE_PATH)
result = self.helper.create_share(fake.SHARE_NAME, fake.SHARE_ADDRESS)
@ -145,8 +145,8 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
def test_get_existing_rules(self):
self.mock_client.get_nfs_export_rules.return_value = \
fake.NFS_ACCESS_HOSTS
self.mock_client.get_nfs_export_rules.return_value = (
fake.NFS_ACCESS_HOSTS)
result = self.helper._get_existing_rules(fake.NFS_SHARE)

View File

@ -84,18 +84,18 @@ class NetAppDriverUtilsTestCase(test.TestCase):
self.assertEqual('OK', result)
self.assertEqual(2, na_utils.LOG.debug.call_count)
def test_validate_instantiation_proxy(self):
def test_validate_driver_instantiation_proxy(self):
kwargs = {'netapp_mode': 'proxy'}
na_utils.validate_instantiation(**kwargs)
na_utils.validate_driver_instantiation(**kwargs)
self.assertEqual(0, na_utils.LOG.warning.call_count)
def test_validate_instantiation_no_proxy(self):
def test_validate_driver_instantiation_no_proxy(self):
self.mock_object(na_utils, 'LOG')
kwargs = {'netapp_mode': 'asdf'}
na_utils.validate_instantiation(**kwargs)
na_utils.validate_driver_instantiation(**kwargs)
self.assertEqual(1, na_utils.LOG.warning.call_count)