Manila cDOT storage service catalog

Like Cinder, Manila has a scheduler that operates (among other things)
on extra spec values in share types to determine which backend should
receive a new share.  In Kilo, Manila will adopt the pools feature from
Cinder, such that the scheduler may choose from more than one storage
destination per backend.  This commit adds a couple of per-pool
attributes to the share stats reported by the cDOT driver to the scheduler,
including aggregate disk type and RAID type.

Implements blueprint cdot-storage-service-catalog

Change-Id: Ice616d4189832d0e4b1a0fdfdd31b82b8c0a831d
This commit is contained in:
Clinton Knight 2015-01-27 13:38:58 -05:00
parent f396a2ce1a
commit 2e2b718edd
13 changed files with 445 additions and 49 deletions

View File

@ -1022,3 +1022,73 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
LOG.debug('EMS executed successfully.')
except netapp_api.NaApiError as e:
LOG.warning(_LW('Failed to invoke EMS. %s') % e)
def get_aggregate_raid_types(self, aggregate_names):
"""Get the RAID type of one or more aggregates."""
desired_attributes = {
'aggr-attributes': {
'aggregate-name': None,
'aggr-raid-attributes': {
'raid-type': None,
},
},
}
aggr_list = self._get_aggregates(aggregate_names=aggregate_names,
desired_attributes=desired_attributes)
aggr_raid_dict = {}
for aggr in aggr_list:
aggr_name = aggr.get_child_content('aggregate-name')
aggr_raid_attrs = aggr.get_child_by_name('aggr-raid-attributes')
aggr_raid_dict[aggr_name] = aggr_raid_attrs.get_child_content(
'raid-type')
return aggr_raid_dict
def get_aggregate_disk_types(self, aggregate_names):
"""Get the disk type of one or more aggregates."""
aggr_disk_type_dict = {}
for aggregate_name in aggregate_names:
# Only get 1 disk, since apart from hybrid aggregates all disks
# must be the same type.
api_args = {
'max-records': 1,
'query': {
'storage-disk-info': {
'disk-raid-info': {
'disk-aggregate-info': {
'aggregate-name': aggregate_name,
},
},
},
},
'desired-attributes': {
'storage-disk-info': {
'disk-raid-info': {
'effective-disk-type': None,
},
},
},
}
result = self.send_request('storage-disk-get-iter', api_args)
attributes_list = result.get_child_by_name(
'attributes-list') or netapp_api.NaElement('none')
storage_disk_info_list = attributes_list.get_children()
if len(storage_disk_info_list) >= 1:
storage_disk_info = storage_disk_info_list[0]
disk_raid_info = storage_disk_info.get_child_by_name(
'disk-raid-info')
if disk_raid_info:
disk_type = disk_raid_info.get_child_content(
'effective-disk-type')
if disk_type:
aggr_disk_type_dict[aggregate_name] = disk_type
return aggr_disk_type_dict

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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
@ -18,6 +18,7 @@ This driver requires a Data ONTAP (Cluster-mode) storage system with
installed CIFS and/or NFS licenses.
"""
import copy
import re
import socket
@ -25,10 +26,12 @@ from oslo_log import log
from oslo_utils import excutils
from oslo_utils import timeutils
from oslo_utils import units
import six
from manila import context
from manila import exception
from manila.i18n import _, _LE, _LI
from manila.openstack.common import loopingcall
from manila.share.drivers.netapp.dataontap.client import api as netapp_api
from manila.share.drivers.netapp.dataontap.client import client_cmode
from manila.share.drivers.netapp.dataontap.protocols import cifs_cmode
@ -72,6 +75,7 @@ class NetAppCmodeFileStorageLibrary(object):
"""
AUTOSUPPORT_INTERVAL_SECONDS = 3600 # hourly
SSC_UPDATE_INTERVAL_SECONDS = 3600 # hourly
def __init__(self, db, driver_name, **kwargs):
na_utils.validate_driver_instantiation(**kwargs)
@ -83,6 +87,7 @@ class NetAppCmodeFileStorageLibrary(object):
self._licenses = []
self._client = None
self._clients = {}
self._ssc_stats = {}
self._last_ems = timeutils.utcnow()
self.configuration = kwargs['configuration']
@ -96,7 +101,7 @@ class NetAppCmodeFileStorageLibrary(object):
self._app_version = kwargs.get('app_version', 'unknown')
na_utils.setup_tracing(self.configuration.netapp_trace_flags)
self.backend_name = self.configuration.safe_get(
self._backend_name = self.configuration.safe_get(
'share_backend_name') or driver_name
@na_utils.trace
@ -107,6 +112,7 @@ class NetAppCmodeFileStorageLibrary(object):
@na_utils.trace
def check_for_setup_error(self):
self._get_licenses()
self._start_periodic_tasks()
@na_utils.trace
def _get_api_client(self, vserver=None):
@ -131,7 +137,7 @@ class NetAppCmodeFileStorageLibrary(object):
self._licenses = self._client.get_licenses()
log_data = {
'backend': self.backend_name,
'backend': self._backend_name,
'licenses': ', '.join(self._licenses),
}
LOG.info(_LI('Available licenses on %(backend)s '
@ -139,11 +145,22 @@ class NetAppCmodeFileStorageLibrary(object):
if 'nfs' not in self._licenses and 'cifs' not in self._licenses:
msg = _LE('Neither NFS nor CIFS is licensed on %(backend)s')
msg_args = {'backend': self.backend_name}
msg_args = {'backend': self._backend_name}
LOG.error(msg % msg_args)
return self._licenses
def _start_periodic_tasks(self):
# Run the task once in the current thread so prevent a race with
# the first invocation of get_share_stats.
self._update_ssc_info()
ssc_periodic_task = loopingcall.FixedIntervalLoopingCall(
self._update_ssc_info)
ssc_periodic_task.start(interval=self.SSC_UPDATE_INTERVAL_SECONDS,
initial_delay=self.SSC_UPDATE_INTERVAL_SECONDS)
@na_utils.trace
def _get_valid_share_name(self, share_id):
"""Get share name according to share name template."""
@ -162,10 +179,11 @@ class NetAppCmodeFileStorageLibrary(object):
self._find_matching_aggregates())
data = {
'share_backend_name': self.backend_name,
'share_backend_name': self._backend_name,
'driver_name': self.driver_name,
'vendor_name': 'NetApp',
'driver_version': '1.0',
'netapp_storage_family': 'ontap_cluster',
'storage_protocol': 'NFS_CIFS',
'total_capacity_gb': 0.0,
'free_capacity_gb': 0.0,
@ -181,14 +199,21 @@ class NetAppCmodeFileStorageLibrary(object):
allocated_capacity_gb = na_utils.round_down(
float(aggr_space[aggr_name]['used']) / units.Gi, '0.01')
pools.append({
pool = {
'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,
})
}
# Add storage service catalog data.
pool_ssc_stats = self._ssc_stats.get(aggr_name)
if pool_ssc_stats:
pool.update(pool_ssc_stats)
pools.append(pool)
data['pools'] = pools
@ -504,3 +529,42 @@ class NetAppCmodeFileStorageLibrary(object):
vserver_client = self._get_api_client(vserver=vserver)
self._client.delete_vserver(vserver, vserver_client,
security_services=security_services)
def _update_ssc_info(self):
"""Periodically runs to update Storage Service Catalog data.
The self._ssc_stats attribute is updated with the following format.
{<aggregate_name> : {<ssc_key>: <ssc_value>}}
"""
LOG.info(_LI("Updating storage service catalog information for "
"backend '%s'"), self._backend_name)
# Work on a copy and update the ssc data atomically before returning.
ssc_stats = copy.deepcopy(self._ssc_stats)
aggregate_names = self._find_matching_aggregates()
# Initialize entries for each aggregate.
for aggregate_name in aggregate_names:
if aggregate_name not in ssc_stats:
ssc_stats[aggregate_name] = {}
if aggregate_names:
self._update_ssc_aggr_info(aggregate_names, ssc_stats)
self._ssc_stats = ssc_stats
def _update_ssc_aggr_info(self, aggregate_names, ssc_stats):
"""Updates the given SSC dictionary with new disk type information.
:param volume_groups: The volume groups this driver cares about
:param ssc_stats: The dictionary to update
"""
raid_types = self._client.get_aggregate_raid_types(aggregate_names)
for aggregate_name, raid_type in six.iteritems(raid_types):
ssc_stats[aggregate_name]['netapp_raid_type'] = raid_type
disk_types = self._client.get_aggregate_disk_types(aggregate_names)
for aggregate_name, disk_type in six.iteritems(disk_types):
ssc_stats[aggregate_name]['netapp_disk_type'] = disk_type

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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

View File

@ -1,4 +1,4 @@
# Copyright (c) - 2014, Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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
@ -30,7 +30,9 @@ NODE_VSERVER_NAME = 'fake_node_vserver'
ROOT_VOLUME_AGGREGATE_NAME = 'fake_root_aggr'
ROOT_VOLUME_NAME = 'fake_root_volume'
SHARE_AGGREGATE_NAME = 'fake_aggr1'
SHARE_AGGREGATE_NAMES = ['fake_aggr1', 'fake_aggr2']
SHARE_AGGREGATE_NAMES = ('fake_aggr1', 'fake_aggr2')
SHARE_AGGREGATE_RAID_TYPES = ('raid4', 'raid_dp')
SHARE_AGGREGATE_DISK_TYPE = 'FCAL'
SHARE_NAME = 'fake_share'
SNAPSHOT_NAME = 'fake_snapshot'
PARENT_SHARE_NAME = 'fake_parent_share'
@ -237,10 +239,10 @@ LICENSE_V2_LIST_INFO_RESPONSE = etree.XML("""
</results>
""")
LICENSES = [
LICENSES = (
'base', 'cifs', 'fcp', 'flexclone', 'iscsi', 'nfs', 'snapmirror',
'snaprestore', 'snapvault'
]
)
VOLUME_COUNT_RESPONSE = etree.XML("""
<results status="passed">
@ -384,7 +386,7 @@ NET_PORT_GET_ITER_RESPONSE = etree.XML("""
</results>
""" % {'node_name': NODE_NAME})
PORTS = ['e0a', 'e0b', 'e0c', 'e0d']
PORTS = ('e0a', 'e0b', 'e0c', 'e0d')
NET_INTERFACE_GET_ITER_RESPONSE = etree.XML("""
<results status="passed">
@ -483,9 +485,9 @@ NET_INTERFACE_GET_ITER_RESPONSE = etree.XML("""
'vlan': VLAN_PORT
})
LIF_NAMES = ['cluster_mgmt', 'mgmt1', LIF_NAME]
LIF_NAMES = ('cluster_mgmt', 'mgmt1', LIF_NAME)
LIFS = [
LIFS = (
{'address': '192.168.228.42',
'home-node': NODE_NAME,
'home-port': 'e0c',
@ -509,8 +511,8 @@ LIFS = [
'netmask': NETMASK,
'role': 'data',
'vserver': VSERVER_NAME
}
]
},
)
NET_INTERFACE_GET_ONE_RESPONSE = etree.XML("""
<results status="passed">
@ -565,7 +567,7 @@ AGGR_GET_NAMES_RESPONSE = etree.XML("""
</results>
""")
AGGR_NAMES = ['aggr0', 'manila']
AGGR_NAMES = ('aggr0', 'manila')
AGGR_GET_SPACE_RESPONSE = etree.XML("""
<results status="passed">
@ -918,7 +920,7 @@ SNAPSHOT_GET_ITER_BUSY_RESPONSE = etree.XML("""
</results>
""" % {'snap': SNAPSHOT_NAME, 'volume': SHARE_NAME, 'vserver': VSERVER_NAME})
NFS_EXPORT_RULES = ['10.10.10.10', '10.10.10.20']
NFS_EXPORT_RULES = ('10.10.10.10', '10.10.10.20')
NFS_EXPORTFS_LIST_RULES_2_NO_RULES_RESPONSE = etree.XML("""
<results status="passed">
@ -975,6 +977,68 @@ NFS_EXPORTFS_LIST_RULES_2_RESPONSE = etree.XML("""
'host2': NFS_EXPORT_RULES[1],
})
AGGR_GET_RAID_TYPE_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<aggr-attributes>
<aggr-raid-attributes>
<plexes>
<plex-attributes>
<plex-name>/aggr0/plex0</plex-name>
<raidgroups>
<raidgroup-attributes>
<raidgroup-name>/aggr0/plex0/rg0</raidgroup-name>
</raidgroup-attributes>
</raidgroups>
</plex-attributes>
</plexes>
<raid-type>%(raid_type1)s</raid-type>
</aggr-raid-attributes>
<aggregate-name>%(aggr1)s</aggregate-name>
</aggr-attributes>
<aggr-attributes>
<aggr-raid-attributes>
<plexes>
<plex-attributes>
<plex-name>/manila/plex0</plex-name>
<raidgroups>
<raidgroup-attributes>
<raidgroup-name>/manila/plex0/rg0</raidgroup-name>
</raidgroup-attributes>
<raidgroup-attributes>
<raidgroup-name>/manila/plex0/rg1</raidgroup-name>
</raidgroup-attributes>
</raidgroups>
</plex-attributes>
</plexes>
<raid-type>%(raid_type2)s</raid-type>
</aggr-raid-attributes>
<aggregate-name>%(aggr2)s</aggregate-name>
</aggr-attributes>
</attributes-list>
<num-records>2</num-records>
</results>
""" % {
'aggr1': SHARE_AGGREGATE_NAMES[0],
'aggr2': SHARE_AGGREGATE_NAMES[1],
'raid_type1': SHARE_AGGREGATE_RAID_TYPES[0],
'raid_type2': SHARE_AGGREGATE_RAID_TYPES[1]
})
STORAGE_DISK_GET_ITER_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>
<storage-disk-info>
<disk-name>cluster3-01:v5.19</disk-name>
<disk-raid-info>
<effective-disk-type>%s</effective-disk-type>
</disk-raid-info>
</storage-disk-info>
</attributes-list>
<num-records>1</num-records>
</results>
""" % SHARE_AGGREGATE_DISK_TYPE)
GET_AGGREGATE_FOR_VOLUME_RESPONSE = etree.XML("""
<results status="passed">
<attributes-list>

View File

@ -99,7 +99,7 @@ class NetAppBaseClientTestCase(test.TestCase):
response = self.client.get_licenses()
self.assertListEqual(fake.LICENSES, response)
self.assertSequenceEqual(fake.LICENSES, response)
def test_get_licenses_api_error(self):

View File

@ -1,5 +1,5 @@
# Copyright (c) 2014 Alex Meade. All rights reserved.
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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
@ -473,7 +473,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
result = self.client.list_aggregates()
self.assertListEqual(fake.AGGR_NAMES, result)
self.assertSequenceEqual(fake.AGGR_NAMES, result)
def test_list_aggregates_not_found(self):
@ -628,7 +628,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('net-interface-get-iter', net_interface_get_args)])
self.assertListEqual(fake.LIF_NAMES, result)
self.assertSequenceEqual(fake.LIF_NAMES, result)
def test_list_network_interfaces_not_found(self):
@ -653,7 +653,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('net-interface-get-iter')])
self.assertListEqual(fake.LIFS, result)
self.assertSequenceEqual(fake.LIFS, result)
def test_delete_network_interface(self):
@ -1671,7 +1671,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('nfs-exportfs-list-rules-2',
nfs_exportfs_list_rules_2_args)])
self.assertListEqual(fake.NFS_EXPORT_RULES, result)
self.assertSequenceEqual(fake.NFS_EXPORT_RULES, result)
def test_get_nfs_export_rules_not_found(self):
@ -1783,3 +1783,89 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('ems-autosupport-log', fake.EMS_MESSAGE)])
self.assertEqual(1, client_cmode.LOG.warning.call_count)
def test_get_aggregate_raid_types(self):
api_response = netapp_api.NaElement(fake.AGGR_GET_RAID_TYPE_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.get_aggregate_raid_types(
fake.SHARE_AGGREGATE_NAMES)
aggr_get_iter_args = {
'query': {
'aggr-attributes': {
'aggregate-name': '|'.join(fake.SHARE_AGGREGATE_NAMES),
}
},
'desired-attributes': {
'aggr-attributes': {
'aggregate-name': None,
'aggr-raid-attributes': {
'raid-type': None,
}
}
}
}
expected = {
fake.SHARE_AGGREGATE_NAMES[0]:
fake.SHARE_AGGREGATE_RAID_TYPES[0],
fake.SHARE_AGGREGATE_NAMES[1]:
fake.SHARE_AGGREGATE_RAID_TYPES[1]
}
self.client.send_request.assert_has_calls([
mock.call('aggr-get-iter', aggr_get_iter_args)])
self.assertDictEqual(expected, result)
def test_get_aggregate_raid_types_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))
result = self.client.get_aggregate_raid_types(
fake.SHARE_AGGREGATE_NAMES)
self.assertDictEqual({}, result)
def test_get_aggregate_disk_types(self):
api_response = netapp_api.NaElement(
fake.STORAGE_DISK_GET_ITER_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.get_aggregate_disk_types(
fake.SHARE_AGGREGATE_NAMES)
expected = {
fake.SHARE_AGGREGATE_NAMES[0]:
fake.SHARE_AGGREGATE_DISK_TYPE,
fake.SHARE_AGGREGATE_NAMES[1]:
fake.SHARE_AGGREGATE_DISK_TYPE
}
self.assertEqual(len(fake.SHARE_AGGREGATE_NAMES),
self.client.send_request.call_count)
self.assertDictEqual(expected, result)
def test_get_aggregate_disk_types_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))
result = self.client.get_aggregate_disk_types(
fake.SHARE_AGGREGATE_NAMES)
self.assertEqual(len(fake.SHARE_AGGREGATE_NAMES),
self.client.send_request.call_count)
self.assertDictEqual({}, result)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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
@ -24,6 +24,7 @@ from oslo_utils import timeutils
from manila import context
from manila import exception
from manila.openstack.common import loopingcall
from manila.share.drivers.netapp.dataontap.client import api as netapp_api
from manila.share.drivers.netapp.dataontap.client import client_cmode
from manila.share.drivers.netapp.dataontap.cluster_mode import lib_base
@ -141,6 +142,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertIsNone(self.library._helpers)
self.assertListEqual([], self.library._licenses)
self.assertDictEqual({}, self.library._clients)
self.assertDictEqual({}, self.library._ssc_stats)
self.assertIsNotNone(self.library._app_version)
self.assertIsNotNone(self.library._last_ems)
@ -155,10 +157,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
def test_check_for_setup_error(self):
mock_get_licenses = self.mock_object(self.library, '_get_licenses')
mock_start_periodic_tasks = self.mock_object(self.library,
'_start_periodic_tasks')
self.library.check_for_setup_error()
mock_get_licenses.assert_called_once_with()
mock_start_periodic_tasks.assert_called_once_with()
def test_get_api_client(self):
@ -205,6 +210,22 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
client_kwargs['vserver'] = fake.VSERVER2
mock_client_constructor.assert_called_once_with(**client_kwargs)
def test_start_periodic_tasks(self):
mock_update_ssc_info = self.mock_object(self.library,
'_update_ssc_info')
mock_ssc_periodic_task = mock.Mock()
mock_loopingcall = self.mock_object(
loopingcall,
'FixedIntervalLoopingCall',
mock.Mock(return_value=mock_ssc_periodic_task))
self.library._start_periodic_tasks()
self.assertTrue(mock_update_ssc_info.called)
mock_loopingcall.assert_called_once_with(mock_update_ssc_info)
self.assertTrue(mock_ssc_periodic_task.start.called)
def test_get_licenses_both_protocols(self):
self.mock_object(self.client,
'get_licenses',
@ -212,7 +233,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
result = self.library._get_licenses()
self.assertListEqual(fake.LICENSES, result)
self.assertSequenceEqual(fake.LICENSES, result)
self.assertEqual(0, lib_base.LOG.error.call_count)
self.assertEqual(1, lib_base.LOG.info.call_count)
@ -266,6 +287,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock.Mock(return_value=fake.AGGREGATE_CAPACITIES))
mock_handle_ems_logging = self.mock_object(self.library,
'_handle_ems_logging')
self.library._ssc_stats = fake.SSC_INFO
result = self.library.get_share_stats()
@ -274,23 +296,28 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'driver_name': fake.DRIVER_NAME,
'vendor_name': 'NetApp',
'driver_version': '1.0',
'netapp_storage_family': 'ontap_cluster',
'storage_protocol': 'NFS_CIFS',
'total_capacity_gb': 0.0,
'free_capacity_gb': 0.0,
'pools': [
{'pool_name': 'manila1',
{'pool_name': fake.AGGREGATES[0],
'total_capacity_gb': 3.3,
'free_capacity_gb': 1.1,
'allocated_capacity_gb': 2.2,
'QoS_support': 'False',
'reserved_percentage': 0,
'netapp_raid_type': 'raid4',
'netapp_disk_type': 'FCAL'
},
{'pool_name': 'manila2',
{'pool_name': fake.AGGREGATES[1],
'total_capacity_gb': 6.0,
'free_capacity_gb': 2.0,
'allocated_capacity_gb': 4.0,
'QoS_support': 'False',
'reserved_percentage': 0,
'netapp_raid_type': 'raid_dp',
'netapp_disk_type': 'SSD'
},
]
}
@ -357,12 +384,12 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
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.assertSequenceEqual(fake.AGGREGATES, result)
self.library.configuration.netapp_aggregate_name_search_pattern = (
'aggr.*')
'man.*')
result = self.library._find_matching_aggregates()
self.assertListEqual(result, ['aggr0'])
self.assertSequenceEqual(fake.AGGREGATES, result)
def test_setup_helpers(self):
@ -1002,3 +1029,67 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake.VSERVER1,
vserver_client,
security_services=fake.NETWORK_INFO['security_services'])
def test_update_ssc_info(self):
self.mock_object(self.library,
'_find_matching_aggregates',
mock.Mock(return_value=fake.AGGREGATES))
mock_update_ssc_aggr_info = self.mock_object(self.library,
'_update_ssc_aggr_info')
self.library._update_ssc_info()
expected = {
fake.AGGREGATES[0]: {},
fake.AGGREGATES[1]: {}
}
self.assertDictEqual(expected, self.library._ssc_stats)
self.assertTrue(mock_update_ssc_aggr_info.called)
def test_update_ssc_info_no_aggregates(self):
self.mock_object(self.library,
'_find_matching_aggregates',
mock.Mock(return_value=[]))
mock_update_ssc_aggr_info = self.mock_object(self.library,
'_update_ssc_aggr_info')
self.library._update_ssc_info()
self.assertDictEqual({}, self.library._ssc_stats)
self.assertFalse(mock_update_ssc_aggr_info.called)
def test_update_ssc_aggr_info(self):
self.mock_object(self.client,
'get_aggregate_raid_types',
mock.Mock(return_value=fake.SSC_RAID_TYPES))
self.mock_object(self.client,
'get_aggregate_disk_types',
mock.Mock(return_value=fake.SSC_DISK_TYPES))
ssc_stats = {
fake.AGGREGATES[0]: {},
fake.AGGREGATES[1]: {}
}
self.library._update_ssc_aggr_info(fake.AGGREGATES, ssc_stats)
self.assertDictEqual(fake.SSC_INFO, ssc_stats)
def test_update_ssc_aggr_info_not_found(self):
self.mock_object(self.client,
'get_aggregate_raid_types',
mock.Mock(return_value={}))
self.mock_object(self.client,
'get_aggregate_disk_types',
mock.Mock(return_value={}))
ssc_stats = {}
self.library._update_ssc_aggr_info(fake.AGGREGATES, ssc_stats)
self.assertDictEqual({}, ssc_stats)

