6877 lines
272 KiB
Python
6877 lines
272 KiB
Python
# Copyright (c) 2023 NetApp, 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 copy
|
|
import math
|
|
import time
|
|
from unittest import mock
|
|
|
|
import ddt
|
|
from oslo_log import log
|
|
from oslo_utils import units
|
|
|
|
from manila import exception
|
|
from manila.share.drivers.netapp.dataontap.client import client_cmode
|
|
from manila.share.drivers.netapp.dataontap.client import client_cmode_rest
|
|
from manila.share.drivers.netapp.dataontap.client import rest_api as netapp_api
|
|
from manila.share.drivers.netapp import utils as netapp_utils
|
|
from manila import test
|
|
from manila.tests.share.drivers.netapp.dataontap.client import fakes as fake
|
|
|
|
|
|
@ddt.ddt
|
|
class NetAppRestCmodeClientTestCase(test.TestCase):
|
|
|
|
def setUp(self):
|
|
super(NetAppRestCmodeClientTestCase, self).setUp()
|
|
|
|
# Mock loggers as themselves to allow logger arg validation
|
|
mock_logger = log.getLogger('mock_logger')
|
|
self.mock_object(client_cmode_rest.LOG,
|
|
'error',
|
|
mock.Mock(side_effect=mock_logger.error))
|
|
self.mock_object(client_cmode_rest.LOG,
|
|
'warning',
|
|
mock.Mock(side_effect=mock_logger.warning))
|
|
self.mock_object(client_cmode_rest.LOG,
|
|
'debug',
|
|
mock.Mock(side_effect=mock_logger.debug))
|
|
self.mock_object(client_cmode.NetAppCmodeClient,
|
|
'get_ontapi_version',
|
|
mock.Mock(return_value=(1, 20)))
|
|
# store the original reference so we can call it later in
|
|
# test_get_ontap_version
|
|
self.original_get_ontap_version = (
|
|
client_cmode_rest.NetAppRestClient.get_ontap_version)
|
|
self.mock_object(client_cmode_rest.NetAppRestClient,
|
|
'get_ontap_version',
|
|
mock.Mock(return_value={
|
|
'version-tuple': (9, 12, 1),
|
|
'version': fake.VERSION,
|
|
}))
|
|
self.original_check_for_cluster_credentials = (
|
|
client_cmode_rest.NetAppRestClient._check_for_cluster_credentials)
|
|
self.mock_object(client_cmode_rest.NetAppRestClient,
|
|
'_check_for_cluster_credentials',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(client_cmode.NetAppCmodeClient,
|
|
'get_system_version',
|
|
mock.Mock(return_value={
|
|
'version-tuple': (9, 10, 1),
|
|
'version': fake.VERSION,
|
|
}))
|
|
|
|
self.client = client_cmode_rest.NetAppRestClient(
|
|
**fake.CONNECTION_INFO)
|
|
self.client.connection = mock.MagicMock()
|
|
|
|
def _mock_api_error(self, code='fake', message='fake'):
|
|
return mock.Mock(
|
|
side_effect=netapp_api.api.NaApiError(code=code, message=message))
|
|
|
|
def test_send_request(self):
|
|
expected = 'fake_response'
|
|
mock_get_records = self.mock_object(
|
|
self.client, 'get_records',
|
|
mock.Mock(return_value=expected))
|
|
|
|
res = self.client.send_request(
|
|
fake.FAKE_ACTION_URL, 'get',
|
|
body=fake.FAKE_HTTP_BODY,
|
|
query=fake.FAKE_HTTP_QUERY, enable_tunneling=False)
|
|
|
|
self.assertEqual(expected, res)
|
|
mock_get_records.assert_called_once_with(
|
|
fake.FAKE_ACTION_URL,
|
|
fake.FAKE_HTTP_QUERY, False, 10000)
|
|
|
|
def test_send_request_post(self):
|
|
expected = (201, 'fake_response')
|
|
mock_invoke = self.mock_object(
|
|
self.client.connection, 'invoke_successfully',
|
|
mock.Mock(return_value=expected))
|
|
|
|
res = self.client.send_request(
|
|
fake.FAKE_ACTION_URL, 'post',
|
|
body=fake.FAKE_HTTP_BODY,
|
|
query=fake.FAKE_HTTP_QUERY, enable_tunneling=False)
|
|
|
|
self.assertEqual(expected[1], res)
|
|
mock_invoke.assert_called_once_with(
|
|
fake.FAKE_ACTION_URL, 'post',
|
|
body=fake.FAKE_HTTP_BODY,
|
|
query=fake.FAKE_HTTP_QUERY, enable_tunneling=False)
|
|
|
|
def test_send_request_wait(self):
|
|
expected = (202, fake.JOB_RESPONSE_REST)
|
|
mock_invoke = self.mock_object(
|
|
self.client.connection, 'invoke_successfully',
|
|
mock.Mock(return_value=expected))
|
|
|
|
mock_wait = self.mock_object(
|
|
self.client, '_wait_job_result',
|
|
mock.Mock(return_value=expected[1]))
|
|
|
|
res = self.client.send_request(
|
|
fake.FAKE_ACTION_URL, 'post',
|
|
body=fake.FAKE_HTTP_BODY,
|
|
query=fake.FAKE_HTTP_QUERY, enable_tunneling=False)
|
|
|
|
self.assertEqual(expected[1], res)
|
|
mock_invoke.assert_called_once_with(
|
|
fake.FAKE_ACTION_URL, 'post',
|
|
body=fake.FAKE_HTTP_BODY,
|
|
query=fake.FAKE_HTTP_QUERY, enable_tunneling=False)
|
|
mock_wait.assert_called_once_with(
|
|
expected[1]['job']['_links']['self']['href'][4:])
|
|
|
|
@ddt.data(True, False)
|
|
def test_get_records(self, enable_tunneling):
|
|
api_responses = [
|
|
(200, fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE),
|
|
(200, fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE),
|
|
(200, fake.VOLUME_GET_ITER_RESPONSE_REST_LAST_PAGE),
|
|
]
|
|
|
|
mock_invoke = self.mock_object(
|
|
self.client.connection, 'invoke_successfully',
|
|
mock.Mock(side_effect=copy.deepcopy(api_responses)))
|
|
|
|
query = {
|
|
'fields': 'name'
|
|
}
|
|
|
|
result = self.client.get_records(
|
|
'/storage/volumes/', query=query,
|
|
enable_tunneling=enable_tunneling,
|
|
max_page_length=10)
|
|
|
|
num_records = result['num_records']
|
|
self.assertEqual(28, num_records)
|
|
self.assertEqual(28, len(result['records']))
|
|
|
|
expected_records = []
|
|
expected_records.extend(api_responses[0][1]['records'])
|
|
expected_records.extend(api_responses[1][1]['records'])
|
|
expected_records.extend(api_responses[2][1]['records'])
|
|
|
|
self.assertEqual(expected_records, result['records'])
|
|
|
|
next_tag = result.get('next')
|
|
self.assertIsNone(next_tag)
|
|
|
|
expected_query = copy.deepcopy(query)
|
|
expected_query['max_records'] = 10
|
|
|
|
next_url_1 = api_responses[0][1]['_links']['next']['href'][4:]
|
|
next_url_2 = api_responses[1][1]['_links']['next']['href'][4:]
|
|
|
|
mock_invoke.assert_has_calls([
|
|
mock.call('/storage/volumes/', 'get', query=expected_query,
|
|
enable_tunneling=enable_tunneling),
|
|
mock.call(next_url_1, 'get', query=None,
|
|
enable_tunneling=enable_tunneling),
|
|
mock.call(next_url_2, 'get', query=None,
|
|
enable_tunneling=enable_tunneling),
|
|
])
|
|
|
|
def test_get_records_single_page(self):
|
|
|
|
api_response = (
|
|
200, fake.VOLUME_GET_ITER_RESPONSE_REST_LAST_PAGE)
|
|
mock_invoke = self.mock_object(self.client.connection,
|
|
'invoke_successfully',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
query = {
|
|
'fields': 'name'
|
|
}
|
|
|
|
result = self.client.get_records(
|
|
'/storage/volumes/', query=query, max_page_length=10)
|
|
|
|
num_records = result['num_records']
|
|
self.assertEqual(8, num_records)
|
|
self.assertEqual(8, len(result['records']))
|
|
|
|
next_tag = result.get('next')
|
|
self.assertIsNone(next_tag)
|
|
|
|
args = copy.deepcopy(query)
|
|
args['max_records'] = 10
|
|
|
|
mock_invoke.assert_has_calls([
|
|
mock.call('/storage/volumes/', 'get', query=args,
|
|
enable_tunneling=True),
|
|
])
|
|
|
|
def test_get_records_not_found(self):
|
|
|
|
api_response = (200, fake.NO_RECORDS_RESPONSE_REST)
|
|
mock_invoke = self.mock_object(self.client.connection,
|
|
'invoke_successfully',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_records('/storage/volumes/')
|
|
|
|
num_records = result['num_records']
|
|
self.assertEqual(0, num_records)
|
|
self.assertEqual(0, len(result['records']))
|
|
|
|
args = {
|
|
'max_records': client_cmode_rest.DEFAULT_MAX_PAGE_LENGTH
|
|
}
|
|
|
|
mock_invoke.assert_has_calls([
|
|
mock.call('/storage/volumes/', 'get', query=args,
|
|
enable_tunneling=True),
|
|
])
|
|
|
|
def test_get_records_timeout(self):
|
|
# To simulate timeout, max_records is 30, but the API returns less
|
|
# records and fill the 'next url' pointing to the next page.
|
|
max_records = 30
|
|
api_responses = [
|
|
(200, fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE),
|
|
(200, fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE),
|
|
(200, fake.VOLUME_GET_ITER_RESPONSE_REST_LAST_PAGE),
|
|
]
|
|
|
|
mock_invoke = self.mock_object(
|
|
self.client.connection, 'invoke_successfully',
|
|
mock.Mock(side_effect=copy.deepcopy(api_responses)))
|
|
|
|
query = {
|
|
'fields': 'name'
|
|
}
|
|
|
|
result = self.client.get_records(
|
|
'/storage/volumes/', query=query, max_page_length=max_records)
|
|
|
|
num_records = result['num_records']
|
|
self.assertEqual(28, num_records)
|
|
self.assertEqual(28, len(result['records']))
|
|
|
|
expected_records = []
|
|
expected_records.extend(api_responses[0][1]['records'])
|
|
expected_records.extend(api_responses[1][1]['records'])
|
|
expected_records.extend(api_responses[2][1]['records'])
|
|
|
|
self.assertEqual(expected_records, result['records'])
|
|
|
|
next_tag = result.get('next', None)
|
|
self.assertIsNone(next_tag)
|
|
|
|
args1 = copy.deepcopy(query)
|
|
args1['max_records'] = max_records
|
|
|
|
next_url_1 = api_responses[0][1]['_links']['next']['href'][4:]
|
|
next_url_2 = api_responses[1][1]['_links']['next']['href'][4:]
|
|
|
|
mock_invoke.assert_has_calls([
|
|
mock.call('/storage/volumes/', 'get', query=args1,
|
|
enable_tunneling=True),
|
|
mock.call(next_url_1, 'get', query=None, enable_tunneling=True),
|
|
mock.call(next_url_2, 'get', query=None, enable_tunneling=True),
|
|
])
|
|
|
|
def test__getattr__(self):
|
|
# NOTE(nahimsouza): get_ontapi_version is implemented only in ZAPI
|
|
# client, therefore, it will call __getattr__
|
|
self.client.get_ontapi_version()
|
|
|
|
@ddt.data(True, False)
|
|
def test_get_ontap_version(self, cached):
|
|
self.client.get_ontap_version = self.original_get_ontap_version
|
|
api_response = {
|
|
'records': [
|
|
{
|
|
'version': {
|
|
'generation': 9,
|
|
'major': 11,
|
|
'minor': 1,
|
|
'full': 'NetApp Release 9.11.1'
|
|
}
|
|
}]
|
|
|
|
}
|
|
return_mock = {
|
|
'version': 'NetApp Release 9.11.1',
|
|
'version-tuple': (9, 11, 1)
|
|
}
|
|
mock_connect = self.mock_object(self.client.connection,
|
|
'get_ontap_version',
|
|
mock.Mock(return_value=return_mock))
|
|
mock_send_request = self.mock_object(
|
|
self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_ontap_version(self=self.client, cached=cached)
|
|
|
|
if cached:
|
|
mock_connect.assert_called_once()
|
|
else:
|
|
mock_send_request.assert_called_once_with(
|
|
'/cluster/nodes', 'get', query={'fields': 'version'},
|
|
enable_tunneling=False)
|
|
|
|
self.assertEqual(return_mock, result)
|
|
|
|
def test__wait_job_result(self):
|
|
response = fake.JOB_SUCCESSFUL_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=response))
|
|
result = self.client._wait_job_result(
|
|
f'/cluster/jobs/{fake.FAKE_UUID}')
|
|
self.assertEqual(response, result)
|
|
|
|
def test__wait_job_result_failure(self):
|
|
response = fake.JOB_ERROR_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(netapp_utils.NetAppDriverException,
|
|
self.client._wait_job_result,
|
|
f'/cluster/jobs/{fake.FAKE_UUID}')
|
|
|
|
def test__wait_job_result_timeout(self):
|
|
response = fake.JOB_RUNNING_REST
|
|
self.client.async_rest_timeout = 2
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(netapp_utils.NetAppDriverException,
|
|
self.client._wait_job_result,
|
|
f'/cluster/jobs/{fake.FAKE_UUID}')
|
|
|
|
def test_list_cluster_nodes(self):
|
|
"""Get all available cluster nodes."""
|
|
|
|
return_value = fake.FAKE_GET_CLUSTER_NODE_VERSION_REST
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
test_result = self.client.list_cluster_nodes()
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/cluster/nodes', 'get'
|
|
)
|
|
|
|
nodes = return_value.get('records', [])
|
|
|
|
expected_result = [node['name'] for node in nodes]
|
|
|
|
self.assertEqual(expected_result, test_result)
|
|
|
|
@ddt.data(True, False)
|
|
def test_check_for_cluster_credentials(self, cluster_creds):
|
|
self.client._have_cluster_creds = cluster_creds
|
|
|
|
result = self.client.check_for_cluster_credentials()
|
|
|
|
self.assertEqual(cluster_creds, result)
|
|
|
|
def test__check_for_cluster_credentials(self):
|
|
self.client._check_for_cluster_credentials = (
|
|
self.original_check_for_cluster_credentials)
|
|
api_response = fake.FAKE_GET_CLUSTER_NODE_VERSION_REST
|
|
self.mock_object(self.client,
|
|
'list_cluster_nodes',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client._check_for_cluster_credentials(self=self.client)
|
|
|
|
self.assertTrue(result)
|
|
|
|
def test__check_for_cluster_credentials_not_cluster(self):
|
|
self.client._check_for_cluster_credentials = (
|
|
self.original_check_for_cluster_credentials)
|
|
self.mock_object(self.client, 'list_cluster_nodes',
|
|
self._mock_api_error(
|
|
netapp_api.EREST_NOT_AUTHORIZED))
|
|
|
|
result = self.client._check_for_cluster_credentials(self=self.client)
|
|
|
|
self.assertFalse(result)
|
|
|
|
def test__check_for_cluster_credentials_api_error(self):
|
|
self.client._check_for_cluster_credentials = (
|
|
self.original_check_for_cluster_credentials)
|
|
self.mock_object(self.client, 'list_cluster_nodes',
|
|
self._mock_api_error())
|
|
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client._check_for_cluster_credentials,
|
|
self.client)
|
|
|
|
def test_get_licenses(self):
|
|
return_value = fake.FAKE_GET_LICENSES_REST
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
test_result = self.client.get_licenses()
|
|
|
|
expected_result = sorted(
|
|
[license['name'] for license in return_value.get('records', [])])
|
|
|
|
self.assertEqual(test_result, expected_result)
|
|
|
|
@ddt.data(((9, 1, 0), fake.VERSION_NO_DARE), ((8, 3, 2), fake.VERSION))
|
|
@ddt.unpack
|
|
def test_is_nve_supported_unsupported_release_or_platform(self, gen, ver):
|
|
system_version = {'version-tuple': gen, 'version': ver}
|
|
self.mock_object(self.client,
|
|
'get_ontap_version',
|
|
mock.Mock(return_value=system_version))
|
|
self.mock_object(self.client,
|
|
'_get_security_key_manager_nve_support',
|
|
mock.Mock(return_value=False))
|
|
self.mock_object(self.client,
|
|
'list_cluster_nodes',
|
|
mock.Mock(return_value=fake.NODE_NAMES))
|
|
|
|
result = self.client.is_nve_supported()
|
|
|
|
self.assertFalse(result)
|
|
|
|
def test_is_nve_supported_valid_platform_and_supported_release(self):
|
|
|
|
system_version = {
|
|
'version-tuple': (9, 1, 0),
|
|
'version': fake.VERSION,
|
|
}
|
|
self.mock_object(self.client,
|
|
'get_ontap_version',
|
|
mock.Mock(return_value=system_version))
|
|
self.mock_object(self.client,
|
|
'_get_security_key_manager_nve_support',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client,
|
|
'list_cluster_nodes',
|
|
mock.Mock(return_value=fake.NODE_NAMES))
|
|
|
|
result = self.client.is_nve_supported()
|
|
self.assertTrue(result)
|
|
|
|
def test_is_nve_supported_key_manager_not_enabled(self):
|
|
|
|
system_version = {
|
|
'version-tuple': (9, 1, 0),
|
|
'version': fake.VERSION,
|
|
}
|
|
self.mock_object(self.client,
|
|
'get_ontap_version',
|
|
mock.Mock(return_value=system_version))
|
|
self.mock_object(self.client,
|
|
'_get_security_key_manager_nve_support',
|
|
mock.Mock(return_value=False))
|
|
self.mock_object(self.client,
|
|
'list_cluster_nodes',
|
|
mock.Mock(return_value=fake.NODE_NAMES))
|
|
|
|
result = self.client.is_nve_supported()
|
|
|
|
self.assertFalse(result)
|
|
|
|
def test__get_volume_by_args(self):
|
|
response = fake.VOLUME_LIST_SIMPLE_RESPONSE_REST
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
|
|
result = self.client._get_volume_by_args(
|
|
vol_name=fake.VOLUME_NAMES[0],
|
|
aggregate_name=fake.SHARE_AGGREGATE_NAME,
|
|
vol_path=fake.VOLUME_JUNCTION_PATH,
|
|
vserver=fake.VSERVER_NAME,
|
|
fields='name,style,svm.name,svm.uuid')
|
|
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0],
|
|
'aggregates.name': fake.SHARE_AGGREGATE_NAME,
|
|
'nas.path': fake.VOLUME_JUNCTION_PATH,
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'style': 'flex*', # Match both 'flexvol' and 'flexgroup'
|
|
'error_state.is_inconsistent': 'false',
|
|
'fields': 'name,style,svm.name,svm.uuid'
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes/', 'get', query=query)
|
|
|
|
self.assertEqual(volume, result)
|
|
|
|
def test_restore_snapshot(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST["uuid"]
|
|
body = {
|
|
'restore_to.snapshot.name': fake.SNAPSHOT_NAME
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.restore_snapshot(fake.VOLUME_NAMES[0], fake.SNAPSHOT_NAME)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
|
|
@ddt.data(0, 10)
|
|
def test__has_records(self, num_records):
|
|
result = self.client._has_records({'num_records': num_records})
|
|
|
|
if not num_records or num_records == 0:
|
|
self.assertFalse(result)
|
|
else:
|
|
self.assertTrue(result)
|
|
|
|
def test_vserver_exists(self):
|
|
query = {
|
|
'name': fake.VSERVER_NAME
|
|
}
|
|
return_value = fake.SVMS_LIST_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
|
|
result = self.client.vserver_exists(fake.VSERVER_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/svm/svms', 'get', query=query, enable_tunneling=False)
|
|
self.client._has_records.assert_called_once_with(
|
|
fake.SVMS_LIST_SIMPLE_RESPONSE_REST)
|
|
|
|
self.assertEqual(result, True)
|
|
|
|
def test_get_aggregate(self):
|
|
|
|
response = fake.AGGR_GET_ITER_RESPONSE_REST['records']
|
|
self.mock_object(self.client,
|
|
'_get_aggregates',
|
|
mock.Mock(return_value=response))
|
|
|
|
result = self.client.get_aggregate(fake.SHARE_AGGREGATE_NAME)
|
|
|
|
fields = ('name,block_storage.primary.raid_type,'
|
|
'block_storage.storage_type')
|
|
|
|
self.client._get_aggregates.assert_has_calls([
|
|
mock.call(
|
|
aggregate_names=[fake.SHARE_AGGREGATE_NAME],
|
|
fields=fields)])
|
|
|
|
expected = {
|
|
'name': fake.SHARE_AGGREGATE_NAME,
|
|
'raid-type': response[0]['block_storage']['primary']['raid_type'],
|
|
'is-hybrid':
|
|
response[0]['block_storage']['storage_type'] == 'hybrid',
|
|
}
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_cluster_aggregate_capacities(self):
|
|
|
|
response = fake.AGGR_GET_ITER_RESPONSE_REST['records']
|
|
self.mock_object(self.client,
|
|
'_get_aggregates',
|
|
mock.Mock(return_value=response))
|
|
|
|
result = self.client.get_cluster_aggregate_capacities(
|
|
response)
|
|
|
|
fields = 'name,space'
|
|
self.client._get_aggregates.assert_has_calls([
|
|
mock.call(
|
|
aggregate_names=response,
|
|
fields=fields)])
|
|
expected = {
|
|
response[0]['name']: {
|
|
'available': 568692293632,
|
|
'total': 1271819509760,
|
|
'used': 703127216128,
|
|
},
|
|
response[1]['name']: {
|
|
'available': 727211110400,
|
|
'total': 1426876227584,
|
|
'used': 699665117184,
|
|
}
|
|
}
|
|
|
|
self.assertDictEqual(expected, result)
|
|
|
|
def test_list_non_root_aggregates(self):
|
|
return_value = fake.FAKE_AGGR_LIST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.list_non_root_aggregates()
|
|
|
|
expected = [fake.SHARE_AGGREGATE_NAMES_LIST[0]]
|
|
self.assertEqual(expected, result)
|
|
|
|
def test__get_aggregates(self):
|
|
|
|
api_response = fake.AGGR_GET_ITER_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result = self.client._get_aggregates(
|
|
aggregate_names=fake.SHARE_AGGREGATE_NAMES)
|
|
expected = fake.AGGR_GET_ITER_RESPONSE_REST['records']
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_node_for_aggregate(self):
|
|
|
|
response = fake.AGGR_GET_ITER_RESPONSE_REST['records']
|
|
self.mock_object(self.client,
|
|
'_get_aggregates',
|
|
mock.Mock(return_value=response))
|
|
result = self.client.get_node_for_aggregate(fake.SHARE_AGGREGATE_NAME)
|
|
expected = 'fake_home_node_name'
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data({'types': {'FCAL'}, 'expected': ['FCAL']},
|
|
{'types': {'SATA', 'SSD'}, 'expected': ['SATA', 'SSD']},)
|
|
@ddt.unpack
|
|
def test_get_aggregate_disk_types(self, types, expected):
|
|
|
|
mock_get_aggregate_disk_types = self.mock_object(
|
|
self.client, '_get_aggregate_disk_types',
|
|
mock.Mock(return_value=types))
|
|
|
|
result = self.client.get_aggregate_disk_types(
|
|
fake.SHARE_AGGREGATE_NAME)
|
|
|
|
self.assertEqual(sorted(expected), sorted(result))
|
|
mock_get_aggregate_disk_types.assert_called_once_with(
|
|
fake.SHARE_AGGREGATE_NAME)
|
|
|
|
def test_volume_exists(self):
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0]
|
|
}
|
|
return_value = fake.VOLUME_LIST_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
|
|
result = self.client.volume_exists(fake.VOLUME_NAMES[0])
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes', 'get', query=query)
|
|
self.client._has_records.assert_called_once_with(
|
|
fake.VOLUME_LIST_SIMPLE_RESPONSE_REST)
|
|
self.assertEqual(result, True)
|
|
|
|
def test_list_vserver_aggregates(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_vserver_aggregate_capacities',
|
|
mock.Mock(return_value=fake.VSERVER_AGGREGATES))
|
|
|
|
result = self.client.list_vserver_aggregates()
|
|
|
|
self.assertListEqual(list(fake.VSERVER_AGGREGATES.keys()), result)
|
|
|
|
def test_list_vserver_aggregates_none_found(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_vserver_aggregate_capacities',
|
|
mock.Mock(return_value={}))
|
|
|
|
result = self.client.list_vserver_aggregates()
|
|
|
|
self.assertListEqual([], result)
|
|
|
|
def test_get_vserver_aggregate_capacities(self):
|
|
|
|
response = fake.FAKE_SVM_AGGREGATES
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=response))
|
|
|
|
result = self.client.get_vserver_aggregate_capacities(
|
|
fake.SHARE_AGGREGATE_NAMES_LIST)
|
|
|
|
query = {
|
|
'fields': 'name,aggregates.name,aggregates.available_size'
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get', query=query)])
|
|
|
|
expected = {
|
|
response['records'][0].get('aggregates')[0].get('name'): {
|
|
'available': 568692293632,
|
|
},
|
|
response['records'][0].get('aggregates')[1].get('name'): {
|
|
'available': 727211110400,
|
|
}
|
|
}
|
|
|
|
self.assertDictEqual(expected, result)
|
|
|
|
def test_get_vserver_aggregate_capacities_partial_request(self):
|
|
response = fake.FAKE_SVM_AGGREGATES
|
|
size = response['records'][0].get('aggregates')[0].get(
|
|
'available_size')
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=response))
|
|
|
|
result = self.client.get_vserver_aggregate_capacities(
|
|
[fake.SHARE_AGGREGATE_NAMES[0]])
|
|
|
|
expected = {
|
|
fake.SHARE_AGGREGATE_NAMES[0]: {
|
|
'available': size
|
|
}
|
|
}
|
|
self.assertDictEqual(expected, result)
|
|
|
|
def test_get_vserver_aggregate_capacities_aggregate_not_found(self):
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=fake.FAKE_SVM_AGGR_EMPTY))
|
|
|
|
result = self.client.get_vserver_aggregate_capacities(
|
|
['other-aggr'])
|
|
|
|
self.assertDictEqual({}, result)
|
|
self.assertEqual(1, client_cmode_rest.LOG.warning.call_count)
|
|
|
|
def test_get_vserver_aggregate_capacities_none_requested(self):
|
|
result = self.client.get_vserver_aggregate_capacities([])
|
|
self.assertEqual({}, result)
|
|
|
|
@ddt.data(None, fake.QOS_MAX_THROUGHPUT, fake.QOS_MAX_THROUGHPUT_IOPS)
|
|
def test_qos_policy_group_create(self, max_throughput):
|
|
return_value = fake.GENERIC_JOB_POST_RESPONSE
|
|
body = {
|
|
'name': fake.QOS_POLICY_GROUP_NAME,
|
|
'svm.name': fake.VSERVER_NAME,
|
|
}
|
|
if max_throughput:
|
|
if 'iops' in max_throughput:
|
|
qos = fake.QOS_MAX_THROUGHPUT_IOPS_NO_UNIT
|
|
body['fixed.max_throughput_iops'] = qos
|
|
else:
|
|
qos = math.ceil(fake.QOS_MAX_THROUGHPUT_NO_UNIT / units.Mi)
|
|
body['fixed.max_throughput_mbps'] = qos
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
if max_throughput:
|
|
result = self.client.qos_policy_group_create(
|
|
fake.QOS_POLICY_GROUP_NAME, fake.VSERVER_NAME,
|
|
max_throughput)
|
|
else:
|
|
result = self.client.qos_policy_group_create(
|
|
fake.QOS_POLICY_GROUP_NAME, fake.VSERVER_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/qos/policies', 'post', body=body)
|
|
self.assertEqual(result, return_value)
|
|
|
|
@ddt.data(None, ['CIFS', 'NFS'])
|
|
def test_get_network_interfaces(self, protocols):
|
|
return_value = fake.GENERIC_NETWORK_INTERFACES_GET_REPONSE
|
|
|
|
lif_info = return_value.get('records', [])[0]
|
|
|
|
fake_lif = [{
|
|
'uuid': lif_info['uuid'],
|
|
'address': lif_info['ip']['address'],
|
|
'home-node': lif_info['location']['home_node']['name'],
|
|
'home-port': lif_info['location']['home_port']['name'],
|
|
'interface-name': lif_info['name'],
|
|
'netmask': lif_info['ip']['netmask'],
|
|
'role': lif_info['services'],
|
|
'vserver': lif_info['svm']['name'],
|
|
}]
|
|
|
|
if protocols:
|
|
query = {
|
|
'services': 'data_cifs,data_nfs',
|
|
'fields': 'ip.address,location.home_node.name,'
|
|
'location.home_port.name,ip.netmask,'
|
|
'services,svm.name'
|
|
}
|
|
else:
|
|
query = {
|
|
'fields': 'ip.address,location.home_node.name,'
|
|
'location.home_port.name,ip.netmask,'
|
|
'services,svm.name'
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_network_interfaces(protocols)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/interfaces', 'get', query=query)
|
|
|
|
self.assertEqual(result, fake_lif)
|
|
|
|
def test_clear_nfs_export_policy_for_volume(self):
|
|
|
|
mock_set_nfs_export_policy_for_volume = self.mock_object(
|
|
self.client, 'set_nfs_export_policy_for_volume')
|
|
|
|
self.client.clear_nfs_export_policy_for_volume(fake.SHARE_NAME)
|
|
|
|
mock_set_nfs_export_policy_for_volume.assert_called_once_with(
|
|
fake.SHARE_NAME, 'default')
|
|
|
|
def test_set_nfs_export_policy_for_volume(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
query = {'name': volume['name']}
|
|
|
|
body = {
|
|
'nas.export_policy.name': fake.EXPORT_POLICY_NAME
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.set_nfs_export_policy_for_volume(
|
|
fake.VOLUME_NAMES[0], fake.EXPORT_POLICY_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes/', 'patch',
|
|
query=query, body=body)
|
|
|
|
def test_create_nfs_export_policy(self):
|
|
|
|
body = {'name': fake.EXPORT_POLICY_NAME}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_nfs_export_policy(fake.EXPORT_POLICY_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/nfs/export-policies', 'post', body=body)
|
|
|
|
def test_soft_delete_nfs_export_policy(self):
|
|
self.mock_object(self.client, 'delete_nfs_export_policy',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.mock_object(self.client, 'rename_nfs_export_policy')
|
|
|
|
self.client.soft_delete_nfs_export_policy(fake.EXPORT_POLICY_NAME)
|
|
|
|
self.client.rename_nfs_export_policy.assert_has_calls([
|
|
mock.call(
|
|
fake.EXPORT_POLICY_NAME,
|
|
'deleted_manila_' + fake.EXPORT_POLICY_NAME)])
|
|
|
|
def test_rename_nfs_export_policy(self):
|
|
return_uuid = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
uuid = "fake-policy-uuid"
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_uuid))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
|
|
body = {
|
|
'name': 'fake_new_policy_name'
|
|
}
|
|
|
|
self.client.rename_nfs_export_policy(fake.EXPORT_POLICY_NAME,
|
|
'fake_new_policy_name')
|
|
|
|
self.client._has_records.assert_called_once_with(return_uuid)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/nfs/export-policies', 'get',
|
|
query={'name': fake.EXPORT_POLICY_NAME}),
|
|
mock.call(f'/protocols/nfs/export-policies/{uuid}', 'patch',
|
|
body=body)])
|
|
|
|
def test_get_volume_junction_path(self):
|
|
return_value = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
|
|
query = {
|
|
'name': fake.SHARE_NAME,
|
|
'fields': 'nas.path'
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_volume_junction_path(fake.SHARE_NAME)
|
|
|
|
expected = fake.VOLUME_JUNCTION_PATH
|
|
|
|
self.client.send_request.assert_called_once_with('/storage/volumes/',
|
|
'get', query=query)
|
|
|
|
self.assertEqual(result, expected)
|
|
|
|
def test_get_volume(self):
|
|
return_value = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
|
|
fake_volume = return_value.get('records', [])[0]
|
|
|
|
expected = {
|
|
'aggregate': fake.SHARE_AGGREGATE_NAME,
|
|
'aggr-list': [fake.SHARE_AGGREGATE_NAME],
|
|
'junction-path': fake_volume.get('nas', {}).get('path', ''),
|
|
'name': fake_volume.get('name', ''),
|
|
'owning-vserver-name': fake_volume.get('svm', {}).get('name', ''),
|
|
'type': fake_volume.get('type', ''),
|
|
'style': fake_volume.get('style', ''),
|
|
'size': fake_volume.get('space', {}).get('size', ''),
|
|
'qos-policy-group-name': fake_volume.get('qos', {})
|
|
.get('policy', {})
|
|
.get('name'),
|
|
'style-extended': fake_volume.get('style', '')
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
|
|
result = self.client.get_volume(fake.SHARE_NAME)
|
|
|
|
self.client._has_records.assert_called_once_with(return_value)
|
|
self.assertEqual(result, expected)
|
|
|
|
def test_cifs_share_exists(self):
|
|
return_value = fake.VOLUME_LIST_SIMPLE_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
|
|
result = self.client.cifs_share_exists(fake.SHARE_NAME)
|
|
|
|
query = {
|
|
'name': fake.SHARE_NAME,
|
|
'path': fake.VOLUME_JUNCTION_PATH
|
|
}
|
|
self.client._has_records.assert_called_once_with(return_value)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/cifs/shares', 'get', query=query)
|
|
self.assertTrue(result)
|
|
|
|
def test_create_cifs_share(self):
|
|
|
|
body = {
|
|
'name': fake.SHARE_NAME,
|
|
'path': fake.VOLUME_JUNCTION_PATH,
|
|
'svm.name': self.client.vserver,
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_cifs_share(fake.SHARE_NAME, f'/{fake.SHARE_NAME}')
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/cifs/shares', 'post', body=body)
|
|
|
|
@ddt.data(None, 'fake_security_style')
|
|
def test_set_volume_security_style(self, security_style):
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
if security_style:
|
|
self.client.set_volume_security_style(fake.VOLUME_NAMES[0],
|
|
security_style)
|
|
else:
|
|
self.client.set_volume_security_style(fake.VOLUME_NAMES[0])
|
|
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0],
|
|
}
|
|
body = {
|
|
'nas.security_style': security_style if security_style else 'unix'
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes', 'patch', body=body, query=query)
|
|
|
|
def test_remove_cifs_share_access(self):
|
|
return_uuid = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_uuid))
|
|
|
|
self.client.remove_cifs_share_access(fake.SHARE_NAME, fake.USER_NAME)
|
|
|
|
fake_uuid = "fake_uuid"
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/cifs/shares', 'get',
|
|
query={'name': fake.SHARE_NAME, 'fields': 'svm.uuid'}),
|
|
mock.call(f'/protocols/cifs/shares/{fake_uuid}/{fake.SHARE_NAME}/'
|
|
f'acls/{fake.USER_NAME}/windows', 'delete')])
|
|
|
|
def test_create_volume(self):
|
|
|
|
mock_create_volume_async = self.mock_object(self.client,
|
|
'create_volume_async')
|
|
mock_update = self.mock_object(
|
|
self.client, 'update_volume_efficiency_attributes')
|
|
mock_max_files = self.mock_object(self.client, 'set_volume_max_files')
|
|
self.client.create_volume(fake.SHARE_AGGREGATE_NAME,
|
|
fake.VOLUME_NAMES[0], fake.SHARE_SIZE,
|
|
max_files=1)
|
|
|
|
mock_create_volume_async.assert_called_once_with(
|
|
[fake.SHARE_AGGREGATE_NAME], fake.VOLUME_NAMES[0], fake.SHARE_SIZE,
|
|
is_flexgroup=False, thin_provisioned=False, snapshot_policy=None,
|
|
language=None, max_files=1, snapshot_reserve=None,
|
|
volume_type='rw', qos_policy_group=None, encrypt=False,
|
|
adaptive_qos_policy_group=None)
|
|
mock_update.assert_called_once_with(fake.VOLUME_NAMES[0], False, False)
|
|
mock_max_files.assert_called_once_with(fake.VOLUME_NAMES[0], 1)
|
|
|
|
def test_create_volume_async(self):
|
|
body = {
|
|
'size': 1073741824,
|
|
'name': fake.VOLUME_NAMES[0],
|
|
'style': 'flexvol',
|
|
'aggregates': [{'name': fake.SHARE_AGGREGATE_NAME}]
|
|
}
|
|
|
|
return_value = fake.GENERIC_JOB_POST_RESPONSE
|
|
|
|
expected_result = {
|
|
'jobid': fake.GENERIC_JOB_POST_RESPONSE['job']['uuid'],
|
|
'error-code': '',
|
|
'error-message': '',
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_create_volume_body',
|
|
mock.Mock(return_value={}))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.create_volume_async([
|
|
fake.SHARE_AGGREGATE_NAME], fake.VOLUME_NAMES[0], 1,
|
|
is_flexgroup=False)
|
|
|
|
self.client._get_create_volume_body.assert_called_once_with(
|
|
fake.VOLUME_NAMES[0], False, None, None, None, 'rw', None, False,
|
|
None)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes', 'post', body=body, wait_on_accepted=True)
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_get_volume_efficiency_status(self):
|
|
return_value = fake.VOLUME_LIST_SIMPLE_RESPONSE_REST
|
|
|
|
query = {
|
|
'efficiency.volume_path': '/vol/%s' % fake.VOLUME_NAMES[0],
|
|
'fields': 'efficiency.state,efficiency.compression'
|
|
}
|
|
|
|
expected_result = {
|
|
'dedupe': True,
|
|
'compression': True
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_volume_efficiency_status(fake.VOLUME_NAMES[0])
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes', 'get', query=query)
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_enable_dedupe_async(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
body = {
|
|
'efficiency': {'dedupe': 'background'}
|
|
}
|
|
|
|
self.client.enable_dedupe_async(fake.VOLUME_NAMES[0])
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
def test_disable_dedupe_async(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
body = {
|
|
'efficiency': {'dedupe': 'none'}
|
|
}
|
|
|
|
self.client.disable_dedupe_async(fake.VOLUME_NAMES[0])
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
def test_enable_compression_async(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
body = {
|
|
'efficiency': {'compression': 'background'}
|
|
}
|
|
|
|
self.client.enable_compression_async(fake.VOLUME_NAMES[0])
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
def test_disable_compression_async(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
body = {
|
|
'efficiency': {'compression': 'none'}
|
|
}
|
|
|
|
self.client.disable_compression_async(fake.VOLUME_NAMES[0])
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
def test_set_volume_max_files(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
fake_max_files = '40000'
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
body = {
|
|
'files.maximum': int(fake_max_files)
|
|
}
|
|
|
|
self.client.set_volume_max_files(fake.VOLUME_NAMES[0], fake_max_files)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
def test_set_volume_snapdir_access(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
fake_hide_snapdir = 'fake-snapdir'
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
body = {
|
|
'snapshot_directory_access_enabled': str(
|
|
not fake_hide_snapdir).lower()
|
|
}
|
|
|
|
self.client.set_volume_snapdir_access(fake.VOLUME_NAMES[0],
|
|
fake_hide_snapdir)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
def test_get_fpolicy_scopes(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.GENERIC_FPOLICY_RESPONSE
|
|
|
|
query = {
|
|
'name': fake.FPOLICY_POLICY_NAME,
|
|
'scope.include_shares': fake.VOLUME_NAMES[0],
|
|
'scope.include_extension': fake.FPOLICY_EXT_TO_INCLUDE,
|
|
'scope.exclude_extension': fake.FPOLICY_EXT_TO_EXCLUDE
|
|
}
|
|
|
|
expected_result = [
|
|
{
|
|
'policy-name': fake.FPOLICY_POLICY_NAME,
|
|
'file-extensions-to-include': fake.FPOLICY_EXT_TO_INCLUDE_LIST,
|
|
'file-extensions-to-exclude': fake.FPOLICY_EXT_TO_EXCLUDE_LIST,
|
|
'shares-to-include': [fake.VOLUME_NAMES[0]],
|
|
}
|
|
]
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_fpolicy_scopes(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_POLICY_NAME,
|
|
fake.FPOLICY_EXT_TO_INCLUDE_LIST, fake.FPOLICY_EXT_TO_EXCLUDE_LIST,
|
|
[fake.VOLUME_NAMES[0]])
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/policies', 'get', query=query)
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_get_fpolicy_policies_status(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.GENERIC_FPOLICY_RESPONSE
|
|
|
|
query = {
|
|
'name': fake.FPOLICY_POLICY_NAME,
|
|
'enabled': 'true'
|
|
}
|
|
|
|
expected_result = [
|
|
{
|
|
'policy-name': fake.FPOLICY_POLICY_NAME,
|
|
'status': True,
|
|
'sequence-number': 1
|
|
}
|
|
]
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_fpolicy_policies_status(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_POLICY_NAME, 'true')
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/policies', 'get', query=query)
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_get_fpolicy_policies(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.GENERIC_FPOLICY_RESPONSE
|
|
|
|
query = {
|
|
'name': fake.FPOLICY_POLICY_NAME,
|
|
'engine.name': 'native',
|
|
'events': fake.FPOLICY_EVENT_NAME
|
|
}
|
|
|
|
expected_result = [
|
|
{
|
|
'policy-name': fake.FPOLICY_POLICY_NAME,
|
|
'engine-name': 'native',
|
|
'events': [fake.FPOLICY_EVENT_NAME]
|
|
}
|
|
]
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_fpolicy_policies(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_POLICY_NAME, 'native',
|
|
[fake.FPOLICY_EVENT_NAME])
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/policies', 'get', query=query)
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_get_fpolicy_events(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
return_value = fake.GENERIC_FPOLICY_EVENTS_RESPONSE
|
|
|
|
query = {
|
|
'name': fake.FPOLICY_EVENT_NAME,
|
|
'protocol': fake.FPOLICY_PROTOCOL,
|
|
'fields': 'file_operations.create,file_operations.write,'
|
|
'file_operations.rename'
|
|
}
|
|
|
|
expected_result = [
|
|
{
|
|
'event-name': fake.FPOLICY_EVENT_NAME,
|
|
'protocol': fake.FPOLICY_PROTOCOL,
|
|
'file-operations': fake.FPOLICY_FILE_OPERATIONS_LIST
|
|
}
|
|
]
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.get_fpolicy_events(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_EVENT_NAME,
|
|
fake.FPOLICY_PROTOCOL, fake.FPOLICY_FILE_OPERATIONS_LIST)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/events', 'get', query=query)
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_create_fpolicy_event(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
|
|
body = {
|
|
'name': fake.FPOLICY_EVENT_NAME,
|
|
'protocol': fake.FPOLICY_PROTOCOL,
|
|
'file_operations.create': 'true',
|
|
'file_operations.write': 'true',
|
|
'file_operations.rename': 'true'
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_fpolicy_event(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_EVENT_NAME,
|
|
fake.FPOLICY_PROTOCOL, fake.FPOLICY_FILE_OPERATIONS_LIST)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/events', 'post', body=body)
|
|
|
|
def test_delete_fpolicy_policy(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.delete_fpolicy_policy(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_POLICY_NAME)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/policies/{fake.FPOLICY_POLICY_NAME}',
|
|
'delete')
|
|
|
|
def test_delete_fpolicy_event(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.delete_fpolicy_event(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_EVENT_NAME)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/events/{fake.FPOLICY_EVENT_NAME}',
|
|
'delete')
|
|
|
|
def test_enable_fpolicy_policy(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
|
|
body = {
|
|
'priority': 1,
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.enable_fpolicy_policy(
|
|
fake.VOLUME_NAMES[0], fake.FPOLICY_POLICY_NAME, 1)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/policies/{fake.FPOLICY_POLICY_NAME}',
|
|
'patch', body=body)
|
|
|
|
def test_create_fpolicy_policy_with_scope(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
|
|
body = {
|
|
'name': fake.FPOLICY_POLICY_NAME,
|
|
'events.name': fake.FPOLICY_EVENT_NAME,
|
|
'engine.name': fake.FPOLICY_ENGINE,
|
|
'scope.include_shares': [fake.VOLUME_NAMES[0]],
|
|
'scope.include_extension': fake.FPOLICY_EXT_TO_INCLUDE_LIST,
|
|
'scope.exclude_extension': fake.FPOLICY_EXT_TO_EXCLUDE_LIST
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_fpolicy_policy_with_scope(
|
|
fake.FPOLICY_POLICY_NAME, fake.VOLUME_NAMES[0],
|
|
fake.FPOLICY_EVENT_NAME, fake.FPOLICY_ENGINE,
|
|
extensions_to_include=fake.FPOLICY_EXT_TO_INCLUDE,
|
|
extensions_to_exclude=fake.FPOLICY_EXT_TO_EXCLUDE)
|
|
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/fpolicy/{uuid}/policies', 'post', body=body)
|
|
|
|
def test_delete_nfs_export_policy(self):
|
|
policy_name = 'fake_policy_name'
|
|
|
|
query = {
|
|
'name': policy_name,
|
|
}
|
|
|
|
api_response = fake.EXPORT_POLICY_REST
|
|
|
|
mock_sr = self.mock_object(self.client, 'send_request', mock.Mock(
|
|
return_value=api_response))
|
|
|
|
if not api_response.get('records'):
|
|
return
|
|
id = api_response.get('records')[0]['id']
|
|
|
|
self.client.delete_nfs_export_policy(policy_name)
|
|
|
|
mock_sr.assert_has_calls([
|
|
mock.call('/protocols/nfs/export-policies', 'get',
|
|
query=query),
|
|
mock.call(f'/protocols/nfs/export-policies/{id}', 'delete'),
|
|
])
|
|
|
|
def test_delete_volume(self):
|
|
"""Deletes a volume."""
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
# Get volume UUID.
|
|
uuid = volume['uuid']
|
|
|
|
self.client.delete_volume('fake_volume_name')
|
|
|
|
mock_sr.assert_called_once_with(f'/storage/volumes/{uuid}', 'delete')
|
|
|
|
def test__unmount_volume(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_send_request = self.mock_object(self.client, 'send_request')
|
|
uuid = volume['uuid']
|
|
|
|
# Unmount volume async operation.
|
|
body = {"nas": {"path": ""}}
|
|
|
|
self.client._unmount_volume('fake_volume_name')
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
|
|
def test_offline_volume(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_send_request = self.mock_object(self.client, 'send_request')
|
|
uuid = volume['uuid']
|
|
|
|
body = {'state': 'offline'}
|
|
self.client.offline_volume('fake_volume_name')
|
|
mock_send_request.assert_called_once_with(f'/storage/volumes/{uuid}',
|
|
'patch', body=body)
|
|
|
|
def test_qos_policy_group_rename(self):
|
|
"""Renames a QoS policy group."""
|
|
|
|
qos_policy_group_name = 'extreme'
|
|
new_name = 'new_name'
|
|
res = fake.QOS_POLICY_GROUP_REST
|
|
mock_send_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=res))
|
|
query = {
|
|
'name': qos_policy_group_name,
|
|
'fields': 'uuid',
|
|
}
|
|
uuid = res.get('records')[0]['uuid']
|
|
body = {"name": new_name}
|
|
|
|
self.client.qos_policy_group_rename(qos_policy_group_name, new_name)
|
|
|
|
mock_send_request.assert_has_calls([
|
|
mock.call('/storage/qos/policies', 'get', query=query),
|
|
mock.call(f'/storage/qos/policies/{uuid}', 'patch',
|
|
body=body),
|
|
])
|
|
|
|
def test_qos_policy_group_get(self):
|
|
qos_policy_group_name = 'extreme'
|
|
qos_policy_group = fake.QOS_POLICY_GROUP_REST
|
|
qos_policy = qos_policy_group.get('records')[0]
|
|
max_throughput = qos_policy.get('fixed',
|
|
{}).get('max_throughput_iops')
|
|
|
|
expected = {
|
|
'policy-group': qos_policy.get('name'),
|
|
'vserver': qos_policy.get('svm', {}).get('name'),
|
|
'max-throughput': max_throughput if max_throughput else None,
|
|
'num-workloads': int(qos_policy.get('object_count')),
|
|
}
|
|
|
|
query = {
|
|
'name': qos_policy_group_name,
|
|
'fields': 'name,object_count,fixed.max_throughput_iops,' +
|
|
'fixed.max_throughput_mbps,svm.name'
|
|
}
|
|
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=qos_policy_group))
|
|
|
|
result = self.client.qos_policy_group_get(qos_policy_group_name)
|
|
mock_sr.assert_called_once_with('/storage/qos/policies', 'get',
|
|
query=query)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_remove_unused_qos_policy_groups(self):
|
|
|
|
result = fake.QOS_POLICY_GROUP_REST
|
|
|
|
query = {
|
|
'name': '%s*' % client_cmode_rest.DELETED_PREFIX,
|
|
'fields': 'uuid,name',
|
|
}
|
|
|
|
mock_send_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=result))
|
|
|
|
res = result.get('records')
|
|
for record in res:
|
|
uuid = record['uuid']
|
|
|
|
self.client.remove_unused_qos_policy_groups()
|
|
|
|
mock_send_request.assert_has_calls([
|
|
mock.call('/storage/qos/policies', 'get', query=query),
|
|
mock.call(f'/storage/qos/policies/{uuid}', 'delete')])
|
|
|
|
def test_unmount_volume(self):
|
|
|
|
self.mock_object(self.client, '_unmount_volume')
|
|
|
|
self.client.unmount_volume(fake.SHARE_NAME)
|
|
|
|
self.client._unmount_volume.assert_called_once_with(fake.SHARE_NAME)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
self.assertEqual(0, client_cmode_rest.LOG.warning.call_count)
|
|
|
|
def test_unmount_volume_api_error(self):
|
|
|
|
self.mock_object(self.client,
|
|
'_unmount_volume',
|
|
self._mock_api_error())
|
|
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.unmount_volume,
|
|
fake.SHARE_NAME)
|
|
|
|
self.assertEqual(1, self.client._unmount_volume.call_count)
|
|
self.assertEqual(0, client_cmode_rest.LOG.debug.call_count)
|
|
self.assertEqual(0, client_cmode_rest.LOG.warning.call_count)
|
|
|
|
def test_unmount_volume_with_retries(self):
|
|
return_code = netapp_api.EREST_UNMOUNT_FAILED_LOCK
|
|
side_effect = [netapp_api.api.NaApiError(code=return_code,
|
|
message='...job ID...')] * 5
|
|
side_effect.append(None)
|
|
self.mock_object(self.client,
|
|
'_unmount_volume',
|
|
mock.Mock(side_effect=side_effect))
|
|
self.mock_object(time, 'sleep')
|
|
|
|
self.client.unmount_volume(fake.SHARE_NAME)
|
|
|
|
self.assertEqual(6, self.client._unmount_volume.call_count)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
self.assertEqual(5, client_cmode_rest.LOG.warning.call_count)
|
|
|
|
def test_unmount_volume_with_max_retries(self):
|
|
return_code = netapp_api.EREST_UNMOUNT_FAILED_LOCK
|
|
side_effect = [netapp_api.api.NaApiError(code=return_code,
|
|
message='...job ID...')] * 30
|
|
self.mock_object(self.client,
|
|
'_unmount_volume',
|
|
mock.Mock(side_effect=side_effect))
|
|
self.mock_object(time, 'sleep')
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.unmount_volume,
|
|
fake.SHARE_NAME)
|
|
|
|
self.assertEqual(10, self.client._unmount_volume.call_count)
|
|
self.assertEqual(0, client_cmode_rest.LOG.debug.call_count)
|
|
self.assertEqual(10, client_cmode_rest.LOG.warning.call_count)
|
|
|
|
def test_qos_policy_group_exists(self):
|
|
mock = self.mock_object(self.client, 'qos_policy_group_get')
|
|
response = self.client.qos_policy_group_exists('extreme')
|
|
mock.assert_called_once_with('extreme')
|
|
self.assertTrue(response)
|
|
|
|
def test_mark_qos_policy_group_for_deletion_rename_failure(self):
|
|
self.mock_object(self.client, 'qos_policy_group_exists',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, 'qos_policy_group_rename',
|
|
mock.Mock(side_effect=netapp_api.api.NaApiError))
|
|
self.mock_object(client_cmode_rest.LOG, 'warning')
|
|
self.mock_object(self.client, 'remove_unused_qos_policy_groups')
|
|
|
|
retval = self.client.mark_qos_policy_group_for_deletion(
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
|
|
self.assertIsNone(retval)
|
|
client_cmode_rest.LOG.warning.assert_called_once()
|
|
self.client.qos_policy_group_exists.assert_called_once_with(
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
self.client.qos_policy_group_rename.assert_called_once_with(
|
|
fake.QOS_POLICY_GROUP_NAME,
|
|
client_cmode_rest.DELETED_PREFIX + fake.QOS_POLICY_GROUP_NAME)
|
|
self.client.remove_unused_qos_policy_groups.assert_called_once_with()
|
|
|
|
@ddt.data(True, False)
|
|
def test_mark_qos_policy_group_for_deletion_policy_exists(self, exists):
|
|
self.mock_object(self.client, 'qos_policy_group_exists',
|
|
mock.Mock(return_value=exists))
|
|
self.mock_object(self.client, 'qos_policy_group_rename')
|
|
mock_remove_unused_policies = self.mock_object(
|
|
self.client, 'remove_unused_qos_policy_groups')
|
|
self.mock_object(client_cmode_rest.LOG, 'warning')
|
|
|
|
retval = self.client.mark_qos_policy_group_for_deletion(
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
|
|
self.assertIsNone(retval)
|
|
|
|
if exists:
|
|
self.client.qos_policy_group_rename.assert_called_once_with(
|
|
fake.QOS_POLICY_GROUP_NAME,
|
|
client_cmode_rest.DELETED_PREFIX + fake.QOS_POLICY_GROUP_NAME)
|
|
mock_remove_unused_policies.assert_called_once_with()
|
|
else:
|
|
self.assertFalse(self.client.qos_policy_group_rename.called)
|
|
self.assertFalse(
|
|
self.client.remove_unused_qos_policy_groups.called)
|
|
self.assertFalse(client_cmode_rest.LOG.warning.called)
|
|
|
|
def test_set_volume_size(self):
|
|
unique_volume_return = {'uuid': 'fake_uuid'}
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=unique_volume_return))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client.set_volume_size('fake_name', 1)
|
|
|
|
body = {
|
|
'space.size': 1 * units.Gi
|
|
}
|
|
mock_sr.assert_called_once_with(
|
|
'/storage/volumes/fake_uuid', 'patch', body=body)
|
|
|
|
def test_qos_policy_group_modify(self):
|
|
return_request = {
|
|
'records': [{'uuid': 'fake_uuid'}]
|
|
}
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_request))
|
|
self.client.qos_policy_group_modify('qos_fake_name', '1000iops')
|
|
|
|
query = {
|
|
'name': 'qos_fake_name',
|
|
}
|
|
body = {
|
|
'fixed.max_throughput_iops': 1000,
|
|
'fixed.max_throughput_mbps': 0
|
|
}
|
|
mock_sr.assert_has_calls([
|
|
mock.call('/storage/qos/policies', 'get', query=query),
|
|
mock.call('/storage/qos/policies/fake_uuid', 'patch', body=body),
|
|
])
|
|
|
|
@ddt.data(True, False)
|
|
def test_set_volume_filesys_size_fixed(self, filesys_size_fixed):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_send_request = self.mock_object(self.client, 'send_request')
|
|
fake_uuid = volume['uuid']
|
|
|
|
self.client.set_volume_filesys_size_fixed(fake.SHARE_NAME,
|
|
filesys_size_fixed)
|
|
body = {
|
|
'space.filesystem_size_fixed': filesys_size_fixed}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{fake_uuid}',
|
|
'patch', body=body)
|
|
|
|
def test_create_snapshot(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
mock_get_volume = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_send_request = self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_snapshot(fake.VOLUME_NAMES[0], fake.SNAPSHOT_NAME)
|
|
|
|
mock_get_volume.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
body = {
|
|
'name': fake.SNAPSHOT_NAME,
|
|
}
|
|
uuid = volume['uuid']
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}/snapshots', 'post', body=body)
|
|
|
|
def test_is_flexgroup_supported(self):
|
|
flexgroup_supported = self.client.is_flexgroup_supported()
|
|
|
|
self.assertTrue(flexgroup_supported)
|
|
|
|
@ddt.data(True, False)
|
|
def test_is_flexgroup_volume(self, is_flexgroup):
|
|
response = copy.deepcopy(fake.VOLUME_LIST_SIMPLE_RESPONSE_REST)
|
|
expected_style = 'flexgroup' if is_flexgroup else 'flexvol'
|
|
response['records'][0]['style'] = expected_style
|
|
mock_send_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
mock_has_records = self.mock_object(
|
|
self.client, '_has_records', mock.Mock(return_value=True))
|
|
mock_na_utils_is_flexgroup = self.mock_object(
|
|
netapp_utils, 'is_style_extended_flexgroup',
|
|
mock.Mock(return_value=is_flexgroup))
|
|
|
|
result = self.client.is_flexgroup_volume(fake.VOLUME_NAMES[0])
|
|
|
|
self.assertEqual(is_flexgroup, result)
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0],
|
|
'fields': 'style'
|
|
}
|
|
mock_send_request.assert_called_once_with('/storage/volumes/', 'get',
|
|
query=query)
|
|
mock_has_records.assert_called_once_with(response)
|
|
mock_na_utils_is_flexgroup.assert_called_once_with(expected_style)
|
|
|
|
def test_is_flexgroup_volume_raise_no_records(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.NO_RECORDS_RESPONSE_REST))
|
|
self.mock_object(
|
|
self.client, '_has_records', mock.Mock(return_value=False))
|
|
|
|
self.assertRaises(
|
|
exception.StorageResourceNotFound,
|
|
self.client.is_flexgroup_volume,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
def test_is_flexgroup_volume_raise_more_than_one_volume(self):
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE))
|
|
self.mock_object(
|
|
self.client, '_has_records', mock.Mock(return_value=True))
|
|
|
|
self.assertRaises(
|
|
exception.NetAppException,
|
|
self.client.is_flexgroup_volume,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
@ddt.data(
|
|
{'is_busy': True, 'owners': ['volume_clone']},
|
|
{'is_busy': False, 'owners': ['snap_restore_dependent']})
|
|
@ddt.unpack
|
|
def test__is_busy_snapshot(self, is_busy, owners):
|
|
result = self.client._is_busy_snapshot(owners)
|
|
|
|
self.assertEqual(is_busy, result)
|
|
|
|
@ddt.data(True, False)
|
|
def test_get_snapshot(self, locked):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
mock_get_volume = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
response = copy.deepcopy(fake.SNAPSHOTS_REST_RESPONSE)
|
|
owners = ['volume_clone'] if locked else []
|
|
response['records'][0]['owners'] = owners
|
|
mock_send_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
mock_has_records = self.mock_object(
|
|
self.client, '_has_records', mock.Mock(return_value=True))
|
|
|
|
mock_is_busy = self.mock_object(self.client, '_is_busy_snapshot',
|
|
mock.Mock(return_value=True))
|
|
|
|
result = self.client.get_snapshot(fake.VOLUME_NAMES[0],
|
|
fake.SNAPSHOT_NAME)
|
|
|
|
expected_snapshot = {
|
|
'access-time': fake.SNAPSHOT_REST['create_time'],
|
|
'name': fake.SNAPSHOT_REST['name'],
|
|
'volume': fake.SNAPSHOT_REST['volume']['name'],
|
|
'owners': set(owners),
|
|
'busy': True,
|
|
'locked_by_clone': locked,
|
|
}
|
|
self.assertEqual(expected_snapshot, result)
|
|
mock_get_volume.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
uuid = volume['uuid']
|
|
query = {
|
|
'name': fake.SNAPSHOT_NAME,
|
|
'fields': 'name,volume,create_time,owners'
|
|
}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}/snapshots', 'get', query=query)
|
|
mock_has_records.assert_called_once_with(response)
|
|
mock_is_busy.assert_called_once_with(set(owners))
|
|
|
|
def test_get_snapshot_raise_not_found(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=fake.NO_RECORDS_RESPONSE_REST))
|
|
self.mock_object(
|
|
self.client, '_has_records', mock.Mock(return_value=False))
|
|
|
|
self.assertRaises(
|
|
exception.SnapshotResourceNotFound,
|
|
self.client.get_snapshot,
|
|
fake.VOLUME_NAMES[0],
|
|
fake.SNAPSHOT_NAME)
|
|
|
|
def test_get_snapshot_raise_more_than_one(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=fake.SNAPSHOTS_MULTIPLE_REST_RESPONSE))
|
|
self.mock_object(
|
|
self.client, '_has_records', mock.Mock(return_value=True))
|
|
|
|
self.assertRaises(
|
|
exception.NetAppException,
|
|
self.client.get_snapshot,
|
|
fake.VOLUME_NAMES[0],
|
|
fake.SNAPSHOT_NAME)
|
|
|
|
def test_get_clone_children_for_snapshot(self):
|
|
mock_get_records = self.mock_object(
|
|
self.client, 'get_records',
|
|
mock.Mock(return_value=fake.VOLUME_LIST_SIMPLE_RESPONSE_REST))
|
|
|
|
result = self.client.get_clone_children_for_snapshot(
|
|
fake.VOLUME_NAMES[0], fake.SNAPSHOT_NAME)
|
|
|
|
expected_children = [{'name': fake.VOLUME_NAMES[0]}]
|
|
self.assertEqual(expected_children, result)
|
|
query = {
|
|
'clone.parent_snapshot.name': fake.SNAPSHOT_NAME,
|
|
'clone.parent_volume.name': fake.VOLUME_NAMES[0],
|
|
'fields': 'name'
|
|
}
|
|
mock_get_records.assert_called_once_with(
|
|
'/storage/volumes', query=query)
|
|
|
|
def test_volume_clone_split_start(self):
|
|
fake_resp_vol = fake.REST_SIMPLE_RESPONSE["records"][0]
|
|
fake_uuid = fake_resp_vol['uuid']
|
|
mock_get_unique_volume = self.mock_object(
|
|
self.client, "_get_volume_by_args",
|
|
mock.Mock(return_value=fake_resp_vol)
|
|
)
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=fake.VOLUME_LIST_SIMPLE_RESPONSE_REST))
|
|
|
|
self.client.volume_clone_split_start(fake.VOLUME_NAMES[0])
|
|
mock_get_unique_volume.assert_called_once()
|
|
body = {
|
|
'clone.split_initiated': 'true',
|
|
}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{fake_uuid}', 'patch', body=body,
|
|
wait_on_accepted=False)
|
|
|
|
def test_volume_clone_split_stop(self):
|
|
fake_resp_vol = fake.REST_SIMPLE_RESPONSE["records"][0]
|
|
fake_uuid = fake_resp_vol['uuid']
|
|
mock_get_unique_volume = self.mock_object(
|
|
self.client, "_get_volume_by_args",
|
|
mock.Mock(return_value=fake_resp_vol)
|
|
)
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=fake.VOLUME_LIST_SIMPLE_RESPONSE_REST))
|
|
|
|
self.client.volume_clone_split_stop(fake.VOLUME_NAMES[0])
|
|
mock_get_unique_volume.assert_called_once()
|
|
body = {
|
|
'clone.split_initiated': 'false',
|
|
}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{fake_uuid}', 'patch', body=body,
|
|
wait_on_accepted=False)
|
|
|
|
def test_rename_snapshot(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
mock_get_volume = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_send_request = self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.rename_snapshot(
|
|
fake.VOLUME_NAMES[0], fake.SNAPSHOT_NAME,
|
|
'new_' + fake.SNAPSHOT_NAME)
|
|
|
|
mock_get_volume.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0])
|
|
query = {
|
|
'name': fake.SNAPSHOT_NAME,
|
|
}
|
|
body = {
|
|
'name': 'new_' + fake.SNAPSHOT_NAME,
|
|
}
|
|
uuid = volume['uuid']
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}/snapshots', 'patch', query=query,
|
|
body=body)
|
|
|
|
def test__get_soft_deleted_snapshots(self):
|
|
mock_get_records = self.mock_object(
|
|
self.client, 'get_records',
|
|
mock.Mock(return_value=fake.SNAPSHOTS_MULTIPLE_REST_RESPONSE))
|
|
self.mock_object(
|
|
self.client, '_is_busy_snapshot',
|
|
mock.Mock(side_effect=[True, False]))
|
|
|
|
snapshots_map = self.client._get_soft_deleted_snapshots()
|
|
|
|
expected_snapshots = {
|
|
fake.VSERVER_NAME: [{
|
|
"uuid": fake.FAKE_SNAPSHOT_UUID,
|
|
"volume_uuid": fake.FAKE_VOLUME_UUID,
|
|
}]
|
|
}
|
|
self.assertEqual(expected_snapshots, snapshots_map)
|
|
query = {
|
|
'name': 'deleted_manila_*',
|
|
'fields': 'uuid,volume,owners,svm.name'
|
|
}
|
|
mock_get_records.assert_called_once_with(
|
|
'/storage/volumes/*/snapshots', query=query)
|
|
|
|
@ddt.data(True, False)
|
|
def test_prune_deleted_snapshots(self, fail_deleting):
|
|
soft_deleted_snapshots = {
|
|
fake.VSERVER_NAME: [{
|
|
"uuid": fake.FAKE_SNAPSHOT_UUID,
|
|
"volume_uuid": fake.FAKE_VOLUME_UUID,
|
|
}]
|
|
}
|
|
mock_get_snaps = self.mock_object(
|
|
self.client, '_get_soft_deleted_snapshots',
|
|
mock.Mock(return_value=soft_deleted_snapshots)
|
|
)
|
|
if fail_deleting:
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=netapp_api.api.NaApiError))
|
|
else:
|
|
mock_send_request = self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.prune_deleted_snapshots()
|
|
|
|
mock_get_snaps.assert_called_once_with()
|
|
vol_uuid = fake.FAKE_VOLUME_UUID
|
|
snap_uuid = fake.FAKE_SNAPSHOT_UUID
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{vol_uuid}/snapshots/{snap_uuid}', 'delete')
|
|
|
|
@ddt.data(True, False)
|
|
def test_snapshot_exists(self, exists):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
vol_uuid = volume['uuid']
|
|
mock_get_vol = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=fake.SNAPSHOTS_REST_RESPONSE))
|
|
mock_has_records = self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=exists))
|
|
|
|
res = self.client.snapshot_exists(fake.SNAPSHOT_NAME,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
self.assertEqual(exists, res)
|
|
mock_get_vol.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0], fields='uuid,state')
|
|
query = {
|
|
'name': fake.SNAPSHOT_NAME
|
|
}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{vol_uuid}/snapshots/', 'get', query=query)
|
|
mock_has_records.assert_called_once_with(fake.SNAPSHOTS_REST_RESPONSE)
|
|
|
|
def test_snapshot_exists_error(self):
|
|
volume = {'state': 'offline'}
|
|
self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
|
|
self.assertRaises(
|
|
exception.SnapshotUnavailable,
|
|
self.client.snapshot_exists,
|
|
fake.SNAPSHOT_NAME, fake.VOLUME_NAMES[0])
|
|
|
|
@ddt.data('source', 'destination', None)
|
|
def test_volume_has_snapmirror_relationships(self, snapmirror_rel_type):
|
|
"""Snapmirror relationships can be both ways."""
|
|
|
|
vol = fake.FAKE_MANAGE_VOLUME
|
|
snapmirror = {
|
|
'source-vserver': fake.SM_SOURCE_VSERVER,
|
|
'source-volume': fake.SM_SOURCE_VOLUME,
|
|
'destination-vserver': fake.SM_DEST_VSERVER,
|
|
'destination-volume': fake.SM_DEST_VOLUME,
|
|
'mirror-state': 'snapmirrored',
|
|
'schedule': 'daily',
|
|
}
|
|
expected_get_snapmirrors_call_count = 2
|
|
expected_get_snapmirrors_calls = [
|
|
mock.call(source_vserver=vol['owning-vserver-name'],
|
|
source_volume=vol['name']),
|
|
mock.call(dest_vserver=vol['owning-vserver-name'],
|
|
dest_volume=vol['name']),
|
|
]
|
|
if snapmirror_rel_type is None:
|
|
side_effect = ([], [])
|
|
elif snapmirror_rel_type == 'source':
|
|
snapmirror['source-vserver'] = vol['owning-vserver-name']
|
|
snapmirror['source-volume'] = vol['name']
|
|
side_effect = ([snapmirror], None)
|
|
expected_get_snapmirrors_call_count = 1
|
|
expected_get_snapmirrors_calls.pop()
|
|
else:
|
|
snapmirror['destination-vserver'] = vol['owning-vserver-name']
|
|
snapmirror['destination-volume'] = vol['name']
|
|
side_effect = (None, [snapmirror])
|
|
mock_get_snapmirrors_call = self.mock_object(
|
|
self.client, 'get_snapmirrors', mock.Mock(side_effect=side_effect))
|
|
mock_exc_log = self.mock_object(client_cmode.LOG, 'exception')
|
|
expected_retval = True if snapmirror_rel_type else False
|
|
|
|
retval = self.client.volume_has_snapmirror_relationships(vol)
|
|
|
|
self.assertEqual(expected_retval, retval)
|
|
self.assertEqual(expected_get_snapmirrors_call_count,
|
|
mock_get_snapmirrors_call.call_count)
|
|
mock_get_snapmirrors_call.assert_has_calls(
|
|
expected_get_snapmirrors_calls)
|
|
self.assertFalse(mock_exc_log.called)
|
|
|
|
def test_volume_has_snapmirror_relationships_api_error(self):
|
|
|
|
vol = fake.FAKE_MANAGE_VOLUME
|
|
expected_get_snapmirrors_calls = [
|
|
mock.call(source_vserver=vol['owning-vserver-name'],
|
|
source_volume=vol['name']),
|
|
]
|
|
mock_get_snapmirrors_call = self.mock_object(
|
|
self.client, 'get_snapmirrors', mock.Mock(
|
|
side_effect=self._mock_api_error()))
|
|
mock_exc_log = self.mock_object(client_cmode_rest.LOG, 'exception')
|
|
|
|
retval = self.client.volume_has_snapmirror_relationships(vol)
|
|
|
|
self.assertFalse(retval)
|
|
self.assertEqual(1, mock_get_snapmirrors_call.call_count)
|
|
mock_get_snapmirrors_call.assert_has_calls(
|
|
expected_get_snapmirrors_calls)
|
|
self.assertTrue(mock_exc_log.called)
|
|
|
|
def test_get_snapmirrors_svm(self):
|
|
return_get_snp = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
|
|
mock_get_snap = self.mock_object(
|
|
self.client, 'get_snapmirrors',
|
|
mock.Mock(return_value=return_get_snp))
|
|
|
|
res = self.client.get_snapmirrors_svm(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_DEST_VSERVER,
|
|
None)
|
|
|
|
mock_get_snap.assert_called_once_with(
|
|
source_path=fake.SM_SOURCE_VSERVER + ':*',
|
|
dest_path=fake.SM_DEST_VSERVER + ':*',
|
|
desired_attributes=None)
|
|
self.assertEqual(return_get_snp, res)
|
|
|
|
def test_get_snapmirrors(self):
|
|
|
|
api_response = fake.SNAPMIRROR_GET_ITER_RESPONSE_REST
|
|
mock_send_request = self.mock_object(
|
|
self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_snapmirrors(
|
|
fake.SM_SOURCE_PATH, fake.SM_DEST_PATH,
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
|
enable_tunneling=True)
|
|
|
|
expected = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
|
|
query = {
|
|
'source.path': (fake.SM_SOURCE_VSERVER + ':' +
|
|
fake.SM_SOURCE_VOLUME),
|
|
'destination.path': (fake.SM_DEST_VSERVER +
|
|
':' + fake.SM_DEST_VOLUME),
|
|
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
|
'destination.path,transfer.end_time,uuid,policy.type,'
|
|
'transfer_schedule.name,transfer.state'
|
|
}
|
|
|
|
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
|
'get', query=query,
|
|
enable_tunneling=True)
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data(
|
|
{'source_path': fake.SM_SOURCE_PATH, 'dest_path': fake.SM_DEST_PATH},
|
|
{'source_path': None, 'dest_path': None})
|
|
@ddt.unpack
|
|
def test__get_snapmirrors(self, source_path, dest_path):
|
|
|
|
api_response = fake.SNAPMIRROR_GET_ITER_RESPONSE_REST
|
|
mock_send_request = self.mock_object(
|
|
self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client._get_snapmirrors(
|
|
source_path, dest_path,
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME)
|
|
|
|
query = {
|
|
'source.path': (fake.SM_SOURCE_VSERVER + ':' +
|
|
fake.SM_SOURCE_VOLUME),
|
|
'destination.path': (fake.SM_DEST_VSERVER +
|
|
':' + fake.SM_DEST_VOLUME),
|
|
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
|
'destination.path,transfer.end_time,uuid,policy.type,'
|
|
'transfer_schedule.name,transfer.state'
|
|
}
|
|
|
|
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
|
'get', query=query,
|
|
enable_tunneling=True)
|
|
self.assertEqual(1, len(result))
|
|
|
|
def test__get_snapmirrors_not_found(self):
|
|
|
|
api_response = fake.NO_RECORDS_RESPONSE_REST
|
|
mock_send_request = self.mock_object(
|
|
self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client._get_snapmirrors(
|
|
fake.SM_SOURCE_PATH, fake.SM_DEST_PATH,
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME)
|
|
|
|
query = {
|
|
'source.path': (fake.SM_SOURCE_VSERVER + ':' +
|
|
fake.SM_SOURCE_VOLUME),
|
|
'destination.path': (fake.SM_DEST_VSERVER +
|
|
':' + fake.SM_DEST_VOLUME),
|
|
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
|
'destination.path,transfer.end_time,uuid,policy.type,'
|
|
'transfer_schedule.name,transfer.state'
|
|
}
|
|
|
|
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
|
'get', query=query,
|
|
enable_tunneling=True)
|
|
self.assertEqual([], result)
|
|
|
|
@ddt.data(True, False)
|
|
def test_modify_volume_no_optional_args(self, is_flexgroup):
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
mock_update_volume_efficiency_attributes = self.mock_object(
|
|
self.client, 'update_volume_efficiency_attributes')
|
|
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
|
|
aggr = fake.SHARE_AGGREGATE_NAME
|
|
if is_flexgroup:
|
|
aggr = list(fake.SHARE_AGGREGATE_NAMES)
|
|
|
|
self.client.modify_volume(aggr, fake.SHARE_NAME)
|
|
|
|
# default body for call with no optional params
|
|
body = {'guarantee': {'type': 'volume'}}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes/' + volume['uuid'], 'patch', body=body)
|
|
mock_update_volume_efficiency_attributes.assert_called_once_with(
|
|
fake.SHARE_NAME, False, False, is_flexgroup=is_flexgroup)
|
|
|
|
@ddt.data((fake.QOS_POLICY_GROUP_NAME, None),
|
|
(None, fake.ADAPTIVE_QOS_POLICY_GROUP_NAME))
|
|
@ddt.unpack
|
|
def test_modify_volume_all_optional_args(self, qos_group,
|
|
adaptive_qos_group):
|
|
self.client.features.add_feature('ADAPTIVE_QOS')
|
|
self.mock_object(self.client, 'send_request')
|
|
mock_update_volume_efficiency_attributes = self.mock_object(
|
|
self.client, 'update_volume_efficiency_attributes')
|
|
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
|
|
self.client.modify_volume(
|
|
fake.SHARE_AGGREGATE_NAME,
|
|
fake.SHARE_NAME,
|
|
thin_provisioned=True,
|
|
snapshot_policy=fake.SNAPSHOT_POLICY_NAME,
|
|
language=fake.LANGUAGE,
|
|
dedup_enabled=True,
|
|
compression_enabled=False,
|
|
max_files=fake.MAX_FILES,
|
|
qos_policy_group=qos_group,
|
|
adaptive_qos_policy_group=adaptive_qos_group,
|
|
autosize_attributes=fake.VOLUME_AUTOSIZE_ATTRS,
|
|
hide_snapdir=True)
|
|
|
|
qos_policy_name = qos_group or adaptive_qos_group
|
|
body = {
|
|
'guarantee': {'type': 'none'},
|
|
'autosize': {
|
|
'mode': 'off',
|
|
'grow_threshold': '85',
|
|
'shrink_threshold': '50',
|
|
'maximum': '1258288',
|
|
'minimum': '1048576'
|
|
},
|
|
'files': {'maximum': 5000},
|
|
'snapshot_policy': {'name': 'fake_snapshot_policy'},
|
|
'qos': {'policy': {'name': qos_policy_name}},
|
|
'snapshot_directory_access_enabled': 'false',
|
|
'language': 'fake_language'
|
|
}
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes/' + volume['uuid'], 'patch', body=body)
|
|
mock_update_volume_efficiency_attributes.assert_called_once_with(
|
|
fake.SHARE_NAME, True, False, is_flexgroup=False)
|
|
|
|
def test__parse_timestamp(self):
|
|
test_time_str = '2022-11-25T14:41:20+00:00'
|
|
res = self.client._parse_timestamp(test_time_str)
|
|
self.assertEqual(1669387280.0, res)
|
|
|
|
def test__parse_timestamp_exception(self):
|
|
test_time_str = None
|
|
self.assertRaises(TypeError,
|
|
self.client._parse_timestamp,
|
|
test_time_str)
|
|
|
|
def test_start_volume_move(self):
|
|
|
|
mock__send_volume_move_request = self.mock_object(
|
|
self.client, '_send_volume_move_request')
|
|
|
|
self.client.start_volume_move(fake.VOLUME_NAMES[0], fake.VSERVER_NAME,
|
|
fake.SHARE_AGGREGATE_NAME,
|
|
'fake_cutover', False)
|
|
|
|
mock__send_volume_move_request.assert_called_once_with(
|
|
fake.VOLUME_NAMES[0], fake.VSERVER_NAME, fake.SHARE_AGGREGATE_NAME,
|
|
cutover_action='fake_cutover', encrypt_destination=False)
|
|
|
|
def test_check_volume_move(self):
|
|
|
|
mock__send_volume_move_request = self.mock_object(
|
|
self.client, '_send_volume_move_request')
|
|
|
|
self.client.check_volume_move(fake.VOLUME_NAMES[0], fake.VSERVER_NAME,
|
|
fake.SHARE_AGGREGATE_NAME, False)
|
|
|
|
mock__send_volume_move_request.assert_called_once_with(
|
|
fake.VOLUME_NAMES[0], fake.VSERVER_NAME, fake.SHARE_AGGREGATE_NAME,
|
|
validation_only=True, encrypt_destination=False)
|
|
|
|
def test__send_volume_move_request(self):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client._send_volume_move_request('volume_name', 'vserver',
|
|
'destination_aggregate',
|
|
cutover_action='wait',
|
|
validation_only=True,
|
|
encrypt_destination=False)
|
|
query = {'name': 'volume_name'}
|
|
body = {
|
|
'movement.destination_aggregate.name': 'destination_aggregate',
|
|
'encryption.enabled': 'false',
|
|
'validate_only': 'true',
|
|
'movement.state': 'wait',
|
|
}
|
|
mock_sr.assert_called_once_with(
|
|
'/storage/volumes/', 'patch', query=query, body=body,
|
|
wait_on_accepted=False)
|
|
|
|
def test_get_nfs_export_policy_for_volume(self):
|
|
|
|
fake_query = {
|
|
'name': 'fake_volume_name',
|
|
'fields': 'nas.export_policy.name'
|
|
}
|
|
|
|
ret = {
|
|
'records': [
|
|
{
|
|
'nas': {
|
|
'export_policy': {
|
|
'name': 'fake_name'
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
mock_records = self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=ret))
|
|
|
|
res = self.client.get_nfs_export_policy_for_volume('fake_volume_name')
|
|
|
|
mock_records.assert_called_once_with(ret)
|
|
mock_sr.assert_called_once_with('/storage/volumes/', 'get',
|
|
query=fake_query)
|
|
expected = 'fake_name'
|
|
self.assertEqual(expected, res)
|
|
|
|
def test_get_unique_export_policy_id(self):
|
|
mock_records = self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
expected = 'fake_uuid'
|
|
ret = {
|
|
'records': [
|
|
{
|
|
'id': 'fake_uuid'
|
|
}
|
|
]
|
|
}
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=ret))
|
|
res = self.client.get_unique_export_policy_id('fake_policy_name')
|
|
mock_records.assert_called_once_with(ret)
|
|
mock_sr.assert_called_once_with(
|
|
'/protocols/nfs/export-policies', 'get',
|
|
query={'name': 'fake_policy_name'})
|
|
self.assertEqual(expected, res)
|
|
|
|
def test__get_nfs_export_rule_indices(self):
|
|
mockpid = self.mock_object(self.client, 'get_unique_export_policy_id',
|
|
mock.Mock(return_value='fake_policy_id'))
|
|
|
|
fake_uuid = 'fake_policy_id'
|
|
|
|
fake_query = {
|
|
'clients.match': 'fakecl',
|
|
'fields': 'clients.match,index'
|
|
}
|
|
|
|
ret = {
|
|
'records': [
|
|
{
|
|
'index': '0'
|
|
}
|
|
]
|
|
}
|
|
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=ret))
|
|
res = self.client._get_nfs_export_rule_indices('fake_policy', 'fakecl')
|
|
mockpid.assert_called_once_with('fake_policy')
|
|
mock_sr.assert_called_once_with(
|
|
f'/protocols/nfs/export-policies/{fake_uuid}/rules', 'get',
|
|
query=fake_query)
|
|
expected = ['0']
|
|
self.assertEqual(expected, res)
|
|
|
|
def test__add_nfs_export_rule(self):
|
|
mockpid = self.mock_object(self.client, 'get_unique_export_policy_id',
|
|
mock.Mock(return_value='fake_policy_id'))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client._add_nfs_export_rule('fake_policy', 'fakecl', False,
|
|
['rw'])
|
|
mockpid.assert_called_once_with('fake_policy')
|
|
|
|
body = {
|
|
'clients': [{'match': 'fakecl'}],
|
|
'ro_rule': ['rw'],
|
|
'rw_rule': ['rw'],
|
|
'superuser': ['rw']
|
|
}
|
|
mock_sr.assert_called_once_with(
|
|
'/protocols/nfs/export-policies/fake_policy_id/rules',
|
|
'post', body=body)
|
|
|
|
def test__update_nfs_export_rule(self):
|
|
|
|
fake_body = {
|
|
'client_match': 'fake_cli',
|
|
'ro_rule': ['rw'],
|
|
'rw_rule': ['rw'],
|
|
'superuser': ['rw']
|
|
}
|
|
|
|
mockpid = self.mock_object(self.client, 'get_unique_export_policy_id',
|
|
mock.Mock(return_value='fake_policy_id'))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client._update_nfs_export_rule('fake_policy', 'fake_cli', False,
|
|
'0', ['rw'])
|
|
mockpid.assert_called_once_with('fake_policy')
|
|
mock_sr.assert_called_once_with(
|
|
'/protocols/nfs/export-policies/fake_policy_id/rules/0',
|
|
'patch', body=fake_body)
|
|
|
|
def test__remove_nfs_export_rules(self):
|
|
|
|
fake_body = {
|
|
'index': 0
|
|
}
|
|
|
|
mockpid = self.mock_object(self.client, 'get_unique_export_policy_id',
|
|
mock.Mock(return_value='fake_policy_id'))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client._remove_nfs_export_rules('fake_policy', [0])
|
|
mockpid.assert_called_once_with('fake_policy')
|
|
mock_sr.assert_called_once_with(
|
|
'/protocols/nfs/export-policies/fake_policy_id/rules/0', 'delete',
|
|
body=fake_body)
|
|
|
|
def test_modify_cifs_share_access(self):
|
|
return_uuid = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_uuid))
|
|
self.client.modify_cifs_share_access(fake.SHARE_NAME, fake.USER_NAME,
|
|
'read')
|
|
fake_user = 'fake_user'
|
|
FAKE_CIFS_USER_GROUP_TYPE = 'windows'
|
|
fake_uuid = 'fake_uuid'
|
|
fake_share = fake.SHARE_NAME
|
|
query = {'name': 'fake_share'}
|
|
body = {'permission': 'read'}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/cifs/shares', 'get', query=query),
|
|
mock.call(f'/protocols/cifs/shares/{fake_uuid}/{fake_share}'
|
|
f'/acls/{fake_user}/{FAKE_CIFS_USER_GROUP_TYPE}',
|
|
'patch', body=body)])
|
|
|
|
def test_add_cifs_share_access(self):
|
|
return_uuid = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_uuid))
|
|
self.client.add_cifs_share_access(fake.SHARE_NAME,
|
|
fake.USER_NAME, 'read')
|
|
fake_uuid = "fake_uuid"
|
|
query = {'name': 'fake_share'}
|
|
body = {'permission': 'read',
|
|
'user_or_group': 'fake_user'}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/cifs/shares', 'get', query=query),
|
|
mock.call(f'/protocols/cifs/shares/{fake_uuid}/{fake.SHARE_NAME}'
|
|
'/acls', 'post', body=body)])
|
|
|
|
def test_get_cifs_share_access_rules_empty(self):
|
|
return_uuid = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_uuid))
|
|
|
|
def test_get_cifs_share_access_rules_not_empty(self):
|
|
return_uuid = fake.GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_uuid))
|
|
|
|
rules = {}
|
|
|
|
fake_results = fake.FAKE_CIFS_RECORDS
|
|
|
|
for record in fake_results['records']:
|
|
user_or_group = record['user_or_group']
|
|
permission = record['permission']
|
|
rules[user_or_group] = permission
|
|
|
|
def test_mount_volume(self):
|
|
volume_name = fake.SHARE_NAME
|
|
junction_path = '/fake_path'
|
|
volume = fake.VOLUME
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.mount_volume(volume_name, junction_path=junction_path)
|
|
|
|
uuid = volume['uuid']
|
|
|
|
body = {
|
|
'nas.path': (junction_path if junction_path
|
|
else '/%s' % volume_name)
|
|
}
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
|
|
def test_set_volume_name(self):
|
|
volume_name = fake.SHARE_NAME
|
|
new_volume_name = 'fake_name'
|
|
|
|
volume = fake.VOLUME
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.set_volume_name(volume_name, new_volume_name)
|
|
|
|
uuid = volume['uuid']
|
|
|
|
body = {
|
|
'name': new_volume_name
|
|
}
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/storage/volumes/{uuid}', 'patch', body=body)
|
|
|
|
def test_get_job(self):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client.get_job('fake_job_uuid')
|
|
mock_sr.assert_called_once_with('/cluster/jobs/fake_job_uuid',
|
|
'get', enable_tunneling=False)
|
|
|
|
@ddt.data(netapp_api.EREST_VSERVER_NOT_FOUND, 'fake')
|
|
def test_vserver_exists_exception(self, er):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code=er)))
|
|
if er == netapp_api.EREST_VSERVER_NOT_FOUND:
|
|
result = self.client.vserver_exists(fake.VSERVER_NAME)
|
|
self.assertFalse(result)
|
|
else:
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.vserver_exists,
|
|
fake.VSERVER_NAME)
|
|
|
|
def test__get_aggregate_disk_types(self):
|
|
response = fake.FAKE_DISK_TYPE_RESPONSE
|
|
aggr = fake.SHARE_AGGREGATE_NAME
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
query = {
|
|
'aggregates.name': aggr,
|
|
'fields': 'effective_type'
|
|
}
|
|
expected = {'fakedisk'}
|
|
|
|
result = self.client._get_aggregate_disk_types(aggr)
|
|
|
|
mock_sr.assert_called_once_with('/storage/disks', 'get', query=query)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test__get_aggregate_disk_types_exception(self):
|
|
aggr = fake.SHARE_AGGREGATE_NAME
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
result = self.client._get_aggregate_disk_types(aggr)
|
|
self.assertEqual(set(), result)
|
|
|
|
def test_create_nfs_export_policy_exception(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.create_nfs_export_policy,
|
|
fake.EXPORT_POLICY_NAME)
|
|
|
|
@ddt.data(True, False)
|
|
def test__get_create_volume_body(self, thin_provisioned):
|
|
expected = {
|
|
'type': 'fake_type',
|
|
'guarantee.type': ('none' if thin_provisioned else 'volume'),
|
|
'nas.path': '/%s' % fake.VOLUME_NAMES[0],
|
|
'snapshot_policy.name': fake.SNAPSHOT_POLICY_NAME,
|
|
'language': 'fake_language',
|
|
'space.snapshot.reserve_percent': 'fake_percent',
|
|
'qos.policy.name': fake.QOS_POLICY_GROUP_NAME,
|
|
'svm.name': 'fake_vserver',
|
|
'encryption.enabled': 'true'
|
|
}
|
|
|
|
self.mock_object(self.client.connection, 'get_vserver',
|
|
mock.Mock(return_value='fake_vserver'))
|
|
res = self.client._get_create_volume_body(fake.VOLUME_NAMES[0],
|
|
thin_provisioned,
|
|
fake.SNAPSHOT_POLICY_NAME,
|
|
'fake_language',
|
|
'fake_percent',
|
|
'fake_type',
|
|
fake.QOS_POLICY_GROUP_NAME,
|
|
True,
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
self.assertEqual(expected, res)
|
|
|
|
def test_get_job_state(self):
|
|
expected = 'success'
|
|
query = {
|
|
'uuid': 'fake_uuid',
|
|
'fields': 'state'
|
|
}
|
|
response = {
|
|
'records': [fake.JOB_SUCCESSFUL_REST]
|
|
}
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
result = self.client.get_job_state('fake_uuid')
|
|
mock_sr.assert_called_once_with('/cluster/jobs/', 'get', query=query,
|
|
enable_tunneling=False)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_job_state_not_found(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=False))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_job_state,
|
|
'fake_uuid')
|
|
|
|
@ddt.data(True, False)
|
|
def test_update_volume_efficiency_attributes(self, status):
|
|
response = {
|
|
'dedupe': not status,
|
|
'compression': not status
|
|
}
|
|
self.mock_object(self.client, 'get_volume_efficiency_status',
|
|
mock.Mock(return_value=response))
|
|
en_dedupe = self.mock_object(self.client, 'enable_dedupe_async')
|
|
dis_dedupe = self.mock_object(self.client, 'disable_dedupe_async')
|
|
en_comp = self.mock_object(self.client, 'enable_compression_async')
|
|
dis_comp = self.mock_object(self.client, 'disable_compression_async')
|
|
|
|
self.client.update_volume_efficiency_attributes(fake.VOLUME_NAMES[0],
|
|
status, status)
|
|
|
|
if status:
|
|
en_dedupe.assert_called_once_with(fake.VOLUME_NAMES[0])
|
|
en_comp.assert_called_once_with(fake.VOLUME_NAMES[0])
|
|
else:
|
|
dis_dedupe.assert_called_once_with(fake.VOLUME_NAMES[0])
|
|
dis_comp.assert_called_once_with(fake.VOLUME_NAMES[0])
|
|
|
|
def test_trigger_volume_move_cutover(self):
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0]
|
|
}
|
|
body = {
|
|
'movement.state': 'cutover'
|
|
}
|
|
self.mock_object(self.client, 'send_request')
|
|
self.client.trigger_volume_move_cutover(
|
|
fake.VOLUME_NAMES[0], fake.VSERVER_NAME)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes/', 'patch', query=query,
|
|
body=body)
|
|
|
|
def test_abort_volume_move(self):
|
|
return_uuid = {
|
|
'uuid': 'fake_uuid'
|
|
}
|
|
mock_get_vol = self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=return_uuid))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.abort_volume_move('fake_volume_name', 'fake_vserver')
|
|
|
|
mock_sr.assert_called_once_with('/storage/volumes/fake_uuid', 'patch')
|
|
mock_get_vol.assert_called_once_with(vol_name='fake_volume_name')
|
|
|
|
def test_get_volume_move_status(self):
|
|
"""Gets the current state of a volume move operation."""
|
|
|
|
return_sr = fake.FAKE_VOL_MOVE_STATUS
|
|
|
|
fields = 'movement.percent_complete,movement.state'
|
|
|
|
query = {
|
|
'name': 'fake_name',
|
|
'svm.name': 'fake_svm',
|
|
'fields': fields
|
|
}
|
|
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_sr))
|
|
|
|
result = self.client.get_volume_move_status('fake_name', 'fake_svm')
|
|
|
|
mock_sr.assert_called_once_with('/storage/volumes/',
|
|
'get', query=query)
|
|
|
|
volume_move_info = return_sr.get('records')[0]
|
|
volume_movement = volume_move_info['movement']
|
|
|
|
expected = {
|
|
'percent-complete': volume_movement['percent_complete'],
|
|
'estimated-completion-time': '',
|
|
'state': volume_movement['state'],
|
|
'details': '',
|
|
'cutover-action': '',
|
|
'phase': volume_movement['state'],
|
|
}
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_list_snapmirror_snapshots(self):
|
|
fake_response = fake.SNAPSHOTS_REST_RESPONSE
|
|
api_response = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
mock_volume = self.mock_object(self.client,
|
|
'_get_volume_by_args',
|
|
mock.Mock(return_value=api_response))
|
|
mock_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake_response))
|
|
self.client.list_snapmirror_snapshots(fake.VOLUME_NAMES[0])
|
|
|
|
query = {
|
|
'owners': 'snapmirror_dependent',
|
|
}
|
|
mock_request.assert_called_once_with(
|
|
'/storage/volumes/fake_uuid/snapshots/',
|
|
'get', query=query)
|
|
mock_volume.assert_called_once_with(vol_name=fake.VOLUME_NAMES[0])
|
|
|
|
@ddt.data({'policy': 'fake_policy'},
|
|
{'policy': None})
|
|
@ddt.unpack
|
|
def test_create_snapmirror_vol(self, policy):
|
|
api_responses = [
|
|
{
|
|
"job": {
|
|
"uuid": fake.FAKE_UUID,
|
|
},
|
|
},
|
|
]
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=copy.deepcopy(api_responses)))
|
|
self.client.create_snapmirror_vol(
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
|
relationship_type=netapp_utils.EXTENDED_DATA_PROTECTION_TYPE,
|
|
policy=policy)
|
|
|
|
body = {
|
|
'source': {
|
|
'path': (fake.SM_SOURCE_VSERVER + ':' +
|
|
fake.SM_SOURCE_VOLUME),
|
|
},
|
|
'destination': {
|
|
'path': (fake.SM_DEST_VSERVER + ':' +
|
|
fake.SM_DEST_VOLUME)
|
|
}
|
|
}
|
|
|
|
if policy:
|
|
body['policy.name'] = policy
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/snapmirror/relationships/', 'post', body=body)])
|
|
|
|
def test_create_snapmirror_vol_already_exists(self):
|
|
api_responses = netapp_api.api.NaApiError(
|
|
code=netapp_api.EREST_ERELATION_EXISTS)
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=api_responses))
|
|
|
|
response = self.client.create_snapmirror_vol(
|
|
fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME,
|
|
schedule=None,
|
|
policy=None,
|
|
relationship_type='data_protection')
|
|
self.assertIsNone(response)
|
|
self.assertTrue(self.client.send_request.called)
|
|
|
|
def test_create_snapmirror_vol_error(self):
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=netapp_api.api.NaApiError(code=123)))
|
|
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.create_snapmirror_vol,
|
|
fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME,
|
|
schedule=None,
|
|
policy=None,
|
|
relationship_type='data_protection')
|
|
self.assertTrue(self.client.send_request.called)
|
|
|
|
def test__set_snapmirror_state(self):
|
|
|
|
api_responses = [
|
|
fake.SNAPMIRROR_GET_ITER_RESPONSE_REST,
|
|
{
|
|
"job":
|
|
{
|
|
"uuid": fake.FAKE_UUID
|
|
},
|
|
"num_records": 1
|
|
}
|
|
]
|
|
|
|
expected_body = {'state': 'snapmirrored'}
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(side_effect=copy.deepcopy(api_responses)))
|
|
|
|
result = self.client._set_snapmirror_state(
|
|
'snapmirrored', None, None,
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/snapmirror/relationships/' + fake.FAKE_UUID,
|
|
'patch', body=expected_body, wait_on_accepted=True)])
|
|
|
|
expected = {
|
|
'operation-id': None,
|
|
'status': None,
|
|
'jobid': fake.FAKE_UUID,
|
|
'error-code': None,
|
|
'error-message': None,
|
|
'relationship-uuid': fake.FAKE_UUID
|
|
}
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_initialize_snapmirror_vol(self):
|
|
|
|
expected_job = {
|
|
'operation-id': None,
|
|
'status': None,
|
|
'jobid': fake.FAKE_UUID,
|
|
'error-code': None,
|
|
'error-message': None,
|
|
}
|
|
|
|
mock_set_snapmirror_state = self.mock_object(
|
|
self.client,
|
|
'_set_snapmirror_state',
|
|
mock.Mock(return_value=expected_job))
|
|
|
|
result = self.client.initialize_snapmirror_vol(
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME)
|
|
|
|
mock_set_snapmirror_state.assert_called_once_with(
|
|
'snapmirrored', None, None,
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
|
wait_result=False)
|
|
|
|
self.assertEqual(expected_job, result)
|
|
|
|
def test_modify_snapmirror_vol(self):
|
|
|
|
expected_job = {
|
|
'operation-id': None,
|
|
'status': None,
|
|
'jobid': fake.FAKE_UUID,
|
|
'error-code': None,
|
|
'error-message': None,
|
|
}
|
|
|
|
mock_set_snapmirror_state = self.mock_object(
|
|
self.client,
|
|
'_set_snapmirror_state',
|
|
mock.Mock(return_value=expected_job))
|
|
|
|
result = self.client.modify_snapmirror_vol(
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
|
None)
|
|
|
|
mock_set_snapmirror_state.assert_called_once_with(
|
|
None, None, None,
|
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
|
wait_result=False, schedule=None)
|
|
|
|
self.assertEqual(expected_job, result)
|
|
|
|
def test__abort_snapmirror(self):
|
|
return_snp = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
mock_get_snap = self.mock_object(self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=return_snp))
|
|
return_sr = fake.REST_SIMPLE_RESPONSE
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_sr))
|
|
|
|
self.client._abort_snapmirror(fake.SM_SOURCE_PATH, fake.SM_DEST_PATH)
|
|
|
|
mock_get_snap.assert_called_once_with(
|
|
source_path=fake.SM_SOURCE_PATH,
|
|
dest_path=fake.SM_DEST_PATH,
|
|
source_vserver=None,
|
|
source_volume=None,
|
|
dest_vserver=None,
|
|
dest_volume=None,
|
|
enable_tunneling=None,
|
|
list_destinations_only=None)
|
|
|
|
mock_sr.assert_has_calls([
|
|
mock.call(f'/snapmirror/relationships/{return_snp[0]["uuid"]}'
|
|
'/transfers/', 'get',
|
|
query={'state': 'transferring'}),
|
|
mock.call(f'/snapmirror/relationships/{return_snp[0]["uuid"]}'
|
|
f'/transfers/{return_sr["records"][0]["uuid"]}',
|
|
'patch', body={'state': 'aborted'}),
|
|
])
|
|
|
|
def test_abort_snapmirror_vol(self):
|
|
mock_abort = self.mock_object(self.client, '_abort_snapmirror')
|
|
self.client.abort_snapmirror_vol(fake.VSERVER_NAME,
|
|
fake.VOLUME_NAMES[0],
|
|
fake.VSERVER_NAME_2,
|
|
fake.VOLUME_NAMES[1])
|
|
mock_abort.assert_called_once_with(source_vserver=fake.VSERVER_NAME,
|
|
source_volume=fake.VOLUME_NAMES[0],
|
|
dest_vserver=fake.VSERVER_NAME_2,
|
|
dest_volume=fake.VOLUME_NAMES[1],
|
|
clear_checkpoint=False)
|
|
|
|
def test_release_snapmirror_vol(self):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
return_snp = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
mock_sd = self.mock_object(self.client, 'get_snapmirror_destinations',
|
|
mock.Mock(return_value=return_snp))
|
|
|
|
self.client.release_snapmirror_vol(fake.VSERVER_NAME,
|
|
fake.VOLUME_NAMES[0],
|
|
fake.VSERVER_NAME_2,
|
|
fake.VOLUME_NAMES[1])
|
|
|
|
mock_sd.assert_called_once_with(source_vserver=fake.VSERVER_NAME,
|
|
source_volume=fake.VOLUME_NAMES[0],
|
|
dest_vserver=fake.VSERVER_NAME_2,
|
|
dest_volume=fake.VOLUME_NAMES[1],
|
|
desired_attributes=['relationship-id'])
|
|
|
|
uuid = return_snp[0].get("uuid")
|
|
query = {"source_only": 'true'}
|
|
mock_sr.assert_called_once_with(f'/snapmirror/relationships/{uuid}',
|
|
'delete', query=query)
|
|
|
|
def test_delete_snapmirror_no_records(self):
|
|
query_uuid = {}
|
|
query_uuid['source.path'] = (fake.SM_SOURCE_VSERVER + ':' +
|
|
fake.SM_SOURCE_VOLUME)
|
|
|
|
query_uuid['destination.path'] = (fake.SM_DEST_VSERVER + ':' +
|
|
fake.SM_DEST_VOLUME)
|
|
query_uuid['fields'] = 'uuid'
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.NO_RECORDS_RESPONSE_REST))
|
|
self.client._delete_snapmirror(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/snapmirror/relationships/', 'get', query=query_uuid)
|
|
|
|
def test_delete_snapmirror(self):
|
|
query_uuid = {}
|
|
query_uuid['source.path'] = (fake.SM_SOURCE_VSERVER + ':' +
|
|
fake.SM_SOURCE_VOLUME)
|
|
|
|
query_uuid['destination.path'] = (fake.SM_DEST_VSERVER + ':' +
|
|
fake.SM_DEST_VOLUME)
|
|
query_uuid['fields'] = 'uuid'
|
|
fake_cluster = fake.FAKE_GET_CLUSTER_NODE_VERSION_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake_cluster))
|
|
self.client._delete_snapmirror(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
|
|
query_delete = {"destination_only": "true"}
|
|
snapmirror_uuid = fake_cluster.get('records')[0].get('uuid')
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/snapmirror/relationships/', 'get', query=query_uuid),
|
|
mock.call('/snapmirror/relationships/' + snapmirror_uuid, 'delete',
|
|
query=query_delete)
|
|
])
|
|
|
|
def test_get_snapmirror_destinations(self):
|
|
mock_get_sm = self.mock_object(self.client, '_get_snapmirrors')
|
|
self.client.get_snapmirror_destinations(fake.SM_SOURCE_PATH,
|
|
fake.SM_DEST_PATH,
|
|
fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
|
|
mock_get_sm.assert_called_once_with(
|
|
source_path=fake.SM_SOURCE_PATH,
|
|
dest_path=fake.SM_DEST_PATH,
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
dest_volume=fake.SM_DEST_VOLUME,
|
|
enable_tunneling=False,
|
|
list_destinations_only=True)
|
|
|
|
def test_delete_snapmirror_vol(self):
|
|
mock_delete = self.mock_object(self.client, '_delete_snapmirror')
|
|
self.client.delete_snapmirror_vol(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
mock_delete.assert_called_once_with(
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_volume=fake.SM_DEST_VOLUME)
|
|
|
|
def test_disable_fpolicy_policy(self):
|
|
query = {
|
|
'name': fake.VSERVER_NAME,
|
|
'fields': 'uuid'
|
|
}
|
|
response_svm = fake.SVMS_LIST_SIMPLE_RESPONSE_REST
|
|
self.client.vserver = fake.VSERVER_NAME
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(side_effect=[response_svm, None]))
|
|
|
|
self.client.disable_fpolicy_policy(fake.FPOLICY_POLICY_NAME)
|
|
|
|
svm_id = response_svm.get('records')[0]['uuid']
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get', query=query,
|
|
enable_tunneling=False),
|
|
mock.call(f'/protocols/fpolicy/{svm_id}/policies'
|
|
f'/{fake.FPOLICY_POLICY_NAME}', 'patch')
|
|
])
|
|
|
|
@ddt.data([fake.NO_RECORDS_RESPONSE_REST, None],
|
|
[fake.SVMS_LIST_SIMPLE_RESPONSE_REST,
|
|
netapp_api.api.NaApiError(code="1000", message="")])
|
|
def test_disable_fpolicy_policy_failure(self, side_effect):
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(side_effect=side_effect))
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.disable_fpolicy_policy,
|
|
fake.FPOLICY_POLICY_NAME)
|
|
|
|
@ddt.data({'qos_policy_group_name': None,
|
|
'adaptive_qos_policy_group_name': None},
|
|
{'qos_policy_group_name': fake.QOS_POLICY_GROUP_NAME,
|
|
'adaptive_qos_policy_group_name': None},
|
|
{'qos_policy_group_name': None,
|
|
'adaptive_qos_policy_group_name':
|
|
fake.ADAPTIVE_QOS_POLICY_GROUP_NAME},
|
|
)
|
|
@ddt.unpack
|
|
def test_create_volume_clone(self, qos_policy_group_name,
|
|
adaptive_qos_policy_group_name):
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
if qos_policy_group_name:
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
uuid = volume["uuid"]
|
|
self.mock_object(self.client,
|
|
'_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'volume_clone_split_start')
|
|
self.mock_object(
|
|
self.client.connection, 'get_vserver',
|
|
mock.Mock(return_value='fake_svm'))
|
|
set_qos_adapt_mock = self.mock_object(
|
|
self.client,
|
|
'set_qos_adaptive_policy_group_for_volume')
|
|
|
|
self.client.create_volume_clone(
|
|
fake.SHARE_NAME,
|
|
fake.PARENT_SHARE_NAME,
|
|
fake.PARENT_SNAPSHOT_NAME,
|
|
qos_policy_group=qos_policy_group_name,
|
|
adaptive_qos_policy_group=adaptive_qos_policy_group_name)
|
|
|
|
body = {
|
|
'name': fake.SHARE_NAME,
|
|
'clone.parent_volume.name': fake.PARENT_SHARE_NAME,
|
|
'clone.parent_snapshot.name': fake.PARENT_SNAPSHOT_NAME,
|
|
'nas.path': '/%s' % fake.SHARE_NAME,
|
|
'clone.is_flexclone': 'true',
|
|
'svm.name': 'fake_svm',
|
|
}
|
|
|
|
if adaptive_qos_policy_group_name is not None:
|
|
set_qos_adapt_mock.assert_called_once_with(
|
|
fake.SHARE_NAME, fake.ADAPTIVE_QOS_POLICY_GROUP_NAME
|
|
)
|
|
|
|
if qos_policy_group_name:
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.SHARE_NAME)
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/storage/volumes', 'post', body=body),
|
|
mock.call(f'/storage/volumes/{uuid}', 'patch',
|
|
body={'qos.policy.name': qos_policy_group_name})
|
|
])
|
|
else:
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes', 'post', body=body)
|
|
self.assertFalse(self.client.volume_clone_split_start.called)
|
|
|
|
@ddt.data(True, False)
|
|
def test_create_volume_split(self, split):
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, 'volume_clone_split_start')
|
|
self.mock_object(
|
|
self.client.connection, 'get_vserver',
|
|
mock.Mock(return_value='fake_svm'))
|
|
body = {
|
|
'name': fake.SHARE_NAME,
|
|
'clone.parent_volume.name': fake.PARENT_SHARE_NAME,
|
|
'clone.parent_snapshot.name': fake.PARENT_SNAPSHOT_NAME,
|
|
'nas.path': '/%s' % fake.SHARE_NAME,
|
|
'clone.is_flexclone': 'true',
|
|
'svm.name': 'fake_svm',
|
|
}
|
|
|
|
self.client.create_volume_clone(
|
|
fake.SHARE_NAME,
|
|
fake.PARENT_SHARE_NAME,
|
|
fake.PARENT_SNAPSHOT_NAME,
|
|
split=split)
|
|
|
|
if split:
|
|
self.client.volume_clone_split_start.assert_called_once_with(
|
|
fake.SHARE_NAME)
|
|
else:
|
|
self.assertFalse(self.client.volume_clone_split_start.called)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes', 'post', body=body)
|
|
|
|
def test_quiesce_snapmirror_vol(self):
|
|
mock__quiesce_snapmirror = self.mock_object(
|
|
self.client, '_quiesce_snapmirror')
|
|
|
|
self.client.quiesce_snapmirror_vol(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
|
|
mock__quiesce_snapmirror.assert_called_once_with(
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
dest_volume=fake.SM_DEST_VOLUME)
|
|
|
|
def test__quiesce_snapmirror(self):
|
|
fake_snapmirror = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
fake_uuid = fake_snapmirror[0]['uuid']
|
|
fake_body = {'state': 'paused'}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
mock_get_snap = self.mock_object(
|
|
self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=fake_snapmirror))
|
|
|
|
self.client._quiesce_snapmirror()
|
|
|
|
mock_get_snap.assert_called_once()
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/snapmirror/relationships/{fake_uuid}', 'patch', body=fake_body)
|
|
|
|
def test_break_snapmirror_vol(self):
|
|
|
|
self.mock_object(self.client, '_break_snapmirror')
|
|
|
|
self.client.break_snapmirror_vol(source_vserver=fake.SM_SOURCE_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
dest_volume=fake.SM_DEST_VOLUME)
|
|
|
|
self.client._break_snapmirror.assert_called_once_with(
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
dest_volume=fake.SM_DEST_VOLUME)
|
|
|
|
def test__break_snapmirror(self):
|
|
fake_snapmirror = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
fake_uuid = fake_snapmirror[0]['uuid']
|
|
fake_body = {'state': 'broken_off'}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
mock_get_snap = self.mock_object(
|
|
self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=fake_snapmirror))
|
|
|
|
self.client._break_snapmirror()
|
|
|
|
mock_get_snap.assert_called_once()
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/snapmirror/relationships/{fake_uuid}', 'patch', body=fake_body)
|
|
|
|
def test_resume_snapmirror_vol(self):
|
|
mock = self.mock_object(self.client, '_resume_snapmirror')
|
|
self.client.resume_snapmirror_vol(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
mock.assert_called_once_with(
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_volume=fake.SM_DEST_VOLUME)
|
|
|
|
def test_resync_snapmirror_vol(self):
|
|
mock = self.mock_object(self.client, '_resync_snapmirror')
|
|
self.client.resync_snapmirror_vol(fake.SM_SOURCE_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_DEST_VOLUME)
|
|
mock.assert_called_once_with(
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_volume=fake.SM_DEST_VOLUME)
|
|
|
|
@ddt.data('async', 'sync')
|
|
def test__resume_snapmirror(self, snapmirror_policy):
|
|
api_response = copy.deepcopy(fake.REST_GET_SNAPMIRRORS_RESPONSE)
|
|
api_response[0]['policy-type'] = snapmirror_policy
|
|
mock_snapmirror = self.mock_object(
|
|
self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=api_response))
|
|
mock_request = self.mock_object(self.client, 'send_request')
|
|
|
|
snapmirror_uuid = fake.FAKE_UUID
|
|
|
|
body_resync = {}
|
|
if snapmirror_policy == 'async':
|
|
body_resync['state'] = 'snapmirrored'
|
|
elif snapmirror_policy == 'sync':
|
|
body_resync['state'] = 'in_sync'
|
|
|
|
self.client._resume_snapmirror(fake.SM_SOURCE_PATH, fake.SM_DEST_PATH)
|
|
|
|
mock_request.assert_called_once_with('/snapmirror/relationships/' +
|
|
snapmirror_uuid, 'patch',
|
|
body=body_resync,
|
|
wait_on_accepted=False)
|
|
|
|
mock_snapmirror.assert_called_once_with(
|
|
source_path=fake.SM_SOURCE_PATH,
|
|
dest_path=fake.SM_DEST_PATH,
|
|
source_vserver=None,
|
|
source_volume=None,
|
|
dest_vserver=None,
|
|
dest_volume=None,
|
|
enable_tunneling=None,
|
|
list_destinations_only=None)
|
|
|
|
def test__resync_snapmirror(self):
|
|
mock = self.mock_object(self.client, '_resume_snapmirror')
|
|
self.client._resume_snapmirror(fake.SM_SOURCE_PATH,
|
|
fake.SM_DEST_PATH)
|
|
mock.assert_called_once_with(fake.SM_SOURCE_PATH, fake.SM_DEST_PATH)
|
|
|
|
def test_add_nfs_export_rule(self):
|
|
|
|
mock_get_nfs_export_rule_indices = self.mock_object(
|
|
self.client, '_get_nfs_export_rule_indices',
|
|
mock.Mock(return_value=[]))
|
|
mock_add_nfs_export_rule = self.mock_object(
|
|
self.client, '_add_nfs_export_rule')
|
|
mock_update_nfs_export_rule = self.mock_object(
|
|
self.client, '_update_nfs_export_rule')
|
|
auth_methods = ['sys']
|
|
|
|
self.client.add_nfs_export_rule(fake.EXPORT_POLICY_NAME,
|
|
fake.IP_ADDRESS,
|
|
False,
|
|
auth_methods)
|
|
|
|
mock_get_nfs_export_rule_indices.assert_called_once_with(
|
|
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS)
|
|
mock_add_nfs_export_rule.assert_called_once_with(
|
|
fake.EXPORT_POLICY_NAME, fake.IP_ADDRESS, False, auth_methods)
|
|
self.assertFalse(mock_update_nfs_export_rule.called)
|
|
|
|
def test_set_qos_policy_group_for_volume(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
mock_get_volume = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request')
|
|
|
|
self.client.set_qos_policy_group_for_volume(
|
|
volume['name'], fake.QOS_POLICY_GROUP_NAME)
|
|
|
|
mock_get_volume.assert_called_once_with(vol_name=volume['name'])
|
|
|
|
body = {'qos.policy.name': fake.QOS_POLICY_GROUP_NAME}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{volume["uuid"]}', 'patch', body=body)
|
|
|
|
def test__update_snapmirror(self):
|
|
api_response = copy.deepcopy(fake.REST_GET_SNAPMIRRORS_RESPONSE)
|
|
mock_snapmirror = self.mock_object(
|
|
self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=api_response))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.client._update_snapmirror(fake.SM_SOURCE_PATH,
|
|
fake.SM_DEST_PATH,
|
|
fake.SM_SOURCE_VSERVER,
|
|
fake.SM_DEST_VSERVER,
|
|
fake.SM_SOURCE_VOLUME,
|
|
fake.SM_DEST_VOLUME)
|
|
mock_sr.assert_called_once()
|
|
mock_snapmirror.assert_called_once_with(
|
|
source_path=fake.SM_SOURCE_PATH,
|
|
dest_path=fake.SM_DEST_PATH,
|
|
source_vserver=fake.SM_SOURCE_VSERVER,
|
|
source_volume=fake.SM_SOURCE_VOLUME,
|
|
dest_vserver=fake.SM_DEST_VSERVER,
|
|
dest_volume=fake.SM_DEST_VOLUME,
|
|
enable_tunneling=None,
|
|
list_destinations_only=None)
|
|
|
|
def test_get_cluster_name(self):
|
|
"""Get all available cluster nodes."""
|
|
|
|
return_value = fake.FAKE_GET_CLUSTER_NODE_VERSION_REST
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
test_result = self.client.get_cluster_name()
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/cluster', 'get', enable_tunneling=False
|
|
)
|
|
|
|
expected_result = return_value.get('name')
|
|
|
|
self.assertEqual(test_result, expected_result)
|
|
|
|
@ddt.data(True, False)
|
|
def test_check_volume_clone_split_completed(self, clone):
|
|
mock__get_volume_by_args = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value={'clone': {'is_flexclone': clone}}))
|
|
|
|
res = self.client.check_volume_clone_split_completed(
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
mock__get_volume_by_args.assert_called_once_with(
|
|
vol_name=fake.VOLUME_NAMES[0], fields='clone.is_flexclone')
|
|
self.assertEqual(not clone, res)
|
|
|
|
def test_rehost_volume(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
self.client.rehost_volume("fake_vol", "fake_svm", "fake_svm_2")
|
|
body = {
|
|
"vserver": "fake_svm",
|
|
"volume": "fake_vol",
|
|
"destination_vserver": "fake_svm_2"
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
"/private/cli/volume/rehost", 'post', body=body)
|
|
|
|
def test_get_net_options(self):
|
|
|
|
res = self.client.get_net_options()
|
|
|
|
self.assertTrue(res['ipv6-enabled'])
|
|
|
|
def test_set_qos_adaptive_policy_group_for_volume(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
mock_get_volume = self.mock_object(
|
|
self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request')
|
|
|
|
self.client.set_qos_adaptive_policy_group_for_volume(
|
|
volume['name'], fake.QOS_POLICY_GROUP_NAME)
|
|
|
|
mock_get_volume.assert_called_once_with(vol_name=volume['name'])
|
|
|
|
body = {'qos.policy.name': fake.QOS_POLICY_GROUP_NAME}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/storage/volumes/{volume["uuid"]}', 'patch', body=body)
|
|
|
|
def test__list_vservers(self):
|
|
api_response = fake.VSERVER_DATA_LIST_RESPONSE_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result = self.client._list_vservers()
|
|
query = {
|
|
'fields': 'name',
|
|
}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get', query=query,
|
|
enable_tunneling=False)])
|
|
self.assertListEqual(
|
|
[fake.VSERVER_NAME, fake.VSERVER_NAME_2], result)
|
|
|
|
def test_list_vservers_not_found(self):
|
|
api_response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result = self.client._list_vservers()
|
|
self.assertListEqual([], result)
|
|
|
|
def test_get_ems_log_destination_vserver(self):
|
|
mock_list_vservers = self.mock_object(
|
|
self.client,
|
|
'_list_vservers',
|
|
mock.Mock(return_value=[fake.VSERVER_NAME]))
|
|
result = self.client._get_ems_log_destination_vserver()
|
|
mock_list_vservers.assert_called_once_with()
|
|
self.assertEqual(fake.VSERVER_NAME, result)
|
|
|
|
def test_get_ems_log_destination_vserver_not_found(self):
|
|
mock_list_vservers = self.mock_object(
|
|
self.client,
|
|
'_list_vservers',
|
|
mock.Mock(return_value=[]))
|
|
|
|
self.assertRaises(exception.NotFound,
|
|
self.client._get_ems_log_destination_vserver)
|
|
|
|
mock_list_vservers.assert_called_once_with()
|
|
|
|
def test_send_ems_log_message(self):
|
|
|
|
message_dict = {
|
|
'computer-name': '25-dev-vm',
|
|
'event-source': 'Cinder driver NetApp_iSCSI_Cluster_direct',
|
|
'app-version': '20.1.0.dev|vendor|Linux-5.4.0-120-generic-x86_64',
|
|
'category': 'provisioning',
|
|
'log-level': '5',
|
|
'auto-support': 'false',
|
|
'event-id': '1',
|
|
'event-description':
|
|
'{"pools": {"vserver": "vserver_name",'
|
|
+ '"aggregates": [], "flexvols": ["flexvol_01"]}}'
|
|
}
|
|
|
|
body = {
|
|
'computer_name': message_dict['computer-name'],
|
|
'event_source': message_dict['event-source'],
|
|
'app_version': message_dict['app-version'],
|
|
'category': message_dict['category'],
|
|
'severity': 'notice',
|
|
'autosupport_required': message_dict['auto-support'] == 'true',
|
|
'event_id': message_dict['event-id'],
|
|
'event_description': message_dict['event-description'],
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_ems_log_destination_vserver',
|
|
mock.Mock(return_value='vserver_name'))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.send_ems_log_message(message_dict)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/support/ems/application-logs', 'post', body=body)
|
|
|
|
@ddt.data('cp_phase_times', 'domain_busy')
|
|
def test_get_performance_counter_info(self, counter_name):
|
|
|
|
response1 = fake.PERF_COUNTER_LIST_INFO_WAFL_RESPONSE_REST
|
|
response2 = fake.PERF_COUNTER_TABLE_ROWS_WAFL
|
|
|
|
object_name = 'wafl'
|
|
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=[response1, response2]))
|
|
|
|
result = self.client.get_performance_counter_info(object_name,
|
|
counter_name)
|
|
|
|
expected = {
|
|
'name': 'cp_phase_times',
|
|
'base-counter': 'total_cp_msecs',
|
|
'labels': fake.PERF_COUNTER_TOTAL_CP_MSECS_LABELS_RESULT,
|
|
}
|
|
|
|
query1 = {
|
|
'counter_schemas.name': counter_name,
|
|
'fields': 'counter_schemas.*'
|
|
}
|
|
|
|
query2 = {
|
|
'counters.name': counter_name,
|
|
'fields': 'counters.*'
|
|
}
|
|
|
|
if counter_name == 'domain_busy':
|
|
expected['name'] = 'domain_busy'
|
|
expected['labels'] = (
|
|
fake.PERF_COUNTER_TOTAL_CP_MSECS_LABELS_REST)
|
|
query1['counter_schemas.name'] = 'domain_busy_percent'
|
|
query2['counters.name'] = 'domain_busy_percent'
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
mock_send_request.assert_has_calls([
|
|
mock.call(f'/cluster/counter/tables/{object_name}',
|
|
'get', query=query1),
|
|
mock.call(f'/cluster/counter/tables/{object_name}/rows',
|
|
'get', query=query2, enable_tunneling=False),
|
|
])
|
|
|
|
def test_get_performance_counter_info_not_found_rows(self):
|
|
response1 = fake.PERF_COUNTER_LIST_INFO_WAFL_RESPONSE_REST
|
|
response2 = fake.NO_RECORDS_RESPONSE_REST
|
|
|
|
object_name = 'wafl'
|
|
counter_name = 'cp_phase_times'
|
|
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=[response1, response2]))
|
|
|
|
result = self.client.get_performance_counter_info(object_name,
|
|
counter_name)
|
|
|
|
expected = {
|
|
'name': 'cp_phase_times',
|
|
'base-counter': 'total_cp_msecs',
|
|
'labels': [],
|
|
}
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_performance_instance_uuids(self):
|
|
response = fake.PERF_COUNTER_TABLE_ROWS_WAFL
|
|
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
|
|
object_name = 'wafl'
|
|
result = self.client.get_performance_instance_uuids(
|
|
object_name, fake.NODE_NAME)
|
|
|
|
expected = [fake.NODE_NAME + ':wafl']
|
|
self.assertEqual(expected, result)
|
|
|
|
query = {
|
|
'id': fake.NODE_NAME + ':*',
|
|
}
|
|
mock_send_request.assert_called_once_with(
|
|
f'/cluster/counter/tables/{object_name}/rows',
|
|
'get', query=query, enable_tunneling=False)
|
|
|
|
def test_get_performance_counters(self):
|
|
response = fake.PERF_GET_INSTANCES_PROCESSOR_RESPONSE_REST
|
|
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
|
|
instance_uuids = [
|
|
fake.NODE_NAME + ':processor0',
|
|
fake.NODE_NAME + ':processor1',
|
|
]
|
|
object_name = 'processor'
|
|
counter_names = ['domain_busy', 'processor_elapsed_time']
|
|
rest_counter_names = ['domain_busy_percent', 'elapsed_time']
|
|
result = self.client.get_performance_counters(object_name,
|
|
instance_uuids,
|
|
counter_names)
|
|
|
|
expected = fake.PERF_COUNTERS_PROCESSOR_EXPECTED
|
|
self.assertEqual(expected, result)
|
|
|
|
query = {
|
|
'id': '|'.join(instance_uuids),
|
|
'counters.name': '|'.join(rest_counter_names),
|
|
'fields': 'id,counter_table.name,counters.*',
|
|
}
|
|
|
|
mock_send_request.assert_called_once_with(
|
|
f'/cluster/counter/tables/{object_name}/rows',
|
|
'get', query=query)
|
|
|
|
def test__get_deleted_nfs_export_policies(self):
|
|
|
|
api_response = fake.DELETED_EXPORT_POLICY_GET_ITER_RESPONSE_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client._get_deleted_nfs_export_policies()
|
|
|
|
query = {
|
|
'name': 'deleted_manila_*',
|
|
'fields': 'name,svm.name',
|
|
}
|
|
|
|
self.assertSequenceEqual(fake.DELETED_EXPORT_POLICIES, result)
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/nfs/export-policies',
|
|
'get', query=query)])
|
|
|
|
def test_prune_deleted_nfs_export_policies(self):
|
|
self.mock_object(self.client, '_get_deleted_nfs_export_policies',
|
|
mock.Mock(return_value=fake.DELETED_EXPORT_POLICIES))
|
|
self.mock_object(self.client, 'delete_nfs_export_policy')
|
|
|
|
self.client.prune_deleted_nfs_export_policies()
|
|
|
|
self.assertTrue(self.client.delete_nfs_export_policy.called)
|
|
|
|
self.client.delete_nfs_export_policy.assert_has_calls([
|
|
mock.call(fake.DELETED_EXPORT_POLICIES[fake.VSERVER_NAME][0]),
|
|
mock.call(fake.DELETED_EXPORT_POLICIES[fake.VSERVER_NAME][1]),
|
|
mock.call(fake.DELETED_EXPORT_POLICIES[fake.VSERVER_NAME_2][0]),
|
|
])
|
|
|
|
def test_prune_deleted_nfs_export_policies_api_error(self):
|
|
self.mock_object(self.client,
|
|
'_get_deleted_nfs_export_policies',
|
|
mock.Mock(return_value=fake.DELETED_EXPORT_POLICIES))
|
|
self.mock_object(self.client,
|
|
'delete_nfs_export_policy',
|
|
self._mock_api_error())
|
|
|
|
self.client.prune_deleted_nfs_export_policies()
|
|
|
|
def test__get_security_key_manager_nve_support_enabled(self):
|
|
api_response = fake.SECUTITY_KEY_MANAGER_SUPPORT_RESPONSE_TRUE_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client._get_security_key_manager_nve_support()
|
|
|
|
self.assertTrue(result)
|
|
|
|
query = {'fields': 'volume_encryption.*'}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/security/key-managers', 'get', query=query)])
|
|
|
|
def test__get_security_key_manager_nve_support_disabled(self):
|
|
api_response = fake.SECUTITY_KEY_MANAGER_SUPPORT_RESPONSE_FALSE_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client._get_security_key_manager_nve_support()
|
|
|
|
self.assertFalse(result)
|
|
|
|
query = {'fields': 'volume_encryption.*'}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/security/key-managers', 'get', query=query)])
|
|
|
|
def test__get_security_key_manager_nve_support_no_records(self):
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=fake.NO_RECORDS_RESPONSE_REST))
|
|
|
|
result = self.client._get_security_key_manager_nve_support()
|
|
|
|
self.assertFalse(result)
|
|
|
|
query = {'fields': 'volume_encryption.*'}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/security/key-managers', 'get', query=query)])
|
|
|
|
def test__get_security_key_manager_nve_support_no_license(self):
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
self._mock_api_error())
|
|
|
|
result = self.client._get_security_key_manager_nve_support()
|
|
|
|
self.assertFalse(result)
|
|
|
|
query = {'fields': 'volume_encryption.*'}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/security/key-managers', 'get', query=query)])
|
|
|
|
def test_get_nfs_config_default(self):
|
|
api_response = fake.NFS_CONFIG_DEFAULT_RESULT_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_nfs_config_default(['tcp-max-xfer-size',
|
|
'udp-max-xfer-size'])
|
|
expected = {
|
|
'tcp-max-xfer-size': '65536',
|
|
'udp-max-xfer-size': '32768',
|
|
}
|
|
self.assertEqual(expected, result)
|
|
|
|
query = {'fields': 'transport.*'}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/nfs/services/', 'get', query=query)
|
|
|
|
def test_get_kerberos_service_principal_name(self):
|
|
|
|
spn = self.client._get_kerberos_service_principal_name(
|
|
fake.KERBEROS_SECURITY_SERVICE, fake.VSERVER_NAME
|
|
)
|
|
self.assertEqual(fake.KERBEROS_SERVICE_PRINCIPAL_NAME, spn)
|
|
|
|
def test_get_cifs_server_name(self):
|
|
|
|
expected_return = 'FAKE-VSE-SERVER'
|
|
|
|
cifs_server = self.client._get_cifs_server_name(fake.VSERVER_NAME)
|
|
|
|
self.assertEqual(expected_return, cifs_server)
|
|
|
|
def test_list_network_interfaces(self):
|
|
|
|
api_response = fake.GENERIC_NETWORK_INTERFACES_GET_REPONSE
|
|
expected_result = [fake.LIF_NAME]
|
|
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
|
|
fake_query = {
|
|
'fields': 'name'
|
|
}
|
|
|
|
result = self.client.list_network_interfaces()
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ip/interfaces', 'get', query=fake_query)])
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_create_kerberos_realm(self):
|
|
|
|
fake_security = fake.KERBEROS_SECURITY_SERVICE
|
|
|
|
fake_body = {
|
|
'comment': '',
|
|
'kdc.ip': fake_security['server'],
|
|
'kdc.port': '88',
|
|
'kdc.vendor': 'other',
|
|
'name': fake_security['domain'].upper(),
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_kerberos_realm(fake.KERBEROS_SECURITY_SERVICE)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/nfs/kerberos/realms', 'post', body=fake_body)
|
|
|
|
def test_configure_kerberos(self):
|
|
|
|
fake_api_response = fake.NFS_LIFS_REST
|
|
fake_security = fake.KERBEROS_SECURITY_SERVICE
|
|
fake_keberos_name = fake.KERBEROS_SERVICE_PRINCIPAL_NAME
|
|
|
|
fake_body = {
|
|
'password': fake_security['password'],
|
|
'user': fake_security['user'],
|
|
'interface.name': fake.LIF_NAME,
|
|
'enabled': True,
|
|
'spn': fake_keberos_name
|
|
}
|
|
|
|
self.mock_object(self.client, 'configure_dns')
|
|
self_get_kerberos = self.mock_object(
|
|
self.client, '_get_kerberos_service_principal_name',
|
|
mock.Mock(return_value=fake_keberos_name))
|
|
self.mock_object(self.client, 'get_network_interfaces',
|
|
mock.Mock(return_value=fake_api_response))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.configure_kerberos(fake.KERBEROS_SECURITY_SERVICE,
|
|
fake.VSERVER_NAME)
|
|
|
|
self.client.configure_dns.assert_called_once_with(
|
|
fake.KERBEROS_SECURITY_SERVICE, vserver_name=fake.VSERVER_NAME)
|
|
self_get_kerberos.assert_called_once_with(
|
|
fake.KERBEROS_SECURITY_SERVICE, fake.VSERVER_NAME)
|
|
self.client.get_network_interfaces.assert_called_once_with()
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/nfs/kerberos/interfaces/fake_uuid_1',
|
|
'patch', body=fake_body),
|
|
mock.call('/protocols/nfs/kerberos/interfaces/fake_uuid_2',
|
|
'patch', body=fake_body),
|
|
mock.call('/protocols/nfs/kerberos/interfaces/fake_uuid_3',
|
|
'patch', body=fake_body)
|
|
])
|
|
|
|
@ddt.data(fake.CIFS_SECURITY_SERVICE, fake.CIFS_SECURITY_SERVICE_3)
|
|
def test_configure_active_directory(self, security_service):
|
|
|
|
fake_security = copy.deepcopy(security_service)
|
|
|
|
fake_body1 = {
|
|
'ad_domain.user': fake_security['user'],
|
|
'ad_domain.password': fake_security['password'],
|
|
'force': 'true',
|
|
'name': 'FAKE-VSE-SERVER',
|
|
'ad_domain.fqdn': fake_security['domain'],
|
|
}
|
|
|
|
self.mock_object(self.client, 'configure_dns')
|
|
self.mock_object(self.client, 'set_preferred_dc')
|
|
self.mock_object(self.client, '_get_cifs_server_name',
|
|
mock.Mock(return_value='FAKE-VSE-SERVER'))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.configure_active_directory(fake_security,
|
|
fake.VSERVER_NAME)
|
|
|
|
self.client.configure_dns.assert_called_once_with(
|
|
fake_security, vserver_name=fake.VSERVER_NAME)
|
|
self.client.set_preferred_dc.assert_called_once_with(
|
|
fake_security, fake.VSERVER_NAME)
|
|
self.client._get_cifs_server_name.assert_called_once_with(
|
|
fake.VSERVER_NAME)
|
|
|
|
if fake_security['ou'] is not None:
|
|
fake_body1['ad_domain.organizational_unit'] = fake_security['ou']
|
|
fake_body2 = fake_body1
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/cifs/services', 'post', body=fake_body2)
|
|
else:
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/cifs/services', 'post', body=fake_body1)
|
|
|
|
def test__create_ldap_client_ad(self):
|
|
mock_dns = self.mock_object(self.client, 'configure_dns')
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
security_service = {
|
|
'domain': 'fake_domain',
|
|
'user': 'fake_user',
|
|
'ou': 'fake_ou',
|
|
'dns_ip': 'fake_ip',
|
|
'password': 'fake_password'
|
|
}
|
|
|
|
ad_domain = security_service.get('domain')
|
|
body = {
|
|
'port': '389',
|
|
'schema': 'MS-AD-BIS',
|
|
'bind_dn': (security_service.get('user') + '@' + ad_domain),
|
|
'bind_password': security_service.get('password'),
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'base_dn': security_service.get('ou'),
|
|
'ad_domain': security_service.get('domain'),
|
|
}
|
|
|
|
self.client._create_ldap_client(security_service,
|
|
vserver_name=fake.VSERVER_NAME)
|
|
mock_dns.assert_called_once_with(security_service)
|
|
mock_sr.assert_called_once_with('/name-services/ldap', 'post',
|
|
body=body)
|
|
|
|
def test__create_ldap_client_linux(self):
|
|
mock_dns = self.mock_object(self.client, 'configure_dns')
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
security_service = {
|
|
'server': 'fake_server',
|
|
'user': 'fake_user',
|
|
'ou': 'fake_ou',
|
|
'dns_ip': 'fake_ip'
|
|
}
|
|
|
|
body = {
|
|
'port': '389',
|
|
'schema': 'RFC-2307',
|
|
'bind_dn': security_service.get('user'),
|
|
'bind_password': security_service.get('password'),
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'base_dn': security_service.get('ou'),
|
|
'servers': [security_service.get('server')]
|
|
}
|
|
|
|
self.client._create_ldap_client(security_service,
|
|
vserver_name=fake.VSERVER_NAME)
|
|
mock_dns.assert_called_once_with(security_service)
|
|
mock_sr.assert_called_once_with('/name-services/ldap', 'post',
|
|
body=body)
|
|
|
|
def test_configure_dns_already_present(self):
|
|
dns_config = {
|
|
'domains': [fake.KERBEROS_SECURITY_SERVICE['domain']],
|
|
'dns-ips': [fake.KERBEROS_SECURITY_SERVICE['dns_ip']],
|
|
}
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value=dns_config))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.FAKE_VOL_MOVE_STATUS))
|
|
|
|
security_service = copy.deepcopy(fake.KERBEROS_SECURITY_SERVICE)
|
|
self.client.configure_dns(security_service)
|
|
|
|
net_dns_create_args = {
|
|
'domains': [security_service['domain']],
|
|
'servers': [security_service['dns_ip']],
|
|
}
|
|
|
|
uuid = fake.FAKE_VOL_MOVE_STATUS['records'][0]['uuid']
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get',
|
|
query={'name': None, 'fields': 'uuid'}),
|
|
mock.call(f'/name-services/dns/{uuid}', 'patch',
|
|
body=net_dns_create_args)])
|
|
|
|
def test_configure_dns_for_active_directory(self):
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.FAKE_VOL_MOVE_STATUS))
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value={}))
|
|
|
|
security_service = copy.deepcopy(fake.CIFS_SECURITY_SERVICE)
|
|
self.client.configure_dns(security_service)
|
|
|
|
net_dns_create_args = {
|
|
'domains': [security_service['domain']],
|
|
'servers': [security_service['dns_ip']],
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get',
|
|
query={'name': None, 'fields': 'uuid'}),
|
|
mock.call('/name-services/dns', 'post', body=net_dns_create_args)])
|
|
|
|
def test_configure_dns_multiple_dns_ip(self):
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.FAKE_VOL_MOVE_STATUS))
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value={}))
|
|
mock_dns_ips = '10.0.0.5, 10.0.0.6, 10.0.0.7'
|
|
security_service = copy.deepcopy(fake.CIFS_SECURITY_SERVICE)
|
|
security_service['dns_ip'] = mock_dns_ips
|
|
|
|
args_dns = {'domains': [security_service['domain']],
|
|
'servers': ['10.0.0.5',
|
|
'10.0.0.6',
|
|
'10.0.0.7']}
|
|
self.client.configure_dns(security_service)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get',
|
|
query={'name': None, 'fields': 'uuid'}),
|
|
mock.call('/name-services/dns', 'post', body=args_dns)])
|
|
|
|
def test_configure_dns_for_kerberos(self):
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.FAKE_VOL_MOVE_STATUS))
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value={}))
|
|
|
|
security_service = copy.deepcopy(fake.KERBEROS_SECURITY_SERVICE)
|
|
self.client.configure_dns(security_service)
|
|
|
|
net_dns_create_args = {
|
|
'domains': [security_service['domain']],
|
|
'servers': [security_service['dns_ip']],
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get',
|
|
query={'name': None, 'fields': 'uuid'}),
|
|
mock.call('/name-services/dns', 'post', body=net_dns_create_args)])
|
|
|
|
def test_configure_dns_api_error(self):
|
|
self.mock_object(self.client, 'send_request', self._mock_api_error())
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value={}))
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value={}))
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.configure_dns,
|
|
copy.deepcopy(fake.KERBEROS_SECURITY_SERVICE))
|
|
|
|
def test_get_dns_config_no_response(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=netapp_api.api.NaApiError))
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value={}))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_dns_config)
|
|
|
|
def test_get_dns_config(self):
|
|
api_response = fake.DNS_REST_RESPONSE
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
fake_uuid = fake.FAKE_VOL_MOVE_STATUS['records'][0]['svm']['uuid']
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_uuid))
|
|
|
|
result = self.client.get_dns_config()
|
|
|
|
expected_result = {
|
|
'dns-state': 'true',
|
|
'domains': ['example.com', 'example2.example3.com'],
|
|
'dns-ips': ['10.224.65.20', '2001:db08:a0b:12f0::1']
|
|
}
|
|
self.assertEqual(expected_result, result)
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/name-services/dns/{fake_uuid}', 'get')
|
|
|
|
@ddt.data(fake.LDAP_AD_SECURITY_SERVICE, fake.CIFS_SECURITY_SERVICE_3,
|
|
fake.KERBEROS_SECURITY_SERVICE)
|
|
def test_setup_security_services(self, security_service):
|
|
fake_response = fake.FAKE_GET_CLUSTER_NODE_VERSION_REST
|
|
mock_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake_response))
|
|
self.mock_object(self.client, 'configure_ldap')
|
|
self.mock_object(self.client, 'configure_active_directory')
|
|
self.mock_object(self.client, 'configure_cifs_options')
|
|
self.mock_object(self.client, 'create_kerberos_realm')
|
|
self.mock_object(self.client, 'configure_kerberos')
|
|
|
|
ss_copy = copy.deepcopy(security_service)
|
|
self.client.setup_security_services([ss_copy], self.client,
|
|
'fake_vservername')
|
|
uuid = fake_response.get('records')[0].get('uuid')
|
|
body = {
|
|
'nsswitch.namemap': ['ldap', 'files'],
|
|
'nsswitch.group': ['ldap', 'files'],
|
|
'nsswitch.netgroup': ['ldap', 'files'],
|
|
'nsswitch.passwd': ['ldap', 'files'],
|
|
}
|
|
mock_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get',
|
|
query={'name': 'fake_vservername', 'fields': 'uuid'}),
|
|
mock.call(f'/svm/svms/{uuid}', 'patch', body=body)])
|
|
|
|
def test_modify_ldap_ad(self):
|
|
fake_svm_uuid = fake.FAKE_UUID
|
|
mock_svm_uuid = self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_svm_uuid))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
security_service = {
|
|
'domain': 'fake_domain',
|
|
'user': 'fake_user',
|
|
'ou': 'fake_ou',
|
|
'dns_ip': 'fake_ip',
|
|
'password': 'fake_password'
|
|
}
|
|
|
|
ad_domain = security_service.get('domain')
|
|
body = {
|
|
'port': '389',
|
|
'schema': 'MS-AD-BIS',
|
|
'bind_dn': (security_service.get('user') + '@' + ad_domain),
|
|
'bind_password': security_service.get('password'),
|
|
'base_dn': security_service.get('ou'),
|
|
'ad_domain': security_service.get('domain'),
|
|
}
|
|
|
|
self.client.modify_ldap(security_service, None)
|
|
mock_svm_uuid.assert_called_once_with(None)
|
|
mock_sr.assert_called_once_with(f'/name-services/ldap/{fake_svm_uuid}',
|
|
'patch', body=body)
|
|
|
|
def test_modify_ldap_linux(self):
|
|
fake_svm_uuid = fake.FAKE_UUID
|
|
mock_svm_uuid = self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_svm_uuid))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
security_service = {
|
|
'server': 'fake_server',
|
|
'user': 'fake_user',
|
|
'ou': 'fake_ou',
|
|
'dns_ip': 'fake_ip'
|
|
}
|
|
|
|
body = {
|
|
'port': '389',
|
|
'schema': 'RFC-2307',
|
|
'bind_dn': security_service.get('user'),
|
|
'bind_password': security_service.get('password'),
|
|
'base_dn': security_service.get('ou'),
|
|
'servers': [security_service.get('server')]
|
|
}
|
|
|
|
self.client.modify_ldap(security_service, None)
|
|
mock_svm_uuid.assert_called_once_with(None)
|
|
mock_sr.assert_called_once_with(f'/name-services/ldap/{fake_svm_uuid}',
|
|
'patch', body=body)
|
|
|
|
def test_update_kerberos_realm(self):
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
fake_uuid = fake.FAKE_UUID
|
|
self.mock_object(self.client, 'send_request')
|
|
self.client.update_kerberos_realm(fake.KERBEROS_SECURITY_SERVICE)
|
|
fake_domain = fake.KERBEROS_SECURITY_SERVICE['domain']
|
|
body = {
|
|
'kdc-ip': fake.KERBEROS_SECURITY_SERVICE['server'],
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(
|
|
f'/protocols/nfs/kerberos/realms/{fake_uuid}/{fake_domain}',
|
|
'patch', body=body)])
|
|
|
|
def test__get_unique_svm_by_name(self):
|
|
response = fake.SVMS_LIST_SIMPLE_RESPONSE_REST
|
|
svm = fake.SVM_ITEM_SIMPLE_RESPONSE_REST['uuid']
|
|
|
|
fake_query = {
|
|
'name': fake.VSERVER_NAME,
|
|
'fields': 'uuid'
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
|
|
result = self.client._get_unique_svm_by_name(
|
|
fake.VSERVER_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/svm/svms', 'get', query=fake_query)
|
|
|
|
self.assertEqual(svm, result)
|
|
|
|
def test_update_dns_configuration(self):
|
|
dns_config = {
|
|
'domains': [fake.KERBEROS_SECURITY_SERVICE['domain']],
|
|
'dns-ips': [fake.KERBEROS_SECURITY_SERVICE['dns_ip']],
|
|
}
|
|
|
|
body = {
|
|
'domains': [fake.KERBEROS_SECURITY_SERVICE['domain']],
|
|
'servers': [fake.KERBEROS_SECURITY_SERVICE['dns_ip']]
|
|
}
|
|
|
|
fake_uuid = 'fake_uuid'
|
|
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value=dns_config))
|
|
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_uuid))
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.FAKE_VOL_MOVE_STATUS))
|
|
|
|
self.client.configure_dns(fake.KERBEROS_SECURITY_SERVICE)
|
|
body = {
|
|
'domains': [fake.KERBEROS_SECURITY_SERVICE['domain']],
|
|
'servers': [fake.KERBEROS_SECURITY_SERVICE['dns_ip']]
|
|
}
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/name-services/dns/{fake_uuid}', 'patch', body=body)
|
|
|
|
def test_remove_preferred_dcs(self):
|
|
svm_uuid = copy.deepcopy(fake.FAKE_UUID)
|
|
fqdn = copy.deepcopy(fake.PREFERRED_DC_REST.get('fqdn'))
|
|
server_ip = copy.deepcopy(fake.PREFERRED_DC_REST.get('server_ip'))
|
|
fake_response = copy.deepcopy(fake.PREFERRED_DC_REST)
|
|
fake_ss = copy.deepcopy(fake.LDAP_AD_SECURITY_SERVICE)
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake_response))
|
|
self.client.remove_preferred_dcs(fake_ss, svm_uuid)
|
|
self.client.send_request.has_calls([
|
|
mock.call(f'/protocols/cifs/domains/{svm_uuid}/'
|
|
f'preferred-domain-controllers/', 'get'),
|
|
mock.call(f'/protocols/cifs/domains/{svm_uuid}/'
|
|
f'preferred-domain-controllers/{fqdn}/{server_ip}',
|
|
'delete', query=fqdn)
|
|
])
|
|
|
|
def test_remove_preferred_dcs_api_error(self):
|
|
fake_response = copy.deepcopy(fake.PREFERRED_DC_REST)
|
|
fake_ss = copy.deepcopy(fake.LDAP_AD_SECURITY_SERVICE)
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake_response))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=netapp_api.api.NaApiError))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.remove_preferred_dcs,
|
|
fake_ss, fake.FAKE_UUID)
|
|
|
|
def test_set_preferred_dc(self):
|
|
fake_ss = copy.deepcopy(fake.LDAP_AD_SECURITY_SERVICE_WITH_SERVER)
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
|
|
self.client.set_preferred_dc(fake_ss, fake.VSERVER_NAME)
|
|
|
|
self.client._get_unique_svm_by_name.assert_called_once_with(
|
|
fake.VSERVER_NAME)
|
|
|
|
query = {
|
|
'fqdn': fake_ss['domain'],
|
|
'skip_config_validation': 'false',
|
|
'server_ip': ['10.10.10.1']
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/cifs/domains/{fake.FAKE_UUID}'
|
|
'/preferred-domain-controllers', 'post', query=query)
|
|
|
|
@ddt.data(None, 'cluster_name')
|
|
def test_create_vserver_peer(self, cluster_name):
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_vserver_peer(fake.VSERVER_NAME,
|
|
fake.VSERVER_PEER_NAME,
|
|
peer_cluster_name=cluster_name)
|
|
|
|
body = {
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'peer.svm.name': fake.VSERVER_PEER_NAME,
|
|
'applications': ['snapmirror'],
|
|
}
|
|
if cluster_name:
|
|
body['peer.cluster.name'] = cluster_name
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/peers', 'post', body=body,
|
|
enable_tunneling=False)])
|
|
|
|
def test__get_svm_peer_uuid(self):
|
|
response = {
|
|
"records": [{
|
|
"uuid": "fake-vserver-uuid",
|
|
"name": fake.VSERVER_NAME,
|
|
"svm": {
|
|
"name": fake.VSERVER_NAME,
|
|
},
|
|
"peer": {
|
|
"svm": {
|
|
"name": fake.VSERVER_PEER_NAME,
|
|
}
|
|
}
|
|
}],
|
|
}
|
|
expected_result = "fake-vserver-uuid"
|
|
return_value = response['records'][0]['uuid']
|
|
self.mock_object(self.client, '_get_svm_peer_uuid',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client._get_svm_peer_uuid(
|
|
fake.VSERVER_NAME, fake.VSERVER_PEER_NAME)
|
|
|
|
self.client._get_svm_peer_uuid.assert_called_once_with(
|
|
fake.VSERVER_NAME, fake.VSERVER_PEER_NAME)
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
def test_accept_vserver_peer(self):
|
|
|
|
fake_resp = {
|
|
'records': [{'uuid': 'fake-vserver-uuid'}],
|
|
'num_records': 1,
|
|
}
|
|
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=[fake_resp, None]))
|
|
self.client.accept_vserver_peer(
|
|
fake.VSERVER_NAME, fake.VSERVER_PEER_NAME)
|
|
|
|
body = {'state': 'peered'}
|
|
|
|
uuid = "fake-vserver-uuid"
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(f'/svm/peers/{uuid}', 'patch', body=body,
|
|
enable_tunneling=False)])
|
|
|
|
def test_get_vserver_peers(self):
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=fake.FAKE_PEER_GET_RESPONSE))
|
|
|
|
result = self.client.get_vserver_peers(
|
|
vserver_name=fake.VSERVER_NAME,
|
|
peer_vserver_name=fake.VSERVER_NAME_2)
|
|
|
|
query = {
|
|
'name': fake.VSERVER_NAME_2,
|
|
'svm.name': fake.VSERVER_NAME
|
|
}
|
|
query['fields'] = 'uuid,svm.name,peer.svm.name,state,peer.cluster.name'
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/peers', 'get', query=query)])
|
|
|
|
expected = [{
|
|
'uuid': fake.FAKE_UUID,
|
|
'vserver': fake.VSERVER_NAME,
|
|
'peer-vserver': fake.VSERVER_NAME_2,
|
|
'peer-state': fake.VSERVER_PEER_STATE,
|
|
'peer-cluster': fake.CLUSTER_NAME
|
|
}]
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_vserver_peers_not_found(self):
|
|
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=fake.NO_RECORDS_RESPONSE_REST))
|
|
|
|
result = self.client.get_vserver_peers(
|
|
vserver_name=fake.VSERVER_NAME,
|
|
peer_vserver_name=fake.VSERVER_NAME_2)
|
|
|
|
self.assertEqual([], result)
|
|
self.assertTrue(self.client.send_request.called)
|
|
|
|
def test_delete_vserver_peer(self):
|
|
|
|
self.mock_object(self.client, 'get_vserver_peers',
|
|
mock.Mock(return_value=fake.FAKE_VSERVER_PEERS))
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.delete_vserver_peer(fake.VSERVER_NAME,
|
|
fake.VSERVER_PEER_NAME)
|
|
|
|
self.client.get_vserver_peers.assert_called_once_with(
|
|
fake.VSERVER_NAME, fake.VSERVER_PEER_NAME)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/svm/peers/fake_uuid', 'delete', enable_tunneling=False)
|
|
|
|
@ddt.data({'tcp-max-xfer-size': 10000}, {}, None)
|
|
def test_enable_nfs(self, nfs_config):
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client,
|
|
'_enable_nfs_protocols')
|
|
self.mock_object(self.client,
|
|
'_configure_nfs')
|
|
self.mock_object(self.client,
|
|
'_create_default_nfs_export_rules')
|
|
|
|
self.mock_object(self.client, '_enable_nfs_protocols')
|
|
|
|
self.client.enable_nfs(fake.NFS_VERSIONS, nfs_config)
|
|
body = {
|
|
'svm.uuid': fake.FAKE_UUID,
|
|
'enabled': 'true'
|
|
}
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/nfs/services/', 'post', body=body)
|
|
self.client._get_unique_svm_by_name.assert_called_once_with()
|
|
self.client._enable_nfs_protocols.assert_called_once_with(
|
|
fake.NFS_VERSIONS, fake.FAKE_UUID)
|
|
if nfs_config:
|
|
self.client._configure_nfs.assert_called_once_with(nfs_config,
|
|
fake.FAKE_UUID)
|
|
else:
|
|
self.client._configure_nfs.assert_not_called()
|
|
self.client._create_default_nfs_export_rules.assert_called_once_with()
|
|
|
|
@ddt.data((True, True, True), (True, False, False), (False, True, True))
|
|
@ddt.unpack
|
|
def test_enable_nfs_protocols(self, v3, v40, v41):
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
versions = []
|
|
if v3:
|
|
versions.append('nfs3')
|
|
if v40:
|
|
versions.append('nfs4.0')
|
|
if v41:
|
|
versions.append('nfs4.1')
|
|
|
|
self.client._enable_nfs_protocols(versions, fake.FAKE_UUID)
|
|
|
|
body = {
|
|
'protocol.v3_enabled': 'true' if v3 else 'false',
|
|
'protocol.v40_enabled': 'true' if v40 else 'false',
|
|
'protocol.v41_enabled': 'true' if v41 else 'false',
|
|
'showmount_enabled': 'true',
|
|
'windows.v3_ms_dos_client_enabled': 'true',
|
|
'protocol.v3_features.connection_drop': 'false',
|
|
'protocol.v3_features.ejukebox_enabled': 'false',
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/nfs/services/{fake.FAKE_UUID}',
|
|
'patch', body=body)
|
|
|
|
def test_configure_nfs(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
fake_nfs = {
|
|
'tcp-max-xfer-size': 10000,
|
|
}
|
|
self.client._configure_nfs(fake_nfs, fake.FAKE_UUID)
|
|
|
|
body = {
|
|
'transport.tcp_max_transfer_size': 10000
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/nfs/services/{fake.FAKE_UUID}',
|
|
'patch', body=body)
|
|
|
|
def test__create_default_nfs_export_rules(self):
|
|
|
|
class CopyingMock(mock.Mock):
|
|
def __call__(self, *args, **kwargs):
|
|
args = copy.deepcopy(args)
|
|
kwargs = copy.deepcopy(kwargs)
|
|
return super(CopyingMock, self).__call__(*args, **kwargs)
|
|
|
|
self.mock_object(self.client, 'send_request', CopyingMock())
|
|
|
|
fake_uuid = fake.FAKE_UUID
|
|
|
|
mock_id = self.mock_object(self.client, 'get_unique_export_policy_id',
|
|
mock.Mock(return_value=fake_uuid))
|
|
|
|
self.client._create_default_nfs_export_rules()
|
|
|
|
body = {
|
|
'clients': [{
|
|
'match': '0.0.0.0/0'
|
|
}],
|
|
'ro_rule': [
|
|
'any',
|
|
],
|
|
'rw_rule': [
|
|
'never'
|
|
],
|
|
}
|
|
body2 = body.copy()
|
|
body2['clients'] = [{
|
|
'match': '::/0'
|
|
}]
|
|
|
|
mock_id.assert_called_once_with('default')
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(f'/protocols/nfs/export-policies/{fake_uuid}/rules',
|
|
"post", body=body),
|
|
mock.call(f'/protocols/nfs/export-policies/{fake_uuid}/rules',
|
|
"post", body=body2)])
|
|
|
|
def test_get_node_data_ports(self):
|
|
self.mock_object(
|
|
self.client, 'send_request', mock.Mock(
|
|
side_effect=[fake.REST_ETHERNET_PORTS,
|
|
fake.REST_MGMT_INTERFACES]))
|
|
self.mock_object(
|
|
self.client, '_sort_data_ports_by_speed', mock.Mock(
|
|
return_value=fake.REST_SPEED_SORTED_PORTS))
|
|
|
|
test_result = self.client.get_node_data_ports(fake.NODE_NAME)
|
|
|
|
fake_query = {
|
|
'node.name': fake.NODE_NAME,
|
|
'state': 'up',
|
|
'type': 'physical',
|
|
'fields': 'node.name,speed,name'
|
|
}
|
|
|
|
query_interfaces = {
|
|
'service_policy.name': 'default-management',
|
|
'fields': 'location.port.name'
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ethernet/ports', 'get', query=fake_query),
|
|
mock.call('/network/ip/interfaces', 'get',
|
|
query=query_interfaces, enable_tunneling=False),
|
|
])
|
|
self.client._sort_data_ports_by_speed.assert_called_once_with(
|
|
fake.REST_SPEED_NOT_SORTED_PORTS)
|
|
self.assertEqual(fake.REST_SPEED_SORTED_PORTS, test_result)
|
|
|
|
def test_list_node_data_ports(self):
|
|
|
|
expected_resulted = ['e0d', 'e0c', 'e0b']
|
|
|
|
mock_ports = (
|
|
self.mock_object(self.client, 'get_node_data_ports', mock.Mock(
|
|
return_value=fake.REST_SPEED_SORTED_PORTS)))
|
|
|
|
test_result = self.client.list_node_data_ports(fake.NODE_NAME)
|
|
|
|
mock_ports.assert_called_once_with(fake.NODE_NAME)
|
|
self.assertEqual(test_result, expected_resulted)
|
|
|
|
def test_create_ipspace(self):
|
|
fake_body = {'name': fake.IPSPACE_NAME}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_ipspace(fake.IPSPACE_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ipspaces', 'post', body=fake_body)
|
|
|
|
def test_get_ipspace_name_for_vlan_port(self):
|
|
|
|
fake_query = {
|
|
'node.name': fake.NODE_NAME,
|
|
'name': fake.VLAN_PORT,
|
|
'fields': 'broadcast_domain.ipspace.name',
|
|
}
|
|
|
|
expected_result = "Default"
|
|
|
|
self.mock_object(
|
|
self.client, 'send_request', mock.Mock(
|
|
return_value=fake.REST_ETHERNET_PORTS))
|
|
|
|
test_result = self.client.get_ipspace_name_for_vlan_port(
|
|
fake.NODE_NAME, fake.PORT, fake.VLAN)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/ports/', 'get', query=fake_query)
|
|
|
|
self.assertEqual(test_result, expected_result)
|
|
|
|
def test__create_broadcast_domain(self):
|
|
|
|
fake_body = {
|
|
'ipspace.name': fake.IPSPACE_NAME,
|
|
'name': fake.BROADCAST_DOMAIN,
|
|
'mtu': fake.MTU,
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client._create_broadcast_domain(fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME,
|
|
fake.MTU)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/broadcast-domains', 'post', body=fake_body)
|
|
|
|
def test_ensure_broadcast_domain_for_port_domain_match(self):
|
|
|
|
port_info = {
|
|
'ipspace': fake.IPSPACE_NAME,
|
|
'broadcast-domain': fake.BROADCAST_DOMAIN,
|
|
}
|
|
self.mock_object(self.client,
|
|
'_get_broadcast_domain_for_port',
|
|
mock.Mock(return_value=port_info))
|
|
self.mock_object(self.client,
|
|
'_broadcast_domain_exists',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, '_create_broadcast_domain')
|
|
self.mock_object(self.client, '_modify_broadcast_domain')
|
|
self.mock_object(self.client, '_add_port_to_broadcast_domain')
|
|
|
|
self.client._ensure_broadcast_domain_for_port(
|
|
fake.NODE_NAME, fake.PORT, fake.MTU, ipspace=fake.IPSPACE_NAME)
|
|
|
|
self.client._get_broadcast_domain_for_port.assert_called_once_with(
|
|
fake.NODE_NAME, fake.PORT)
|
|
self.client._modify_broadcast_domain.assert_called_once_with(
|
|
fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME, fake.MTU)
|
|
self.assertFalse(self.client._broadcast_domain_exists.called)
|
|
self.assertFalse(self.client._create_broadcast_domain.called)
|
|
self.assertFalse(self.client._add_port_to_broadcast_domain.called)
|
|
|
|
@ddt.data(fake.IPSPACE_NAME, client_cmode.DEFAULT_IPSPACE)
|
|
def test_ensure_broadcast_domain_for_port_other_domain(self, ipspace):
|
|
|
|
port_info = {
|
|
'ipspace': ipspace,
|
|
'broadcast-domain': 'other_domain',
|
|
}
|
|
self.mock_object(self.client,
|
|
'_get_broadcast_domain_for_port',
|
|
mock.Mock(return_value=port_info))
|
|
self.mock_object(self.client,
|
|
'_broadcast_domain_exists',
|
|
mock.Mock(return_value=True))
|
|
self.mock_object(self.client, '_create_broadcast_domain')
|
|
self.mock_object(self.client, '_modify_broadcast_domain')
|
|
self.mock_object(self.client, '_add_port_to_broadcast_domain')
|
|
|
|
self.client._ensure_broadcast_domain_for_port(
|
|
fake.NODE_NAME, fake.PORT, ipspace=fake.IPSPACE_NAME, mtu=fake.MTU)
|
|
|
|
self.client._get_broadcast_domain_for_port.assert_called_once_with(
|
|
fake.NODE_NAME, fake.PORT)
|
|
self.client._broadcast_domain_exists.assert_called_once_with(
|
|
fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME)
|
|
self.assertFalse(self.client._create_broadcast_domain.called)
|
|
self.client._modify_broadcast_domain.assert_called_once_with(
|
|
fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME, fake.MTU)
|
|
self.client._add_port_to_broadcast_domain.assert_called_once_with(
|
|
fake.NODE_NAME, fake.PORT, fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
|
|
def test_ensure_broadcast_domain_for_port_no_domain(self):
|
|
|
|
port_info = {
|
|
'ipspace': fake.IPSPACE_NAME,
|
|
'broadcast-domain': None,
|
|
}
|
|
self.mock_object(self.client,
|
|
'_get_broadcast_domain_for_port',
|
|
mock.Mock(return_value=port_info))
|
|
self.mock_object(self.client,
|
|
'_broadcast_domain_exists',
|
|
mock.Mock(return_value=False))
|
|
self.mock_object(self.client, '_create_broadcast_domain')
|
|
self.mock_object(self.client, '_modify_broadcast_domain')
|
|
self.mock_object(self.client, '_add_port_to_broadcast_domain')
|
|
|
|
self.client._ensure_broadcast_domain_for_port(
|
|
fake.NODE_NAME, fake.PORT, ipspace=fake.IPSPACE_NAME, mtu=fake.MTU)
|
|
|
|
self.client._get_broadcast_domain_for_port.assert_called_once_with(
|
|
fake.NODE_NAME, fake.PORT)
|
|
self.client._broadcast_domain_exists.assert_called_once_with(
|
|
fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME)
|
|
self.client._create_broadcast_domain.assert_called_once_with(
|
|
fake.BROADCAST_DOMAIN, fake.IPSPACE_NAME, fake.MTU)
|
|
self.assertFalse(self.client._modify_broadcast_domain.called)
|
|
self.client._add_port_to_broadcast_domain.assert_called_once_with(
|
|
fake.NODE_NAME, fake.PORT, fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
|
|
def test__add_port_to_broadcast_domain(self):
|
|
query = {
|
|
'name': fake.PORT,
|
|
'node.name': fake.NODE_NAME,
|
|
}
|
|
body = {
|
|
'broadcast_domain.ipspace.name': fake.IPSPACE_NAME,
|
|
'broadcast_domain.name': fake.BROADCAST_DOMAIN,
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
self.client._add_port_to_broadcast_domain(fake.NODE_NAME,
|
|
fake.PORT,
|
|
fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/ports/', 'patch', query=query, body=body)
|
|
|
|
def test__add_port_to_broadcast_domain_exists(self):
|
|
query = {
|
|
'name': fake.PORT,
|
|
'node.name': fake.NODE_NAME,
|
|
}
|
|
body = {
|
|
'broadcast_domain.ipspace.name': fake.IPSPACE_NAME,
|
|
'broadcast_domain.name': fake.BROADCAST_DOMAIN,
|
|
}
|
|
self.mock_object(
|
|
self.client, 'send_request', self._mock_api_error(
|
|
code=netapp_api.EREST_FAIL_ADD_PORT_BROADCAST))
|
|
|
|
self.client._add_port_to_broadcast_domain(fake.NODE_NAME,
|
|
fake.PORT,
|
|
fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/ports/', 'patch', query=query, body=body)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test__add_port_to_broadcast_domain_exception(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
self._mock_api_error())
|
|
self.assertRaises(
|
|
exception.NetAppException,
|
|
self.client._add_port_to_broadcast_domain,
|
|
fake.NODE_NAME, fake.PORT, fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
|
|
def test_rename_vserver(self):
|
|
svm_uuid = fake.SVM_ITEM_SIMPLE_RESPONSE_REST["uuid"]
|
|
body = {
|
|
'name': fake.VSERVER_NAME_2
|
|
}
|
|
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=svm_uuid))
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.rename_vserver(fake.VSERVER_NAME, fake.VSERVER_NAME_2)
|
|
|
|
self.client._get_unique_svm_by_name.assert_called_once_with(
|
|
fake.VSERVER_NAME)
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/svm/svms/{svm_uuid}', 'patch', body=body)
|
|
|
|
def test_create_network_interface(self):
|
|
api_response = copy.deepcopy(fake.SERVICE_POLICIES_REST)
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=[api_response, None, None]))
|
|
|
|
self.client.create_network_interface(fake.IP_ADDRESS,
|
|
fake.NETMASK,
|
|
fake.NODE_NAME,
|
|
fake.VLAN_PORT,
|
|
fake.VSERVER_NAME,
|
|
fake.LIF_NAME)
|
|
query = {
|
|
'name': 'default-data-files',
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'fields': 'uuid,name,services,svm.name'
|
|
}
|
|
|
|
policy = copy.deepcopy(fake.SERVICE_POLICIES_REST['records'][0])
|
|
uuid = policy['uuid']
|
|
|
|
policy['services'].append('data_nfs')
|
|
policy['services'].append('data_cifs')
|
|
body1 = {'services': policy['services']}
|
|
|
|
body2 = {
|
|
'ip.address': fake.IP_ADDRESS,
|
|
'ip.netmask': fake.NETMASK,
|
|
'enabled': 'true',
|
|
'service_policy.name': 'default-data-files',
|
|
'location.home_node.name': fake.NODE_NAME,
|
|
'location.home_port.name': fake.VLAN_PORT,
|
|
'name': fake.LIF_NAME,
|
|
'svm.name': fake.VSERVER_NAME,
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ip/service-policies/', 'get', query=query),
|
|
mock.call(f'/network/ip/service-policies/{uuid}',
|
|
'patch', body=body1),
|
|
mock.call('/network/ip/interfaces', 'post', body=body2)
|
|
])
|
|
|
|
def test_create_vserver(self):
|
|
mock = self.mock_object(self.client, '_create_vserver')
|
|
self.mock_object(self.client, '_modify_security_cert',
|
|
mock.Mock(return_value=[]))
|
|
self.client.create_vserver(fake.VSERVER_NAME, None, None,
|
|
[fake.SHARE_AGGREGATE_NAME],
|
|
fake.IPSPACE_NAME,
|
|
fake.SECURITY_CERT_DEFAULT_EXPIRE_DAYS)
|
|
mock.assert_called_once_with(fake.VSERVER_NAME,
|
|
[fake.SHARE_AGGREGATE_NAME],
|
|
fake.IPSPACE_NAME,
|
|
name_server_switch=['files'])
|
|
self.client._modify_security_cert.assert_called_once_with(
|
|
fake.VSERVER_NAME,
|
|
fake.SECURITY_CERT_DEFAULT_EXPIRE_DAYS)
|
|
|
|
def test__modify_security_cert(self):
|
|
api_response = copy.deepcopy(fake.SECURITY_CERT_GET_RESPONSE_REST)
|
|
api_response2 = copy.deepcopy(fake.SECURITY_CERT_POST_RESPONSE_REST)
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=[api_response, api_response2, None, None]))
|
|
|
|
query = {
|
|
'common-name': fake.VSERVER_NAME,
|
|
'ca': fake.VSERVER_NAME,
|
|
'type': 'server',
|
|
'svm.name': fake.VSERVER_NAME,
|
|
}
|
|
old_cert_info = copy.deepcopy(
|
|
fake.SECURITY_CERT_GET_RESPONSE_REST['records'][0])
|
|
old_cert_uuid = old_cert_info['uuid']
|
|
|
|
body1 = {
|
|
'common-name': fake.VSERVER_NAME,
|
|
'type': 'server',
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'expiry_time': 'P' + str(
|
|
fake.SECURITY_CERT_LARGE_EXPIRE_DAYS) + 'DT',
|
|
}
|
|
query1 = {
|
|
'return_records': 'true'
|
|
}
|
|
new_cert_info = copy.deepcopy(
|
|
fake.SECURITY_CERT_POST_RESPONSE_REST['records'][0])
|
|
new_cert_uuid = new_cert_info['uuid']
|
|
new_svm_uuid = new_cert_info['svm']['uuid']
|
|
body2 = {
|
|
'certificate': {
|
|
'uuid': new_cert_uuid,
|
|
},
|
|
'client_enabled': 'false',
|
|
}
|
|
|
|
self.client._modify_security_cert(
|
|
fake.VSERVER_NAME,
|
|
fake.SECURITY_CERT_LARGE_EXPIRE_DAYS)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/security/certificates', 'get', query=query),
|
|
mock.call('/security/certificates', 'post', body=body1,
|
|
query=query1),
|
|
mock.call(f'/svm/svms/{new_svm_uuid}', 'patch', body=body2),
|
|
mock.call(f'/security/certificates/{old_cert_uuid}', 'delete'),
|
|
])
|
|
|
|
def test__broadcast_domain_exists(self):
|
|
response = fake.FAKE_GET_BROADCAST_DOMAIN
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
query = {
|
|
'ipspace.name': fake.IPSPACE_NAME,
|
|
'name': fake.BROADCAST_DOMAIN,
|
|
}
|
|
result = self.client._broadcast_domain_exists(fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/broadcast-domains',
|
|
'get', query=query)
|
|
self.assertTrue(result)
|
|
|
|
def test___delete_port_by_ipspace_and_broadcast_domain(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
query = {
|
|
'broadcast_domain.ipspace.name': fake.IPSPACE_NAME,
|
|
'broadcast_domain.name': fake.BROADCAST_DOMAIN,
|
|
'name': fake.PORT
|
|
}
|
|
self.client._delete_port_by_ipspace_and_broadcast_domain(
|
|
fake.PORT,
|
|
fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/ports/', 'delete',
|
|
query=query)
|
|
|
|
def test_get_broadcast_domain_for_port(self):
|
|
|
|
self.mock_object(self.client, 'send_request', mock.Mock(
|
|
return_value=fake.REST_ETHERNET_PORTS))
|
|
|
|
query = {
|
|
'node.name': fake.NODE_NAME,
|
|
'name': fake.PORT,
|
|
'fields': 'broadcast_domain.name,broadcast_domain.ipspace.name'
|
|
}
|
|
|
|
result = self.client._get_broadcast_domain_for_port(fake.NODE_NAME,
|
|
fake.PORT)
|
|
|
|
expected = {
|
|
'broadcast-domain': "fake_domain_1",
|
|
'ipspace': "Default",
|
|
}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ethernet/ports', 'get', query=query)])
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_modify_broadcast_domain(self):
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
result = self.client._modify_broadcast_domain(fake.BROADCAST_DOMAIN,
|
|
fake.IPSPACE_NAME,
|
|
fake.MTU)
|
|
|
|
query = {
|
|
'name': fake.BROADCAST_DOMAIN
|
|
}
|
|
|
|
body = {
|
|
'ipspace.name': fake.IPSPACE_NAME,
|
|
'mtu': fake.MTU,
|
|
}
|
|
self.assertIsNone(result)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/broadcast-domains', 'patch', body=body,
|
|
query=query)
|
|
|
|
@ddt.data(fake.NO_RECORDS_RESPONSE,
|
|
fake.SVMS_LIST_SIMPLE_RESPONSE_REST)
|
|
def test_get_vserver_info(self, api_response):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_vserver_info(fake.VSERVER_NAME)
|
|
|
|
query = {
|
|
'name': fake.VSERVER_NAME,
|
|
'fields': 'state,subtype'
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/svm/svms', 'get', query=query)
|
|
if api_response == fake.NO_RECORDS_RESPONSE:
|
|
self.assertIsNone(result)
|
|
else:
|
|
self.assertDictEqual(fake.VSERVER_INFO, result)
|
|
|
|
def test_get_nfs_config(self):
|
|
api_response = fake.NFS_CONFIG_RESULT_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_nfs_config(['tcp-max-xfer-size',
|
|
'udp-max-xfer-size'],
|
|
fake.VSERVER_NAME)
|
|
expected = {
|
|
'tcp-max-xfer-size': '65536',
|
|
'udp-max-xfer-size': '32768',
|
|
}
|
|
self.assertEqual(expected, result)
|
|
|
|
query = {'fields': 'transport.*', 'svm.name': 'fake_vserver'}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/protocols/nfs/services/', 'get', query=query)
|
|
|
|
def test_get_vserver_ipspace(self):
|
|
|
|
self.client.features.add_feature('IPSPACES')
|
|
api_response = fake.REST_VSERVER_GET_IPSPACE_NAME_RESPONSE
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.get_vserver_ipspace(fake.VSERVER_NAME)
|
|
|
|
query = {
|
|
'name': fake.VSERVER_NAME,
|
|
'fields': 'ipspace.name'
|
|
}
|
|
expected = fake.IPSPACE_NAME
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get', query=query)])
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_vserver_ipspace_not_found(self):
|
|
api_response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result = self.client.get_vserver_ipspace(fake.VSERVER_NAME)
|
|
self.assertIsNone(result)
|
|
|
|
def test_get_vserver_ipspace_exception(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
self._mock_api_error())
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_vserver_ipspace,
|
|
fake.VSERVER_NAME)
|
|
|
|
def test_get_snapmirror_policies(self):
|
|
api_response = fake.GET_SNAPMIRROR_POLICIES_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result_elem = [fake.SNAPMIRROR_POLICY_NAME]
|
|
|
|
result = self.client.get_snapmirror_policies(
|
|
fake.VSERVER_NAME)
|
|
|
|
query = {
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'fields': 'name'
|
|
}
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/snapmirror/policies', 'get', query=query)
|
|
self.assertEqual(result_elem, result)
|
|
|
|
def test_delete_snapmirror_policy(self):
|
|
api_response = fake.GET_SNAPMIRROR_POLICIES_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
self.client.delete_snapmirror_policy('fake_policy')
|
|
|
|
query = {}
|
|
query['name'] = 'fake_policy'
|
|
query['fields'] = 'uuid,name'
|
|
uuid = fake.FAKE_UUID
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/snapmirror/policies', 'get', query=query),
|
|
mock.call(f'/snapmirror/policies/{uuid}', 'delete')
|
|
])
|
|
|
|
def test_delete_snapmirror_policy_exception(self):
|
|
api_response = fake.GET_SNAPMIRROR_POLICIES_REST
|
|
api_error = netapp_api.api.NaApiError()
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=[api_response, api_error]))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.delete_snapmirror_policy,
|
|
'fake_policy')
|
|
|
|
def test_delete_snapmirror_policy_no_records(self):
|
|
api_response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
self.client.delete_snapmirror_policy('fake_policy')
|
|
|
|
query = {}
|
|
query['name'] = 'fake_policy'
|
|
query['fields'] = 'uuid,name'
|
|
self.client.send_request.assert_called_once_with(
|
|
'/snapmirror/policies', 'get', query=query)
|
|
|
|
def test_delete_vserver_one_volume(self):
|
|
self.mock_object(self.client, 'get_vserver_info',
|
|
mock.Mock(return_value=fake.VSERVER_INFO))
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client, 'get_vserver_root_volume_name',
|
|
mock.Mock(return_value=fake.ROOT_VOLUME_NAME))
|
|
self.mock_object(self.client, 'get_vserver_volume_count',
|
|
mock.Mock(return_value=1))
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, 'offline_volume')
|
|
self.mock_object(self.client, 'delete_volume')
|
|
self.mock_object(self.client, '_terminate_vserver_services')
|
|
|
|
self.client.delete_vserver(fake.VSERVER_NAME, self.client,
|
|
fake.CIFS_SECURITY_SERVICE)
|
|
|
|
self.client.offline_volume.assert_called_with(fake.ROOT_VOLUME_NAME)
|
|
self.client.delete_volume.assert_called_with(fake.ROOT_VOLUME_NAME)
|
|
self.client._terminate_vserver_services(
|
|
fake.VSERVER_NAME, self.client, fake.CIFS_SECURITY_SERVICE)
|
|
|
|
svm_uuid = fake.FAKE_UUID
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(f'/svm/svms/{svm_uuid}', 'delete')])
|
|
|
|
def test_delete_vserver_one_volume_already_offline(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_vserver_info',
|
|
mock.Mock(return_value=fake.VSERVER_INFO))
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client,
|
|
'get_vserver_root_volume_name',
|
|
mock.Mock(return_value=fake.ROOT_VOLUME_NAME))
|
|
self.mock_object(self.client,
|
|
'get_vserver_volume_count',
|
|
mock.Mock(return_value=1))
|
|
self.mock_object(self.client,
|
|
'offline_volume',
|
|
self._mock_api_error(
|
|
code=netapp_api.EREST_ENTRY_NOT_FOUND))
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, 'delete_volume')
|
|
|
|
self.client.delete_vserver(fake.VSERVER_NAME,
|
|
self.client)
|
|
|
|
self.client.offline_volume.assert_called_with(
|
|
fake.ROOT_VOLUME_NAME)
|
|
self.client.delete_volume.assert_called_with(
|
|
fake.ROOT_VOLUME_NAME)
|
|
|
|
svm_uuid = fake.FAKE_UUID
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(f'/svm/svms/{svm_uuid}', 'delete')])
|
|
self.assertEqual(1, client_cmode_rest.LOG.error.call_count)
|
|
|
|
def test_delete_vserver_one_volume_api_error(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_vserver_info',
|
|
mock.Mock(return_value=fake.VSERVER_INFO))
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client,
|
|
'get_vserver_root_volume_name',
|
|
mock.Mock(return_value=fake.ROOT_VOLUME_NAME))
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client,
|
|
'get_vserver_volume_count',
|
|
mock.Mock(return_value=1))
|
|
self.mock_object(self.client,
|
|
'offline_volume',
|
|
self._mock_api_error())
|
|
self.mock_object(self.client, 'delete_volume')
|
|
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.delete_vserver,
|
|
fake.VSERVER_NAME,
|
|
self.client)
|
|
|
|
def test_delete_vserver_multiple_volumes(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_vserver_info',
|
|
mock.Mock(return_value=fake.VSERVER_INFO))
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client,
|
|
'get_vserver_root_volume_name',
|
|
mock.Mock(return_value=fake.ROOT_VOLUME_NAME))
|
|
self.mock_object(self.client,
|
|
'get_vserver_volume_count',
|
|
mock.Mock(return_value=2))
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.delete_vserver,
|
|
fake.VSERVER_NAME,
|
|
self.client)
|
|
|
|
def test_delete_vserver_not_found(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_vserver_info',
|
|
mock.Mock(return_value=None))
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
|
|
self.client.delete_vserver(fake.VSERVER_NAME,
|
|
self.client)
|
|
|
|
self.assertEqual(1, client_cmode_rest.LOG.error.call_count)
|
|
|
|
def test_get_vserver_volume_count(self):
|
|
fake_response = fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE
|
|
mock_request = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake_response))
|
|
response = self.client.get_vserver_volume_count()
|
|
|
|
self.assertEqual(response, 10)
|
|
query = {'return_records': 'false'}
|
|
mock_request.assert_called_once_with(
|
|
'/storage/volumes', 'get', query=query)
|
|
|
|
def test__terminate_vserver_services(self):
|
|
|
|
fake_uuid = fake.FAKE_UUID
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, 'disable_kerberos')
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_uuid))
|
|
|
|
security_services = [
|
|
copy.deepcopy(fake.CIFS_SECURITY_SERVICE),
|
|
copy.deepcopy(fake.KERBEROS_SECURITY_SERVICE)
|
|
]
|
|
self.client._terminate_vserver_services(
|
|
fake.VSERVER_NAME, self.client, security_services)
|
|
|
|
cifs_server_delete_body = {
|
|
'ad_domain.password': security_services[0]['password'],
|
|
'ad_domain.user': security_services[0]['user'],
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/cifs/services/{fake_uuid}', 'delete',
|
|
body=cifs_server_delete_body)
|
|
self.client.disable_kerberos.assert_called_once_with(
|
|
security_services[1])
|
|
|
|
def test_terminate_vserver_services_cifs_not_found(self):
|
|
|
|
fake_uuid = fake.FAKE_UUID
|
|
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
self._mock_api_error(code=netapp_api.EREST_ENTRY_NOT_FOUND))
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_uuid))
|
|
|
|
security_service = copy.deepcopy(fake.CIFS_SECURITY_SERVICE)
|
|
self.client._terminate_vserver_services(fake.VSERVER_NAME,
|
|
self.client,
|
|
[security_service])
|
|
|
|
cifs_server_delete_body = {
|
|
'ad_domain.password': security_service['password'],
|
|
'ad_domain.user': security_service['user'],
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/protocols/cifs/services/{fake_uuid}', 'delete',
|
|
body=cifs_server_delete_body)
|
|
self.assertEqual(1, client_cmode_rest.LOG.error.call_count)
|
|
|
|
def test_terminate_vserver_services_api_error(self):
|
|
|
|
fake_uuid = fake.FAKE_UUID
|
|
side_effects = [netapp_api.api.NaApiError(code='fake'), None]
|
|
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(side_effect=side_effects))
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake_uuid))
|
|
|
|
security_service = copy.deepcopy(fake.CIFS_SECURITY_SERVICE)
|
|
self.client._terminate_vserver_services(fake.VSERVER_NAME,
|
|
self.client,
|
|
[security_service])
|
|
|
|
cifs_server_delete_body = {
|
|
'ad_domain.password': security_service['password'],
|
|
'ad_domain.user': security_service['user'],
|
|
}
|
|
cifs_server_delete_force_body = {
|
|
'ad_domain.password': security_service['password'],
|
|
'ad_domain.user': security_service['user'],
|
|
'force': True
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(f'/protocols/cifs/services/{fake_uuid}', 'delete',
|
|
body=cifs_server_delete_body),
|
|
mock.call(f'/protocols/cifs/services/{fake_uuid}', 'delete',
|
|
body=cifs_server_delete_force_body)])
|
|
self.assertEqual(0, client_cmode_rest.LOG.error.call_count)
|
|
|
|
def test_disable_kerberos(self):
|
|
fake_api_response = fake.NFS_LIFS_REST
|
|
api_error = self._mock_api_error(
|
|
code=netapp_api.EREST_KERBEROS_IS_ENABLED_DISABLED)
|
|
self.mock_object(self.client, 'get_network_interfaces',
|
|
mock.Mock(return_value=fake_api_response))
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=[None, api_error, None]))
|
|
|
|
self.client.disable_kerberos(fake.KERBEROS_SECURITY_SERVICE)
|
|
|
|
kerberos_config_modify_body = {
|
|
'password': fake.KERBEROS_SECURITY_SERVICE['password'],
|
|
'user': fake.KERBEROS_SECURITY_SERVICE['user'],
|
|
'interface.name': fake.LIF_NAME,
|
|
'enabled': False,
|
|
}
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/protocols/nfs/kerberos/interfaces/fake_uuid_1',
|
|
'patch', body=kerberos_config_modify_body),
|
|
mock.call('/protocols/nfs/kerberos/interfaces/fake_uuid_2',
|
|
'patch', body=kerberos_config_modify_body),
|
|
mock.call('/protocols/nfs/kerberos/interfaces/fake_uuid_3',
|
|
'patch', body=kerberos_config_modify_body)
|
|
])
|
|
self.client.get_network_interfaces.assert_called_once()
|
|
|
|
def test_get_vserver_root_volume_name(self):
|
|
response = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=response))
|
|
self.client.get_vserver_root_volume_name(fake.VSERVER_NAME)
|
|
self.client._get_volume_by_args.assert_called_once_with(
|
|
vserver=fake.VSERVER_NAME, is_root=True)
|
|
|
|
def test_ipspace_has_data_vservers(self):
|
|
api_response = fake.REST_VSERVER_GET_IPSPACE_NAME_RESPONSE
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.ipspace_has_data_vservers(fake.IPSPACE_NAME)
|
|
|
|
query = {'ipspace.name': fake.IPSPACE_NAME}
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/svm/svms', 'get', query=query)])
|
|
self.assertTrue(result)
|
|
|
|
def test_ipspace_has_data_vservers_not_supported(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value='fake_response'))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=False))
|
|
|
|
result = self.client.ipspace_has_data_vservers(fake.IPSPACE_NAME)
|
|
|
|
self.assertFalse(result)
|
|
query = {'ipspace.name': fake.IPSPACE_NAME}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/svm/svms', 'get', query=query)
|
|
self.client._has_records.assert_called_once_with('fake_response')
|
|
|
|
def test_ipspace_has_data_vservers_not_found(self):
|
|
api_response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
|
|
result = self.client.ipspace_has_data_vservers(fake.IPSPACE_NAME)
|
|
|
|
self.assertFalse(result)
|
|
|
|
def test_delete_vlan(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
query = {
|
|
'vlan.base_port.name': fake.PORT,
|
|
'node.name': fake.NODE_NAME,
|
|
'vlan.tag': fake.VLAN
|
|
}
|
|
|
|
self.client.delete_vlan(fake.NODE_NAME, fake.PORT, fake.VLAN)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ethernet/ports/', 'delete', query=query)])
|
|
|
|
def test_delete_vlan_not_found(self):
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
self._mock_api_error(code=netapp_api.EREST_ENTRY_NOT_FOUND))
|
|
|
|
query = {
|
|
'vlan.base_port.name': fake.PORT,
|
|
'node.name': fake.NODE_NAME,
|
|
'vlan.tag': fake.VLAN
|
|
}
|
|
|
|
self.client.delete_vlan(fake.NODE_NAME, fake.PORT, fake.VLAN)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ethernet/ports/', 'delete', query=query)])
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test_delete_vlan_still_used(self):
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
self._mock_api_error(code=netapp_api.EREST_PORT_IN_USE))
|
|
|
|
query = {
|
|
'vlan.base_port.name': fake.PORT,
|
|
'node.name': fake.NODE_NAME,
|
|
'vlan.tag': fake.VLAN
|
|
}
|
|
|
|
self.client.delete_vlan(fake.NODE_NAME, fake.PORT, fake.VLAN)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ethernet/ports/', 'delete', query=query)])
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test_delete_vlan_api_error(self):
|
|
self.mock_object(self.client, 'send_request', self._mock_api_error())
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.delete_vlan,
|
|
fake.NODE_NAME,
|
|
fake.PORT,
|
|
fake.VLAN)
|
|
|
|
@ddt.data(None, fake.IPSPACE_NAME)
|
|
def test_svm_migration_start(self, dest_ipspace):
|
|
check_only = True
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value='fake_migration'))
|
|
|
|
res = self.client.svm_migration_start(
|
|
fake.CLUSTER_NAME, fake.VSERVER_NAME, fake.SHARE_AGGREGATE_NAMES,
|
|
dest_ipspace=dest_ipspace, check_only=check_only)
|
|
|
|
self.assertEqual('fake_migration', res)
|
|
expected_body = {
|
|
"auto_cutover": False,
|
|
"auto_source_cleanup": True,
|
|
"check_only": True,
|
|
"source": {
|
|
"cluster": {"name": fake.CLUSTER_NAME},
|
|
"svm": {"name": fake.VSERVER_NAME},
|
|
},
|
|
"destination": {
|
|
"volume_placement": {
|
|
"aggregates": fake.SHARE_AGGREGATE_NAMES,
|
|
},
|
|
},
|
|
}
|
|
if dest_ipspace is not None:
|
|
ipspace_data = {
|
|
"ipspace": {
|
|
"name": dest_ipspace,
|
|
}
|
|
}
|
|
expected_body["destination"].update(ipspace_data)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/svm/migrations', 'post', body=expected_body,
|
|
wait_on_accepted=False)
|
|
|
|
def test_get_migration_check_job_state(self):
|
|
self.mock_object(self.client, 'get_job',
|
|
mock.Mock(return_value='fake_job'))
|
|
|
|
res = self.client.get_migration_check_job_state(fake.JOB_ID)
|
|
|
|
self.assertEqual('fake_job', res)
|
|
self.client.get_job.assert_called_once_with(fake.JOB_ID)
|
|
|
|
@ddt.data(netapp_api.api.ENFS_V4_0_ENABLED_MIGRATION_FAILURE,
|
|
netapp_api.api.EVSERVER_MIGRATION_TO_NON_AFF_CLUSTER, 'none')
|
|
def test_get_migration_check_job_state_raise_error(self, error_code):
|
|
e = netapp_api.api.NaApiError(code=error_code)
|
|
self.mock_object(self.client, 'get_job', mock.Mock(side_effect=e))
|
|
|
|
self.assertRaises(
|
|
exception.NetAppException,
|
|
self.client.get_migration_check_job_state,
|
|
fake.JOB_ID)
|
|
|
|
def test_svm_migrate_complete(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value='fake_migration'))
|
|
|
|
res = self.client.svm_migrate_complete(fake.FAKE_MIGRATION_POST_ID)
|
|
|
|
self.assertEqual('fake_migration', res)
|
|
expected_body = {
|
|
"action": "cutover"
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/svm/migrations/{fake.FAKE_MIGRATION_POST_ID}', 'patch',
|
|
body=expected_body, wait_on_accepted=False)
|
|
|
|
def test_svm_migrate_cancel(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value='fake_migration'))
|
|
|
|
res = self.client.svm_migrate_cancel(fake.FAKE_MIGRATION_POST_ID)
|
|
|
|
self.assertEqual('fake_migration', res)
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/svm/migrations/{fake.FAKE_MIGRATION_POST_ID}', 'delete',
|
|
wait_on_accepted=False)
|
|
|
|
def test_svm_migration_get(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value='fake_migration'))
|
|
|
|
res = self.client.svm_migration_get(fake.FAKE_MIGRATION_POST_ID)
|
|
|
|
self.assertEqual('fake_migration', res)
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/svm/migrations/{fake.FAKE_MIGRATION_POST_ID}', 'get')
|
|
|
|
def test_svm_migrate_pause(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value='fake_migration'))
|
|
|
|
res = self.client.svm_migrate_pause(fake.FAKE_MIGRATION_POST_ID)
|
|
|
|
self.assertEqual('fake_migration', res)
|
|
expected_body = {
|
|
"action": "pause"
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
f'/svm/migrations/{fake.FAKE_MIGRATION_POST_ID}', 'patch',
|
|
body=expected_body, wait_on_accepted=False)
|
|
|
|
def test_delete_network_interface(self):
|
|
self.mock_object(self.client, 'disable_network_interface')
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.delete_network_interface(fake.VSERVER_NAME, fake.LIF_NAME)
|
|
|
|
self.client.disable_network_interface.assert_called_once_with(
|
|
fake.VSERVER_NAME, fake.LIF_NAME)
|
|
expected_query = {
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'name': fake.LIF_NAME
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/interfaces', 'delete', query=expected_query)
|
|
|
|
def test_disable_network_interface(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.disable_network_interface(fake.VSERVER_NAME, fake.LIF_NAME)
|
|
|
|
expected_body = {
|
|
'enabled': 'false'
|
|
}
|
|
expected_query = {
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'name': fake.LIF_NAME
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/interfaces', 'patch', body=expected_body,
|
|
query=expected_query)
|
|
|
|
def test__delete_port_and_broadcast_domain(self):
|
|
|
|
domain = copy.deepcopy(fake.BROADCAST_DOMAIN)
|
|
ipspace = copy.deepcopy(fake.GET_IPSPACES_RESPONSE)
|
|
|
|
query = {'name': domain, 'ipspace.name': ipspace['ipspace']}
|
|
|
|
response_broadcast = copy.deepcopy(
|
|
fake.BROADCAST_DOMAIN_LIST_SIMPLE_RESPONSE_REST)
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(side_effect=[response_broadcast, None]))
|
|
|
|
self.mock_object(self.client,
|
|
'_delete_port_by_ipspace_and_broadcast_domain')
|
|
|
|
self.client._delete_port_and_broadcast_domain(domain, ipspace)
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call('/network/ethernet/broadcast-domains',
|
|
'delete', query=query)])
|
|
|
|
def test_delete_ipspace(self):
|
|
ipspace = copy.deepcopy(fake.IPSPACES[0])
|
|
mock_del_brcst = self.mock_object(
|
|
self.client, '_delete_port_and_broadcast_domains_for_ipspace')
|
|
|
|
mock_send_request = self.mock_object(
|
|
self.client, 'send_request')
|
|
|
|
query = {'name': fake.IPSPACE_NAME}
|
|
|
|
self.client.delete_ipspace(ipspace['ipspace'])
|
|
|
|
mock_del_brcst.assert_called_once_with(fake.IPSPACE_NAME)
|
|
|
|
mock_send_request.assert_called_once_with(
|
|
'/network/ipspaces', 'delete', query=query)
|
|
|
|
def test_get_ipspaces(self):
|
|
expected = copy.deepcopy(fake.GET_IPSPACES_RESPONSE)
|
|
sr_responses = [fake.IPSPACE_INFO,
|
|
fake.REST_SINGLE_PORT,
|
|
fake.SVMS_LIST_SIMPLE_RESPONSE_REST,
|
|
fake.FAKE_GET_BROADCAST_DOMAIN]
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=sr_responses))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
result = self.client.get_ipspaces(fake.IPSPACE_NAME)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_ipspaces_no_records(self):
|
|
api_response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result = self.client.get_ipspaces(fake.IPSPACE_NAME)
|
|
self.assertEqual([], result)
|
|
|
|
def test_delete_port_and_broadcast_domains_for_ipspace_not_found(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_ipspaces',
|
|
mock.Mock(return_value=[]))
|
|
self.mock_object(self.client, '_delete_port_and_broadcast_domain')
|
|
|
|
self.client._delete_port_and_broadcast_domains_for_ipspace(
|
|
fake.IPSPACE_NAME)
|
|
|
|
self.client.get_ipspaces.assert_called_once_with(
|
|
fake.IPSPACE_NAME)
|
|
self.assertFalse(self.client._delete_port_and_broadcast_domain.called)
|
|
|
|
def test_delete_port_and_broadcast_domains_for_ipspace(self):
|
|
|
|
self.mock_object(self.client,
|
|
'get_ipspaces',
|
|
mock.Mock(return_value=fake.IPSPACES[0]))
|
|
self.mock_object(self.client, '_delete_port_and_broadcast_domain')
|
|
|
|
self.client._delete_port_and_broadcast_domains_for_ipspace(
|
|
fake.IPSPACE_NAME)
|
|
|
|
self.client.get_ipspaces.assert_called_once_with(
|
|
fake.IPSPACE_NAME)
|
|
self.client._delete_port_and_broadcast_domain.assert_called_once_with(
|
|
fake.IPSPACES[0]['broadcast-domains'][0], fake.IPSPACES[0])
|
|
|
|
@ddt.data(('10.10.10.0/24', '10.10.10.1', False),
|
|
('fc00::/7', 'fe80::1', False),
|
|
('0.0.0.0/0', '10.10.10.1', True),
|
|
('::/0', 'fe80::1', True))
|
|
@ddt.unpack
|
|
def test_create_route(self, subnet, gateway, omit_destination):
|
|
|
|
address = None
|
|
netmask = None
|
|
destination = None if omit_destination else subnet
|
|
if not destination:
|
|
if ':' in gateway:
|
|
destination = '::/0'
|
|
else:
|
|
destination = '0.0.0.0/0'
|
|
|
|
if '/' in destination:
|
|
address, netmask = destination.split('/')
|
|
else:
|
|
address = destination
|
|
|
|
body = {
|
|
'destination.address': address,
|
|
'gateway': gateway,
|
|
}
|
|
|
|
if netmask:
|
|
body['destination.netmask'] = netmask
|
|
|
|
self.mock_object(self.client, 'send_request')
|
|
|
|
self.client.create_route(gateway, destination=destination)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/routes', 'post', body=body)
|
|
|
|
def test_create_route_duplicate(self):
|
|
self.mock_object(client_cmode_rest.LOG, 'debug')
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
self._mock_api_error(code=netapp_api.EREST_DUPLICATE_ROUTE))
|
|
|
|
self.client.create_route(fake.GATEWAY, destination=fake.SUBNET)
|
|
|
|
body = {
|
|
'destination.address': fake.SUBNET[:-3],
|
|
'gateway': fake.GATEWAY,
|
|
'destination.netmask': fake.SUBNET[-2:],
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/routes', 'post', body=body)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test_create_route_api_error(self):
|
|
self.mock_object(client_cmode_rest.LOG, 'debug')
|
|
self.mock_object(self.client, 'send_request', self._mock_api_error())
|
|
|
|
body = {
|
|
'destination.address': fake.SUBNET[:-3],
|
|
'gateway': fake.GATEWAY,
|
|
'destination.netmask': fake.SUBNET[-2:],
|
|
}
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.create_route,
|
|
fake.GATEWAY, destination=fake.SUBNET)
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/routes', 'post', body=body)
|
|
|
|
def test_create_route_without_gateway(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
self.client.create_route(None, destination=fake.SUBNET)
|
|
self.assertFalse(self.client.send_request.called)
|
|
|
|
def test_network_interface_exists(self):
|
|
api_response = fake.GENERIC_NETWORK_INTERFACES_GET_REPONSE
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=api_response))
|
|
result = self.client.network_interface_exists(
|
|
fake.VSERVER_NAME, fake.NODE_NAME, fake.PORT, fake.IP_ADDRESS,
|
|
fake.NETMASK, fake.VLAN)
|
|
query = {
|
|
'ip.address': fake.IP_ADDRESS,
|
|
'location.home_node.name': fake.NODE_NAME,
|
|
'location.home_port.name': f'{fake.PORT}-{fake.VLAN}',
|
|
'ip.netmask': fake.NETMASK,
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'fields': 'name',
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ip/interfaces', 'get', query=query)
|
|
self.assertTrue(result)
|
|
|
|
def test_modify_active_directory_security_service(self):
|
|
svm_uuid = fake.FAKE_UUID
|
|
user_records = fake.FAKE_CIFS_LOCAL_USER.get('records')[0]
|
|
sid = user_records.get('sid')
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=svm_uuid))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=[user_records,
|
|
None, None]))
|
|
self.mock_object(self.client, 'remove_preferred_dcs')
|
|
self.mock_object(self.client, 'set_preferred_dc')
|
|
new_security_service = {
|
|
'user': 'new_user',
|
|
'password': 'new_password',
|
|
'server': 'fake_server'
|
|
}
|
|
|
|
current_security_service = {
|
|
'server': 'fake_current_server'
|
|
}
|
|
keys = {'user', 'password', 'server'}
|
|
|
|
self.client.modify_active_directory_security_service(
|
|
fake.VSERVER_NAME, keys, new_security_service,
|
|
current_security_service)
|
|
|
|
self.client.send_request.assert_has_calls([
|
|
mock.call(f'/protocols/cifs/local-users/{svm_uuid}', 'get'),
|
|
mock.call(f'/protocols/cifs/local-users/{svm_uuid}/{sid}', 'patch',
|
|
query={'password': new_security_service['password']}),
|
|
mock.call(f'/protocols/cifs/local-users/{svm_uuid}/{sid}', 'patch',
|
|
query={'name': new_security_service['user']})
|
|
])
|
|
|
|
def test__create_vserver(self):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
body = {
|
|
'name': fake.VSERVER_NAME,
|
|
'nsswitch.namemap': fake.FAKE_SERVER_SWITCH_NAME,
|
|
'subtype': fake.FAKE_SUBTYPE,
|
|
'ipspace.name': fake.IPSPACE_NAME,
|
|
'aggregates': [{
|
|
'name': fake.SHARE_AGGREGATE_NAME
|
|
}]
|
|
}
|
|
|
|
self.client._create_vserver(fake.VSERVER_NAME,
|
|
[fake.SHARE_AGGREGATE_NAME],
|
|
fake.IPSPACE_NAME,
|
|
fake.FAKE_SERVER_SWITCH_NAME,
|
|
fake.FAKE_SUBTYPE)
|
|
|
|
mock_sr.assert_called_once_with('/svm/svms', 'post', body=body)
|
|
|
|
@ddt.data((f'/name-services/dns/{fake.FAKE_UUID}', 'patch',
|
|
['fake_domain'], ['fake_ip']),
|
|
(f'/name-services/dns/{fake.FAKE_UUID}', 'delete', [], []),
|
|
('/name-services/dns', 'post', ['fake_domain'], ['fake_ip']))
|
|
@ddt.unpack
|
|
def test_update_dns_configuration_all_operations(self, endpoint,
|
|
operation, domains, ips):
|
|
return_value = fake.FAKE_DNS_CONFIG if operation != 'post' else {}
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value=return_value))
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
body = {
|
|
'domains': domains,
|
|
'servers': ips
|
|
}
|
|
empty_dns_config = (not body['domains'] and not body['servers'])
|
|
if empty_dns_config:
|
|
body = {}
|
|
self.client.update_dns_configuration(ips, domains)
|
|
mock_sr.assert_called_once_with(endpoint, operation, body)
|
|
|
|
@ddt.data(True, False)
|
|
def test_delete_snapshot(self, ignore_owners):
|
|
volume_id = fake.VOLUME.get('uuid')
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=fake.VOLUME))
|
|
response = fake.SNAPSHOTS_REST_RESPONSE
|
|
snapshot_id = response.get('records')[0].get('uuid')
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
query = {
|
|
'name': fake.SNAPSHOT_NAME,
|
|
'fields': 'uuid'
|
|
}
|
|
calls = [mock.call(f'/storage/volumes/{volume_id}/snapshots', 'get',
|
|
query=query)]
|
|
if ignore_owners:
|
|
query_cli = {
|
|
'vserver': self.client.vserver,
|
|
'volume': fake.VOLUME_NAMES[0],
|
|
'snapshot': fake.SNAPSHOT_NAME,
|
|
'ignore-owners': 'true'
|
|
}
|
|
calls.append(mock.call('/private/cli/snapshot', 'delete',
|
|
query=query_cli))
|
|
else:
|
|
calls.append(mock.call(f'/storage/volumes/{volume_id}/'
|
|
f'snapshots/{snapshot_id}', 'delete'))
|
|
|
|
self.client.delete_snapshot(fake.VOLUME_NAMES[0], fake.SNAPSHOT_NAME,
|
|
ignore_owners)
|
|
mock_sr.assert_has_calls(calls)
|
|
|
|
def test_volume_has_luns(self):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
result = self.client.volume_has_luns(fake.VOLUME_NAMES[0])
|
|
query = {
|
|
'location.volume.name': fake.VOLUME_NAMES[0],
|
|
}
|
|
mock_sr.assert_called_once_with('/storage/luns/', 'get', query=query)
|
|
self.assertTrue(result)
|
|
|
|
@ddt.data(fake.VOLUME_JUNCTION_PATH, '')
|
|
def test_volume_has_junctioned_volumes(self, junction_path):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
return_records = True if junction_path else False
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=return_records))
|
|
result = self.client.volume_has_junctioned_volumes(junction_path)
|
|
if junction_path:
|
|
query = {
|
|
'nas.path': junction_path + '/*',
|
|
}
|
|
|
|
mock_sr.assert_called_once_with('/storage/volumes/', 'get',
|
|
query=query)
|
|
self.assertTrue(result)
|
|
else:
|
|
self.assertFalse(result)
|
|
|
|
@ddt.data(fake.VOLUME_JUNCTION_PATH, '')
|
|
def test_get_volume_at_junction_path(self, junction_path):
|
|
response = fake.VOLUME_LIST_SIMPLE_RESPONSE_REST
|
|
return_records = True if junction_path else False
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=return_records))
|
|
query = {
|
|
'nas.path': junction_path,
|
|
'fields': 'name'
|
|
}
|
|
|
|
result = self.client.get_volume_at_junction_path(junction_path)
|
|
expected = {
|
|
'name': response.get('records')[0].get('name')
|
|
}
|
|
|
|
if junction_path:
|
|
mock_sr.assert_called_once_with('/storage/volumes/', 'get',
|
|
query=query)
|
|
self.assertEqual(expected, result)
|
|
else:
|
|
self.assertIsNone(result)
|
|
|
|
def test_get_aggregate_for_volume(self):
|
|
response = fake.FAKE_SVM_AGGREGATES.get('records')[0]
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
result = self.client.get_aggregate_for_volume(fake.VOLUME_NAMES[0])
|
|
expected = fake.SHARE_AGGREGATE_NAMES_LIST
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0],
|
|
'fields': 'aggregates'
|
|
}
|
|
mock_sr.assert_called_once_with('/storage/volumes/', 'get',
|
|
query=query)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_volume_to_manage(self):
|
|
response = fake.FAKE_VOLUME_MANAGE
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
expected = {
|
|
'aggregate': fake.SHARE_AGGREGATE_NAME,
|
|
'aggr-list': [],
|
|
'junction-path': fake.VOLUME_JUNCTION_PATH,
|
|
'name': fake.VOLUME_NAMES[0],
|
|
'type': 'fake_type',
|
|
'style': 'flex',
|
|
'owning-vserver-name': fake.VSERVER_NAME,
|
|
'size': fake.SHARE_SIZE,
|
|
'qos-policy-group-name': fake.QOS_POLICY_GROUP_NAME
|
|
}
|
|
|
|
result = self.client.get_volume_to_manage(fake.SHARE_AGGREGATE_NAME,
|
|
fake.VOLUME_NAMES[0])
|
|
query = {
|
|
'name': fake.VOLUME_NAMES[0],
|
|
'fields': 'name,aggregates.name,nas.path,name,type,style,'
|
|
'svm.name,qos.policy.name,space.size',
|
|
'aggregates.name': fake.SHARE_AGGREGATE_NAME
|
|
}
|
|
mock_sr.assert_called_once_with('/storage/volumes', 'get',
|
|
query=query)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_cifs_share_access(self):
|
|
response = fake.FAKE_CIFS_RECORDS
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
query = {
|
|
'name': fake.SHARE_NAME
|
|
}
|
|
query_acls = {
|
|
'fields': 'user_or_group,permission'
|
|
}
|
|
expected = {
|
|
'Everyone': 'full_control',
|
|
'root': 'no_access'
|
|
}
|
|
result = self.client.get_cifs_share_access(fake.SHARE_NAME)
|
|
svm_uuid = response.get('records')[0].get('svm').get('uuid')
|
|
mock_sr.assert_has_calls([
|
|
mock.call('/protocols/cifs/shares', 'get', query=query),
|
|
mock.call(f'/protocols/cifs/shares/{svm_uuid}/{fake.SHARE_NAME}/'
|
|
'acls', 'get', query=query_acls)
|
|
])
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data((netapp_api.EREST_LICENSE_NOT_INSTALLED, False),
|
|
(netapp_api.EREST_SNAPSHOT_NOT_SPECIFIED, True))
|
|
@ddt.unpack
|
|
def test_check_snaprestore_license(self, code, expected):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
result = self.client.check_snaprestore_license()
|
|
self.assertEqual(expected, result)
|
|
body = {
|
|
'restore_to.snapshot.name': ''
|
|
}
|
|
query = {
|
|
'name': '*'
|
|
}
|
|
self.client.send_request.assert_called_once_with('/storage/volumes',
|
|
'patch',
|
|
body=body,
|
|
query=query)
|
|
|
|
def test_check_snaprestore_license_error(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.check_snaprestore_license)
|
|
|
|
def test__sort_data_ports_by_speed(self):
|
|
ports = fake.FAKE_PORTS
|
|
result = self.client._sort_data_ports_by_speed(ports)
|
|
expected = [{'speed': '4'},
|
|
{'speed': 'auto'},
|
|
{'speed': 'undef'},
|
|
{'speed': 'fake_speed'},
|
|
{'speed': ''}]
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_create_port_and_broadcast_domain(self):
|
|
self.mock_object(self.client, '_create_vlan')
|
|
self.mock_object(self.client, '_ensure_broadcast_domain_for_port')
|
|
res = self.client.create_port_and_broadcast_domain(fake.NODE_NAME,
|
|
fake.PORT,
|
|
fake.VLAN,
|
|
fake.MTU,
|
|
fake.IPSPACE_NAME)
|
|
expected = f'{fake.PORT}-{fake.VLAN}'
|
|
self.assertEqual(expected, res)
|
|
|
|
@ddt.data(netapp_api.EREST_DUPLICATE_ENTRY, None)
|
|
def test__create_vlan(self, code):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
if not code:
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._create_vlan,
|
|
fake.NODE_NAME,
|
|
fake.PORT,
|
|
fake.VLAN)
|
|
|
|
else:
|
|
self.client._create_vlan(fake.NODE_NAME, fake.PORT, fake.VLAN)
|
|
body = {
|
|
'vlan.base_port.name': fake.PORT,
|
|
'node.name': fake.NODE_NAME,
|
|
'vlan.tag': fake.VLAN,
|
|
'type': 'vlan'
|
|
}
|
|
self.client.send_request.assert_called_once_with(
|
|
'/network/ethernet/ports', 'post', body=body)
|
|
|
|
@ddt.data(netapp_api.EREST_ENTRY_NOT_FOUND, None)
|
|
def test_delete_fpolicy_event_error_not_found(self, code):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
if not code:
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.delete_fpolicy_event,
|
|
fake.SHARE_NAME, 'fake_event')
|
|
else:
|
|
self.client.delete_fpolicy_event(fake.SHARE_NAME, 'fake_event')
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
@ddt.data(netapp_api.EREST_ENTRY_NOT_FOUND, None)
|
|
def test_delete_fpolicy_policy_request_error(self, code):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
if not code:
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.delete_fpolicy_policy,
|
|
fake.SHARE_NAME, 'fake_policy')
|
|
else:
|
|
self.client.delete_fpolicy_policy(fake.SHARE_NAME, 'fake_policy')
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test_modify_fpolicy_scope(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
svm_uuid = volume['svm']['uuid']
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
body = {
|
|
'name': fake.FPOLICY_POLICY_NAME,
|
|
'scope.include_shares': fake.SHARE_NAME,
|
|
'scope.include_extension': 'fake_extension',
|
|
'scope.exclude_extension': 'fake_extension'
|
|
}
|
|
self.client.modify_fpolicy_scope(fake.SHARE_NAME,
|
|
fake.FPOLICY_POLICY_NAME,
|
|
[fake.SHARE_NAME],
|
|
['fake_extension'],
|
|
['fake_extension'])
|
|
mock_sr.assert_called_once_with(f'/protocols/fpolicy/{svm_uuid}/'
|
|
'policies/', 'patch', body=body)
|
|
|
|
def test_remove_cifs_share(self):
|
|
response = fake.SVMS_LIST_SIMPLE_RESPONSE_REST
|
|
svm_id = response.get('records')[0]['uuid']
|
|
mock_sr = self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.client.remove_cifs_share(fake.SHARE_NAME)
|
|
query = {
|
|
'name': self.client.vserver,
|
|
'fields': 'uuid'
|
|
}
|
|
mock_sr.assert_has_calls([
|
|
mock.call('/svm/svms', 'get', query=query),
|
|
mock.call(f'/protocols/cifs/shares/{svm_id}'
|
|
f'/{fake.SHARE_NAME}', 'delete')])
|
|
|
|
def test_qos_policy_group_get_error(self):
|
|
code = netapp_api.EREST_NOT_AUTHORIZED
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.qos_policy_group_get,
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
|
|
def test_qos_policy_group_get_not_found(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.qos_policy_group_get,
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
|
|
def test_remove_unused_qos_policy_groups_error(self):
|
|
res_list = [fake.QOS_POLICY_GROUP_REST, netapp_api.api.NaApiError]
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=res_list))
|
|
self.client.remove_unused_qos_policy_groups()
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test_mount_volume_error(self):
|
|
volume = fake.VOLUME_ITEM_SIMPLE_RESPONSE_REST
|
|
code = netapp_api.EREST_SNAPMIRROR_INITIALIZING
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(return_value=volume))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.mount_volume,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
def test_get_aggregate_for_volume_empty(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_aggregate_for_volume,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
def test_get_nfs_export_policy_for_volume_empty(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=False))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_nfs_export_policy_for_volume,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
def test_get_unique_export_policy_id_empty(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=False))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_unique_export_policy_id,
|
|
fake.FPOLICY_POLICY_NAME)
|
|
|
|
def test__remove_nfs_export_rules_error(self):
|
|
self.mock_object(self.client, 'get_unique_export_policy_id',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client._remove_nfs_export_rules,
|
|
fake.FPOLICY_POLICY_NAME,
|
|
[1])
|
|
|
|
def test_get_volume_move_status_error(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=False))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_volume_move_status,
|
|
fake.VOLUME_NAMES[0],
|
|
fake.VSERVER_NAME)
|
|
|
|
def test__set_snapmirror_state_error(self):
|
|
self.mock_object(self.client, 'get_snapmirrors',
|
|
mock.Mock(return_value=[]))
|
|
self.assertRaises(netapp_utils.NetAppDriverException,
|
|
self.client._set_snapmirror_state,
|
|
'fake_state', 'fake_source_path', 'fake_dest_path',
|
|
'fake_source_vserver', 'fake_source_volume',
|
|
'fake_dest_vserver', 'fake_dest_volume')
|
|
|
|
def test__break_snapmirror_error(self):
|
|
fake_snapmirror = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
self.mock_object(self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=fake_snapmirror))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client._break_snapmirror)
|
|
|
|
def test__resync_snapmirror_no_parameter(self):
|
|
mock_snap = self.mock_object(self.client, '_resume_snapmirror')
|
|
self.client._resync_snapmirror()
|
|
mock_snap.assert_called_once_with(None, None, None, None, None, None)
|
|
|
|
def test_add_nfs_export_rule_with_rule_created(self):
|
|
self.mock_object(self.client, '_get_nfs_export_rule_indices',
|
|
mock.Mock(return_value=[1]))
|
|
update = self.mock_object(self.client, '_update_nfs_export_rule')
|
|
remove = self.mock_object(self.client, '_remove_nfs_export_rules')
|
|
self.client.add_nfs_export_rule(fake.FPOLICY_POLICY_NAME,
|
|
'fake_client',
|
|
True,
|
|
'fake_auth')
|
|
update.assert_called_once_with(fake.FPOLICY_POLICY_NAME,
|
|
'fake_client', True, 1, 'fake_auth')
|
|
remove.assert_called_once_with(fake.FPOLICY_POLICY_NAME, [])
|
|
|
|
def test__update_snapmirror_no_snapmirrors(self):
|
|
self.mock_object(self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=[]))
|
|
self.assertRaises(netapp_utils.NetAppDriverException,
|
|
self.client._update_snapmirror)
|
|
|
|
@ddt.data((netapp_api.EREST_SNAPMIRROR_NOT_INITIALIZED,
|
|
'Another transfer is in progress'),
|
|
(None, 'fake'))
|
|
@ddt.unpack
|
|
def test__update_snapmirror_error(self, code, message):
|
|
snapmirrors = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
|
self.mock_object(self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=snapmirrors))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code,
|
|
message)))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client._update_snapmirror)
|
|
|
|
@ddt.data(netapp_api.EREST_DUPLICATE_ENTRY, None)
|
|
def test_create_kerberos_realm_error(self, code):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
if code:
|
|
self.client.create_kerberos_realm(fake.KERBEROS_SECURITY_SERVICE)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
else:
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.create_kerberos_realm,
|
|
fake.KERBEROS_SECURITY_SERVICE)
|
|
|
|
def test_configure_kerberos_error(self):
|
|
self.mock_object(self.client, 'configure_dns')
|
|
self.mock_object(self.client, '_get_kerberos_service_principal_name')
|
|
self.mock_object(self.client, 'get_network_interfaces',
|
|
mock.Mock(return_value=[]))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.configure_kerberos,
|
|
fake.KERBEROS_SECURITY_SERVICE,
|
|
fake.VSERVER_NAME)
|
|
|
|
def test_configure_ldap(self):
|
|
mock_ldap = self.mock_object(self.client, '_create_ldap_client')
|
|
self.client.configure_ldap(fake.LDAP_AD_SECURITY_SERVICE, 30,
|
|
fake.VSERVER_NAME)
|
|
mock_ldap.assert_called_once_with(fake.LDAP_AD_SECURITY_SERVICE,
|
|
vserver_name=fake.VSERVER_NAME)
|
|
|
|
def test_configure_active_directory_error(self):
|
|
self.mock_object(self.client, 'configure_dns')
|
|
self.mock_object(self.client, 'set_preferred_dc')
|
|
self.mock_object(self.client, '_get_cifs_server_name')
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.configure_active_directory,
|
|
fake.LDAP_AD_SECURITY_SERVICE,
|
|
fake.VSERVER_NAME)
|
|
|
|
def test__get_unique_svm_by_name_error(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._get_unique_svm_by_name,
|
|
fake.VSERVER_NAME)
|
|
|
|
def test_get_ontap_version_scoped(self):
|
|
self.client.get_ontap_version = self.original_get_ontap_version
|
|
e = netapp_api.api.NaApiError(code=netapp_api.EREST_NOT_AUTHORIZED)
|
|
res_list = [e, fake.GET_VERSION_RESPONSE_REST]
|
|
version = fake.GET_VERSION_RESPONSE_REST['records'][0]['version']
|
|
expected = {
|
|
'version': version['full'],
|
|
'version-tuple': (9, 11, 1)
|
|
}
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=res_list))
|
|
result = self.client.get_ontap_version(self=self.client, cached=False)
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_licenses_error(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.get_licenses)
|
|
|
|
def test__get_volume_by_args_error(self):
|
|
res = fake.VOLUME_GET_ITER_RESPONSE_REST_PAGE
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=res))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._get_volume_by_args,
|
|
is_root=True)
|
|
|
|
def test_get_aggregate_no_name(self):
|
|
expected = {}
|
|
result = self.client.get_aggregate('')
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_aggregate_error(self):
|
|
self.mock_object(self.client, '_get_aggregates',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
result = self.client.get_aggregate(fake.SHARE_AGGREGATE_NAME)
|
|
expected = {}
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_node_for_aggregate_no_name(self):
|
|
result = self.client.get_node_for_aggregate('')
|
|
self.assertIsNone(result)
|
|
|
|
@ddt.data(netapp_api.EREST_NOT_AUTHORIZED, None)
|
|
def test_get_node_for_aggregate_error(self, code):
|
|
self.mock_object(self.client, '_get_aggregates',
|
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
|
if code:
|
|
r = self.client.get_node_for_aggregate(fake.SHARE_AGGREGATE_NAME)
|
|
self.assertIsNone(r)
|
|
else:
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.get_node_for_aggregate,
|
|
fake.SHARE_AGGREGATE_NAME)
|
|
|
|
def test_get_vserver_aggregate_capabilities_no_response(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_vserver_aggregate_capacities,
|
|
fake.SHARE_AGGREGATE_NAME)
|
|
|
|
def test_get_vserver_aggregate_capacities_no_aggregate(self):
|
|
response = fake.FAKE_AGGREGATES_RESPONSE
|
|
share_name = fake.SHARE_AGGREGATE_NAME
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(return_value=response))
|
|
res = self.client.get_vserver_aggregate_capacities(share_name)
|
|
expected = {}
|
|
self.assertEqual(expected, res)
|
|
|
|
def test_rename_nfs_export_policy_error(self):
|
|
self.mock_object(self.client, 'send_request')
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=False))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.rename_nfs_export_policy,
|
|
'fake_policy_name',
|
|
'fake_new_policy_name')
|
|
|
|
@ddt.data((False, exception.StorageResourceNotFound),
|
|
(True, exception.NetAppException))
|
|
@ddt.unpack
|
|
def test_get_volume_error(self, records, exception):
|
|
res = copy.deepcopy(fake.FAKE_VOLUME_MANAGE)
|
|
res['num_records'] = 2
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=res))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=records))
|
|
self.assertRaises(exception,
|
|
self.client.get_volume,
|
|
fake.VOLUME_NAMES[0])
|
|
|
|
def test_get_volume_no_aggregate(self):
|
|
res = copy.deepcopy(fake.FAKE_VOLUME_MANAGE)
|
|
res.get('records')[0]['aggregates'] = []
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=res))
|
|
fake_volume = res.get('records', [])[0]
|
|
|
|
expected = {
|
|
'aggregate': '',
|
|
'aggr-list': [],
|
|
'junction-path': fake_volume.get('nas', {}).get('path', ''),
|
|
'name': fake_volume.get('name', ''),
|
|
'owning-vserver-name': fake_volume.get('svm', {}).get('name', ''),
|
|
'type': fake_volume.get('type', ''),
|
|
'style': fake_volume.get('style', ''),
|
|
'size': fake_volume.get('space', {}).get('size', ''),
|
|
'qos-policy-group-name': fake_volume.get('qos', {})
|
|
.get('policy', {})
|
|
.get('name', ''),
|
|
'style-extended': fake_volume.get('style', '')
|
|
}
|
|
result = self.client.get_volume(fake.VOLUME_NAMES[0])
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_job_state_error(self):
|
|
response = {
|
|
'records': [fake.JOB_SUCCESSFUL_REST,
|
|
fake.JOB_SUCCESSFUL_REST]
|
|
}
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.mock_object(self.client, '_has_records',
|
|
mock.Mock(return_value=True))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.get_job_state,
|
|
fake.JOB_ID)
|
|
|
|
def test_get_volume_efficiency_status_error(self):
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.client.get_volume_efficiency_status(fake.VOLUME_NAMES[0])
|
|
self.assertEqual(1, client_cmode_rest.LOG.error.call_count)
|
|
|
|
def test_get_fpolicy_scopes_not_found(self):
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
result = self.client.get_fpolicy_scopes(fake.SHARE_NAME)
|
|
expected = []
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_delete_fpolicy_policy_error(self):
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
self.mock_object(self.client, 'send_request')
|
|
res = self.client.delete_fpolicy_policy(fake.SHARE_NAME,
|
|
fake.FPOLICY_POLICY_NAME)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
self.assertIsNone(res)
|
|
|
|
def test_delete_fpolicy_event_error(self):
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
self.mock_object(self.client, 'send_request')
|
|
res = self.client.delete_fpolicy_event(fake.SHARE_NAME,
|
|
fake.FPOLICY_EVENT_NAME)
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
self.assertIsNone(res)
|
|
|
|
def test_delete_nfs_export_policy_no_records(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
res = self.client.delete_nfs_export_policy(fake.FPOLICY_POLICY_NAME)
|
|
self.assertIsNone(res)
|
|
|
|
def test_remove_cifs_share_not_found(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.remove_cifs_share,
|
|
fake.SHARE_NAME)
|
|
|
|
@ddt.data(netapp_api.EREST_ENTRY_NOT_FOUND, None)
|
|
def test_remove_cifs_share_error(self, code):
|
|
responses = [fake.SVMS_LIST_SIMPLE_RESPONSE_REST,
|
|
netapp_api.api.NaApiError(code=code)]
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=responses))
|
|
if not code:
|
|
self.assertRaises(netapp_api.api.NaApiError,
|
|
self.client.remove_cifs_share,
|
|
fake.SHARE_NAME)
|
|
else:
|
|
result = self.client.remove_cifs_share(fake.SHARE_NAME)
|
|
self.assertIsNone(result)
|
|
|
|
def test_qos_policy_group_does_not_exists(self):
|
|
self.mock_object(self.client, 'qos_policy_group_get',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
result = self.client.qos_policy_group_exists(fake.QOS_POLICY_GROUP)
|
|
self.assertFalse(result)
|
|
|
|
def test_qos_policy_group_rename_error(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.qos_policy_group_rename,
|
|
fake.QOS_POLICY_GROUP_NAME,
|
|
'fake_new_qos_policy_group_name')
|
|
|
|
def test_qos_policy_group_rename_same_name(self):
|
|
res = self.client.qos_policy_group_rename(fake.QOS_POLICY_GROUP_NAME,
|
|
fake.QOS_POLICY_GROUP_NAME)
|
|
self.assertIsNone(res)
|
|
|
|
def test_qos_policy_group_modify_error(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.qos_policy_group_modify,
|
|
fake.QOS_POLICY_GROUP_NAME,
|
|
fake.QOS_MAX_THROUGHPUT)
|
|
|
|
def test_update_kerberos_realm_error(self):
|
|
self.mock_object(self.client,
|
|
'_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.update_kerberos_realm,
|
|
fake.KERBEROS_SECURITY_SERVICE)
|
|
|
|
@ddt.data(('fake_domain', 'fake_server'), (None, None))
|
|
@ddt.unpack
|
|
def test_modify_ldap_error(self, domain, server):
|
|
security_service = {
|
|
'domain': domain,
|
|
'server': server,
|
|
'user': 'fake_user',
|
|
'ou': 'fake_ou',
|
|
'dns_ip': 'fake_ip',
|
|
'password': 'fake_password'
|
|
}
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client, 'send_request')
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.modify_ldap,
|
|
security_service,
|
|
fake.LDAP_AD_SECURITY_SERVICE)
|
|
|
|
def test_update_dns_configuration_error(self):
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
dns_config = {
|
|
'domains': [fake.KERBEROS_SECURITY_SERVICE['domain']],
|
|
'dns-ips': [fake.KERBEROS_SECURITY_SERVICE['dns_ip']],
|
|
}
|
|
self.mock_object(self.client, 'get_dns_config',
|
|
mock.Mock(return_value=dns_config))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.update_dns_configuration,
|
|
['fake_ips'], ['fake_domain'])
|
|
|
|
def test_remove_preferred_dcs_error(self):
|
|
fake_response = [fake.PREFERRED_DC_REST,
|
|
netapp_api.api.NaApiError]
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=fake_response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.remove_preferred_dcs,
|
|
fake.LDAP_AD_SECURITY_SERVICE,
|
|
fake.FAKE_UUID)
|
|
|
|
def test_set_preferred_dc_error(self):
|
|
security = copy.deepcopy(fake.LDAP_AD_SECURITY_SERVICE)
|
|
security['server'] = 'fake_server'
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=fake.FAKE_UUID))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error()))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.set_preferred_dc,
|
|
security,
|
|
fake.VSERVER_NAME)
|
|
|
|
def test_set_preferred_dc_no_server(self):
|
|
result = self.client.set_preferred_dc(fake.LDAP_AD_SECURITY_SERVICE,
|
|
fake.VSERVER_NAME)
|
|
self.assertIsNone(result)
|
|
|
|
def test__get_svm_peer_uuid_error(self):
|
|
response = fake.NO_RECORDS_RESPONSE_REST
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=response))
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._get_svm_peer_uuid,
|
|
fake.VSERVER_NAME,
|
|
fake.VSERVER_PEER_NAME)
|
|
|
|
def test_create_vserver_dp_destination(self):
|
|
mock_vserver = self.mock_object(self.client, '_create_vserver')
|
|
self.client.create_vserver_dp_destination(fake.VSERVER_NAME,
|
|
fake.FAKE_AGGR_LIST,
|
|
fake.IPSPACE_NAME)
|
|
mock_vserver.assert_called_once_with(fake.VSERVER_NAME,
|
|
fake.FAKE_AGGR_LIST,
|
|
fake.IPSPACE_NAME,
|
|
subtype='dp_destination')
|
|
|
|
@ddt.data(':', '.')
|
|
def test_create_route_no_destination(self, gateway):
|
|
mock_sr = self.mock_object(self.client, 'send_request')
|
|
body = {
|
|
'gateway': gateway,
|
|
'destination.address': '::' if ":" in gateway else '0.0.0.0',
|
|
'destination.netmask': '0'
|
|
}
|
|
self.client.create_route(gateway)
|
|
mock_sr.assert_called_once_with('/network/ip/routes', 'post',
|
|
body=body)
|
|
|
|
def test_list_root_aggregates(self):
|
|
return_value = fake.FAKE_ROOT_AGGREGATES_RESPONSE
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=return_value))
|
|
|
|
result = self.client.list_root_aggregates()
|
|
|
|
expected = [fake.SHARE_AGGREGATE_NAME]
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data(("fake_server", "fake_domain"), (None, None))
|
|
@ddt.unpack
|
|
def test__create_ldap_client_error(self, server, domain):
|
|
security_service = {
|
|
'server': server,
|
|
'domain': domain,
|
|
'user': 'fake_user',
|
|
'ou': 'fake_ou',
|
|
'dns_ip': 'fake_ip',
|
|
'password': 'fake_password'
|
|
}
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._create_ldap_client,
|
|
security_service)
|
|
|
|
@ddt.data(["password"], ["user"])
|
|
def test__modify_active_directory_security_service_error(self, keys):
|
|
svm_uuid = fake.FAKE_UUID
|
|
user_records = fake.FAKE_CIFS_LOCAL_USER.get('records')[0]
|
|
self.mock_object(self.client, '_get_unique_svm_by_name',
|
|
mock.Mock(return_value=svm_uuid))
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(side_effect=[user_records,
|
|
netapp_api.api.NaApiError]))
|
|
self.mock_object(self.client, 'remove_preferred_dcs')
|
|
self.mock_object(self.client, 'set_preferred_dc')
|
|
new_security_service = {
|
|
'user': 'new_user',
|
|
'password': 'new_password',
|
|
'server': 'fake_server'
|
|
}
|
|
|
|
current_security_service = {
|
|
'server': 'fake_current_server'
|
|
}
|
|
|
|
self.assertRaises(
|
|
exception.NetAppException,
|
|
self.client.modify_active_directory_security_service,
|
|
fake.VSERVER_NAME,
|
|
keys,
|
|
new_security_service,
|
|
current_security_service)
|
|
|
|
def test_disable_kerberos_error(self):
|
|
fake_api_response = fake.NFS_LIFS_REST
|
|
api_error = self._mock_api_error()
|
|
self.mock_object(self.client, 'get_network_interfaces',
|
|
mock.Mock(return_value=fake_api_response))
|
|
self.mock_object(
|
|
self.client, 'send_request',
|
|
mock.Mock(side_effect=api_error))
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client.disable_kerberos,
|
|
fake.LDAP_AD_SECURITY_SERVICE)
|
|
|
|
def test_set_volume_snapdir_access_exception(self):
|
|
fake_hide_snapdir = 'fake-snapdir'
|
|
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
self.assertRaises(exception.SnapshotResourceNotFound,
|
|
self.client.set_volume_snapdir_access,
|
|
fake.VOLUME_NAMES[0],
|
|
fake_hide_snapdir)
|
|
|
|
def test__get_broadcast_domain_for_port_exception(self):
|
|
fake_response_empty = {
|
|
"records": [{}]
|
|
}
|
|
self.mock_object(self.client, 'send_request', mock.Mock(
|
|
return_value=fake_response_empty))
|
|
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._get_broadcast_domain_for_port,
|
|
fake.NODE_NAME,
|
|
fake.PORT)
|
|
|
|
def test__configure_nfs_exception(self):
|
|
fake_nfs = {
|
|
'udp-max-xfer-size': 10000,
|
|
'tcp-max-xfer-size': 10000,
|
|
}
|
|
self.assertRaises(exception.NetAppException,
|
|
self.client._configure_nfs,
|
|
fake_nfs,
|
|
fake.FAKE_UUID)
|
|
|
|
def test_get_snapshot_exception(self):
|
|
self.mock_object(self.client, '_get_volume_by_args',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
self.assertRaises(exception.SnapshotResourceNotFound,
|
|
self.client.get_snapshot,
|
|
fake.VOLUME_NAMES[0],
|
|
fake.SNAPSHOT_NAME)
|
|
|
|
def test_delete_snapshot_exception(self):
|
|
self.mock_object(self.client,
|
|
'_get_volume_by_args',
|
|
mock.Mock(side_effect=exception.NetAppException))
|
|
self.client.delete_snapshot(fake.VOLUME_NAMES[0], fake.SNAPSHOT_NAME,
|
|
True)
|
|
|
|
self.assertEqual(1, client_cmode_rest.LOG.warning.call_count)
|
|
|
|
def test_set_nfs_export_policy_for_volume_exception(self):
|
|
return_code = netapp_api.EREST_CANNOT_MODITY_OFFLINE_VOLUME
|
|
self.mock_object(self.client,
|
|
'send_request',
|
|
mock.Mock(side_effect=self._mock_api_error(
|
|
code=return_code)))
|
|
self.client.set_nfs_export_policy_for_volume(
|
|
fake.VOLUME_NAMES[0], fake.EXPORT_POLICY_NAME)
|
|
|
|
self.assertEqual(1, client_cmode_rest.LOG.debug.call_count)
|
|
|
|
def test__break_snapmirror_exception(self):
|
|
fake_snapmirror = copy.deepcopy(fake.REST_GET_SNAPMIRRORS_RESPONSE)
|
|
fake_snapmirror[0]['transferring-state'] = 'error'
|
|
|
|
self.mock_object(
|
|
self.client, '_get_snapmirrors',
|
|
mock.Mock(return_value=fake_snapmirror))
|
|
|
|
self.assertRaises(netapp_utils.NetAppDriverException,
|
|
self.client._break_snapmirror)
|
|
|
|
def test_get_svm_volumes_total_size(self):
|
|
expected = 1
|
|
|
|
fake_query = {
|
|
'svm.name': fake.VSERVER_NAME,
|
|
'fields': 'size'
|
|
}
|
|
|
|
self.mock_object(self.client, 'send_request',
|
|
mock.Mock(return_value=fake.FAKE_GET_VOLUME))
|
|
|
|
result = self.client.get_svm_volumes_total_size(fake.VSERVER_NAME)
|
|
|
|
self.client.send_request.assert_called_once_with(
|
|
'/storage/volumes/', 'get', query=fake_query)
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data(fake.CIFS_SECURITY_SERVICE, fake.CIFS_SECURITY_SERVICE_3)
|
|
def test_configure_active_directory_credential_error(self,
|
|
security_service):
|
|
msg = "could not authenticate"
|
|
fake_security = copy.deepcopy(security_service)
|
|
|
|
self.mock_object(self.client, 'configure_dns')
|
|
self.mock_object(self.client, 'set_preferred_dc')
|
|
self.mock_object(self.client, '_get_cifs_server_name')
|
|
self.mock_object(self.client, 'send_request',
|
|
self._mock_api_error(code=netapp_api.api.EAPIERROR,
|
|
message=msg))
|
|
self.assertRaises(exception.SecurityServiceFailedAuth,
|
|
self.client.configure_active_directory,
|
|
fake_security,
|
|
fake.VSERVER_NAME)
|
|
|
|
@ddt.data(fake.CIFS_SECURITY_SERVICE, fake.CIFS_SECURITY_SERVICE_3)
|
|
def test_configure_active_directory_user_privilege_error(self,
|
|
security_service):
|
|
msg = "insufficient access"
|
|
fake_security = copy.deepcopy(security_service)
|
|
|
|
self.mock_object(self.client, 'configure_dns')
|
|
self.mock_object(self.client, 'set_preferred_dc')
|
|
self.mock_object(self.client, '_get_cifs_server_name')
|
|
self.mock_object(self.client, 'send_request',
|
|
self._mock_api_error(code=netapp_api.api.EAPIERROR,
|
|
message=msg))
|
|
self.assertRaises(exception.SecurityServiceFailedAuth,
|
|
self.client.configure_active_directory,
|
|
fake_security,
|
|
fake.VSERVER_NAME)
|