Merge "Support extend_share in NetApp cDOT drivers"
This commit is contained in:
commit
baa83c5414
@ -21,6 +21,7 @@ import time
|
|||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
|
from oslo_utils import units
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
@ -940,6 +941,36 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||||||
}
|
}
|
||||||
self.send_request('volume-modify-iter', api_args)
|
self.send_request('volume-modify-iter', api_args)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def set_volume_size(self, volume_name, size_gb):
|
||||||
|
"""Set volume size."""
|
||||||
|
api_args = {
|
||||||
|
'query': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'volume-id-attributes': {
|
||||||
|
'name': volume_name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'attributes': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'volume-space-attributes': {
|
||||||
|
'size': int(size_gb) * units.Gi,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result = self.send_request('volume-modify-iter', api_args)
|
||||||
|
failures = result.get_child_content('num-failed')
|
||||||
|
if failures and int(failures) > 0:
|
||||||
|
failure_list = result.get_child_by_name(
|
||||||
|
'failure-list') or netapp_api.NaElement('none')
|
||||||
|
errors = failure_list.get_children()
|
||||||
|
if errors:
|
||||||
|
raise netapp_api.NaApiError(
|
||||||
|
errors[0].get_child_content('error-code'),
|
||||||
|
errors[0].get_child_content('error-message'))
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def volume_exists(self, volume_name):
|
def volume_exists(self, volume_name):
|
||||||
"""Checks if volume exists."""
|
"""Checks if volume exists."""
|
||||||
|
@ -61,6 +61,9 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
|
|||||||
def delete_snapshot(self, context, snapshot, **kwargs):
|
def delete_snapshot(self, context, snapshot, **kwargs):
|
||||||
self.library.delete_snapshot(context, snapshot, **kwargs)
|
self.library.delete_snapshot(context, snapshot, **kwargs)
|
||||||
|
|
||||||
|
def extend_share(self, share, new_size, **kwargs):
|
||||||
|
self.library.extend_share(share, new_size, **kwargs)
|
||||||
|
|
||||||
def ensure_share(self, context, share, **kwargs):
|
def ensure_share(self, context, share, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -61,6 +61,9 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
|
|||||||
def delete_snapshot(self, context, snapshot, **kwargs):
|
def delete_snapshot(self, context, snapshot, **kwargs):
|
||||||
self.library.delete_snapshot(context, snapshot, **kwargs)
|
self.library.delete_snapshot(context, snapshot, **kwargs)
|
||||||
|
|
||||||
|
def extend_share(self, share, new_size, **kwargs):
|
||||||
|
self.library.extend_share(share, new_size, **kwargs)
|
||||||
|
|
||||||
def ensure_share(self, context, share, **kwargs):
|
def ensure_share(self, context, share, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -631,6 +631,15 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
|
|
||||||
raise exception.ShareSnapshotIsBusy(snapshot_name=snapshot_name)
|
raise exception.ShareSnapshotIsBusy(snapshot_name=snapshot_name)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def extend_share(self, share, new_size, share_server=None):
|
||||||
|
"""Extends size of existing share."""
|
||||||
|
vserver, vserver_client = self._get_vserver(share_server=share_server)
|
||||||
|
share_name = self._get_valid_share_name(share['id'])
|
||||||
|
LOG.debug('Extending share %(name)s to %(size)s GB.',
|
||||||
|
{'name': share_name, 'size': new_size})
|
||||||
|
vserver_client.set_volume_size(share_name, new_size)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def allow_access(self, context, share, access, share_server=None):
|
def allow_access(self, context, share, access, share_server=None):
|
||||||
"""Allows access to a given NAS storage."""
|
"""Allows access to a given NAS storage."""
|
||||||
|
@ -1037,6 +1037,47 @@ VOLUME_GET_VOLUME_PATH_CIFS_RESPONSE = etree.XML("""
|
|||||||
VOLUME_JUNCTION_PATH = '/' + SHARE_NAME
|
VOLUME_JUNCTION_PATH = '/' + SHARE_NAME
|
||||||
VOLUME_JUNCTION_PATH_CIFS = '\\' + SHARE_NAME
|
VOLUME_JUNCTION_PATH_CIFS = '\\' + SHARE_NAME
|
||||||
|
|
||||||
|
VOLUME_MODIFY_ITER_RESPONSE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<failure-list />
|
||||||
|
<num-failed>0</num-failed>
|
||||||
|
<num-succeeded>1</num-succeeded>
|
||||||
|
<success-list>
|
||||||
|
<volume-modify-iter-info>
|
||||||
|
<volume-key>
|
||||||
|
<volume-attributes>
|
||||||
|
<volume-id-attributes>
|
||||||
|
<name>%(volume)s</name>
|
||||||
|
<owning-vserver-name>%(vserver)s</owning-vserver-name>
|
||||||
|
</volume-id-attributes>
|
||||||
|
</volume-attributes>
|
||||||
|
</volume-key>
|
||||||
|
</volume-modify-iter-info>
|
||||||
|
</success-list>
|
||||||
|
</results>
|
||||||
|
""" % {'volume': SHARE_NAME, 'vserver': VSERVER_NAME})
|
||||||
|
|
||||||
|
VOLUME_MODIFY_ITER_ERROR_RESPONSE = etree.XML("""
|
||||||
|
<results status="passed">
|
||||||
|
<failure-list>
|
||||||
|
<volume-modify-iter-info>
|
||||||
|
<error-code>160</error-code>
|
||||||
|
<error-message>Unable to set volume attribute "size"</error-message>
|
||||||
|
<volume-key>
|
||||||
|
<volume-attributes>
|
||||||
|
<volume-id-attributes>
|
||||||
|
<name>%(volume)s</name>
|
||||||
|
<owning-vserver-name>%(vserver)s</owning-vserver-name>
|
||||||
|
</volume-id-attributes>
|
||||||
|
</volume-attributes>
|
||||||
|
</volume-key>
|
||||||
|
</volume-modify-iter-info>
|
||||||
|
</failure-list>
|
||||||
|
<num-failed>1</num-failed>
|
||||||
|
<num-succeeded>0</num-succeeded>
|
||||||
|
</results>
|
||||||
|
""" % {'volume': SHARE_NAME, 'vserver': VSERVER_NAME})
|
||||||
|
|
||||||
SNAPSHOT_GET_ITER_NOT_BUSY_RESPONSE = etree.XML("""
|
SNAPSHOT_GET_ITER_NOT_BUSY_RESPONSE = etree.XML("""
|
||||||
<results status="passed">
|
<results status="passed">
|
||||||
<attributes-list>
|
<attributes-list>
|
||||||
|
@ -1718,6 +1718,47 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||||||
self.client.send_request.assert_called_once_with('sis-set-config',
|
self.client.send_request.assert_called_once_with('sis-set-config',
|
||||||
sis_set_config_args)
|
sis_set_config_args)
|
||||||
|
|
||||||
|
def test_set_volume_size(self):
|
||||||
|
|
||||||
|
api_response = netapp_api.NaElement(fake.VOLUME_MODIFY_ITER_RESPONSE)
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'send_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
self.client.set_volume_size(fake.SHARE_NAME, 10)
|
||||||
|
|
||||||
|
volume_modify_iter_args = {
|
||||||
|
'query': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'volume-id-attributes': {
|
||||||
|
'name': fake.SHARE_NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'attributes': {
|
||||||
|
'volume-attributes': {
|
||||||
|
'volume-space-attributes': {
|
||||||
|
'size': 10737418240,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.client.send_request.assert_has_calls([
|
||||||
|
mock.call('volume-modify-iter', volume_modify_iter_args)])
|
||||||
|
|
||||||
|
def test_set_volume_size_api_error(self):
|
||||||
|
|
||||||
|
api_response = netapp_api.NaElement(
|
||||||
|
fake.VOLUME_MODIFY_ITER_ERROR_RESPONSE)
|
||||||
|
self.mock_object(self.client,
|
||||||
|
'send_request',
|
||||||
|
mock.Mock(return_value=api_response))
|
||||||
|
|
||||||
|
self.assertRaises(netapp_api.NaApiError,
|
||||||
|
self.client.set_volume_size,
|
||||||
|
fake.SHARE_NAME,
|
||||||
|
10)
|
||||||
|
|
||||||
def test_volume_exists(self):
|
def test_volume_exists(self):
|
||||||
|
|
||||||
api_response = netapp_api.NaElement(fake.VOLUME_GET_NAME_RESPONSE)
|
api_response = netapp_api.NaElement(fake.VOLUME_GET_NAME_RESPONSE)
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
# 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
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
Mock unit tests for the NetApp file share driver interfaces
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import six
|
||||||
|
|
||||||
|
from manila.share.drivers.netapp.dataontap.cluster_mode import drv_multi_svm
|
||||||
|
from manila.share.drivers.netapp.dataontap.cluster_mode import drv_single_svm
|
||||||
|
from manila import test
|
||||||
|
|
||||||
|
|
||||||
|
class NetAppFileStorageDriverInterfaceTestCase(test.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(NetAppFileStorageDriverInterfaceTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.mock_object(drv_multi_svm.NetAppCmodeMultiSvmShareDriver,
|
||||||
|
'__init__',
|
||||||
|
mock.Mock(return_value=None))
|
||||||
|
self.mock_object(drv_single_svm.NetAppCmodeSingleSvmShareDriver,
|
||||||
|
'__init__',
|
||||||
|
mock.Mock(return_value=None))
|
||||||
|
|
||||||
|
self.drv_multi_svm = drv_multi_svm.NetAppCmodeMultiSvmShareDriver()
|
||||||
|
self.drv_single_svm = drv_single_svm.NetAppCmodeSingleSvmShareDriver()
|
||||||
|
|
||||||
|
def test_driver_interfaces_match(self):
|
||||||
|
"""Ensure the NetApp file storage driver interfaces match.
|
||||||
|
|
||||||
|
The two file share Manila drivers from NetApp (cDOT multi-SVM,
|
||||||
|
cDOT single-SVM) are merely passthrough shim layers atop a common
|
||||||
|
file storage library. Bugs are easily introduced when a Manila
|
||||||
|
method is exposed via a subset of those driver shims. This test
|
||||||
|
ensures they remain in sync and the library features are uniformly
|
||||||
|
available in the drivers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get local functions of each driver interface
|
||||||
|
multi_svm_methods = self._get_local_functions(self.drv_multi_svm)
|
||||||
|
single_svm_methods = self._get_local_functions(self.drv_single_svm)
|
||||||
|
|
||||||
|
# Ensure NetApp file share driver shims are identical
|
||||||
|
self.assertSetEqual(multi_svm_methods, single_svm_methods)
|
||||||
|
|
||||||
|
def _get_local_functions(self, obj):
|
||||||
|
"""Get function names of an object without superclass functions."""
|
||||||
|
return set([key for key, value in six.iteritems(type(obj).__dict__)
|
||||||
|
if callable(value)])
|
@ -1086,6 +1086,21 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
mock_sleep.assert_has_calls([mock.call(3)] * 20)
|
mock_sleep.assert_has_calls([mock.call(3)] * 20)
|
||||||
self.assertEqual(20, lib_base.LOG.debug.call_count)
|
self.assertEqual(20, lib_base.LOG.debug.call_count)
|
||||||
|
|
||||||
|
def test_extend_share(self):
|
||||||
|
|
||||||
|
vserver_client = mock.Mock()
|
||||||
|
self.mock_object(self.library,
|
||||||
|
'_get_vserver',
|
||||||
|
mock.Mock(return_value=(fake.VSERVER1,
|
||||||
|
vserver_client)))
|
||||||
|
mock_set_volume_size = self.mock_object(vserver_client,
|
||||||
|
'set_volume_size')
|
||||||
|
new_size = fake.SHARE['size'] * 2
|
||||||
|
|
||||||
|
self.library.extend_share(fake.SHARE, new_size)
|
||||||
|
|
||||||
|
mock_set_volume_size.assert_called_once_with(fake.SHARE_NAME, new_size)
|
||||||
|
|
||||||
def test_allow_access(self):
|
def test_allow_access(self):
|
||||||
|
|
||||||
protocol_helper = mock.Mock()
|
protocol_helper = mock.Mock()
|
||||||
|
Loading…
Reference in New Issue
Block a user