View File

@ -1,4 +1,4 @@
# Copyright (c) - 2014, Clinton Knight All rights reserved.
# Copyright (c) 2015 Clinton Knight 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
@ -19,8 +19,8 @@ HOST_NAME = 'fake_host'
POOL_NAME = 'fake_pool'
VSERVER1 = 'fake_vserver_1'
VSERVER2 = 'fake_vserver_2'
LICENSES = ['base', 'cifs', 'fcp', 'flexclone', 'iscsi', 'nfs', 'snapmirror',
'snaprestore', 'snapvault']
LICENSES = ('base', 'cifs', 'fcp', 'flexclone', 'iscsi', 'nfs', 'snapmirror',
'snaprestore', 'snapvault')
VOLUME_NAME_TEMPLATE = 'share_%(share_id)s'
VSERVER_NAME_TEMPLATE = 'os_%s'
AGGREGATE_NAME_SEARCH_PATTERN = '(.*)'
@ -32,10 +32,10 @@ PARENT_SHARE_ID = '585c3935-2aa9-437c-8bad-5abae1076555'
SNAPSHOT_ID = 'de4c9050-e2f9-4ce1-ade4-5ed0c9f26451'
FREE_CAPACITY = 10000000000
TOTAL_CAPACITY = 20000000000
AGGREGATES = ['aggr0', 'manila']
ROOT_VOLUME_AGGREGATE = 'manila'
AGGREGATES = ('manila_aggr_1', 'manila_aggr_2')
ROOT_VOLUME_AGGREGATE = 'manila1'
ROOT_VOLUME = 'root'
CLUSTER_NODES = ['cluster1_01', 'cluster1_02']
CLUSTER_NODES = ('cluster1_01', 'cluster1_02')
NODE_DATA_PORT = 'e0c'
LIF_NAME_TEMPLATE = 'os_%(net_allocation_id)s'
@ -91,8 +91,8 @@ SNAPSHOT = {
}
LIF_NAMES = []
LIF_ADDRESSES = ['10.10.10.10', '10.10.10.20']
LIFS = [
LIF_ADDRESSES = ('10.10.10.10', '10.10.10.20')
LIFS = (
{'address': LIF_ADDRESSES[0],
'home-node': CLUSTER_NODES[0],
'home-port': 'e0c',
@ -108,8 +108,8 @@ LIFS = [
'netmask': NETWORK_INFO_NETMASK,
'role': 'data',
'vserver': VSERVER1
}
]
},
)
SHARE_ACCESS = {
'access_type': 'user',
@ -128,14 +128,35 @@ EMS_MESSAGE = {
}
AGGREGATE_CAPACITIES = {
'manila1': {
AGGREGATES[0]: {
'available': 1181116007, # 1.1 GB
'total': 3543348020, # 3.3 GB
'used': 2362232013, # 2.2 GB
},
'manila2': {
AGGREGATES[1]: {
'available': 2147483648, # 2.0 GB
'total': 6442450944, # 6.0 GB
'used': 4294967296, # 4.0 GB
}
}
SSC_INFO = {
AGGREGATES[0]: {
'netapp_raid_type': 'raid4',
'netapp_disk_type': 'FCAL'
},
AGGREGATES[1]: {
'netapp_raid_type': 'raid_dp',
'netapp_disk_type': 'SSD'
}
}
SSC_RAID_TYPES = {
AGGREGATES[0]: 'raid4',
AGGREGATES[1]: 'raid_dp'
}
SSC_DISK_TYPES = {
AGGREGATES[0]: 'FCAL',
AGGREGATES[1]: 'SSD'
}

View File

@ -1,4 +1,4 @@
# Copyright (c) - 2014, Clinton Knight All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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

View File

@ -1,4 +1,4 @@
# Copyright (c) 2014 Clinton Knight. All rights reserved.
# Copyright (c) 2015 Clinton Knight. 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