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_utils import strutils
|
||||
from oslo_utils import units
|
||||
import six
|
||||
|
||||
from manila import exception
|
||||
@ -940,6 +941,36 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
||||
}
|
||||
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
|
||||
def volume_exists(self, volume_name):
|
||||
"""Checks if volume exists."""
|
||||
|
@ -61,6 +61,9 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
|
||||
def delete_snapshot(self, 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):
|
||||
pass
|
||||
|
||||
|
@ -61,6 +61,9 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
|
||||
def delete_snapshot(self, 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):
|
||||
pass
|
||||
|
||||
|
@ -631,6 +631,15 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
|
||||
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
|
||||
def allow_access(self, context, share, access, share_server=None):
|
||||
"""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_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("""
|
||||
<results status="passed">
|
||||
<attributes-list>
|
||||
|
@ -1718,6 +1718,47 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
||||
self.client.send_request.assert_called_once_with('sis-set-config',
|
||||
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):
|
||||
|
||||
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)
|
||||
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):
|
||||
|
||||
protocol_helper = mock.Mock()
|
||||
|
Loading…
Reference in New Issue
Block a user