cinder/cinder/tests/volume/drivers/netapp/dataontap/client/test_client_7mode.py

552 lines
23 KiB
Python

# Copyright (c) 2014 Alex Meade. All rights reserved.
# 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 uuid
from lxml import etree
import mock
import six
from cinder import test
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
from cinder.volume.drivers.netapp.dataontap.client import client_7mode
CONNECTION_INFO = {'hostname': 'hostname',
'transport_type': 'https',
'port': 443,
'username': 'admin',
'password': 'passw0rd'}
class NetApp7modeClientTestCase(test.TestCase):
def setUp(self):
super(NetApp7modeClientTestCase, self).setUp()
self.fake_volume = six.text_type(uuid.uuid4())
with mock.patch.object(client_7mode.Client,
'get_ontapi_version',
return_value=(1, 20)):
self.client = client_7mode.Client([self.fake_volume],
**CONNECTION_INFO)
self.client.connection = mock.MagicMock()
self.connection = self.client.connection
self.fake_lun = six.text_type(uuid.uuid4())
def tearDown(self):
super(NetApp7modeClientTestCase, self).tearDown()
def test_get_target_details_no_targets(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<iscsi-portal-list-entries>
</iscsi-portal-list-entries>
</results>"""))
self.connection.invoke_successfully.return_value = response
target_list = self.client.get_target_details()
self.assertEqual([], target_list)
def test_get_target_details(self):
expected_target = {
"address": "127.0.0.1",
"port": "1337",
"tpgroup-tag": "7777",
}
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<iscsi-portal-list-entries>
<iscsi-portal-list-entry-info>
<ip-address>%(address)s</ip-address>
<ip-port>%(port)s</ip-port>
<tpgroup-tag>%(tpgroup-tag)s</tpgroup-tag>
</iscsi-portal-list-entry-info>
</iscsi-portal-list-entries>
</results>""" % expected_target))
self.connection.invoke_successfully.return_value = response
target_list = self.client.get_target_details()
self.assertEqual([expected_target], target_list)
def test_get_iscsi_service_details_with_no_iscsi_service(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
</results>"""))
self.connection.invoke_successfully.return_value = response
iqn = self.client.get_iscsi_service_details()
self.assertEqual(None, iqn)
def test_get_iscsi_service_details(self):
expected_iqn = 'iqn.1998-01.org.openstack.iscsi:name1'
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<node-name>%s</node-name>
</results>""" % expected_iqn))
self.connection.invoke_successfully.return_value = response
iqn = self.client.get_iscsi_service_details()
self.assertEqual(expected_iqn, iqn)
def test_get_lun_list(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<luns>
<lun-info></lun-info>
<lun-info></lun-info>
</luns>
</results>"""))
self.connection.invoke_successfully.return_value = response
luns = self.client.get_lun_list()
self.assertEqual(2, len(luns))
def test_get_igroup_by_initiator_none_found(self):
initiator = 'initiator'
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<initiator-groups>
</initiator-groups>
</results>"""))
self.connection.invoke_successfully.return_value = response
igroup = self.client.get_igroup_by_initiator(initiator)
self.assertEqual([], igroup)
def test_get_igroup_by_initiator(self):
initiator = 'initiator'
expected_igroup = {
"initiator-group-os-type": None,
"initiator-group-type": "1337",
"initiator-group-name": "vserver",
}
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<initiator-groups>
<initiator-group-info>
<initiators>
<initiator-info>
<initiator-name>initiator</initiator-name>
</initiator-info>
</initiators>
<initiator-group-type>%(initiator-group-type)s</initiator-group-type>
<initiator-group-name>%(initiator-group-name)s</initiator-group-name>
</initiator-group-info>
</initiator-groups>
</results>""" % expected_igroup))
self.connection.invoke_successfully.return_value = response
igroup = self.client.get_igroup_by_initiator(initiator)
self.assertEqual([expected_igroup], igroup)
def test_clone_lun(self):
fake_clone_start = netapp_api.NaElement(
etree.XML("""<results status="passed">
<clone-id>
<clone-id-info>
<clone-op-id>1337</clone-op-id>
<volume-uuid>volume-uuid</volume-uuid>
</clone-id-info>
</clone-id>
</results>"""))
fake_clone_status = netapp_api.NaElement(
etree.XML("""<results status="passed">
<status>
<ops-info>
<clone-state>completed</clone-state>
</ops-info>
</status>
</results>"""))
self.connection.invoke_successfully.side_effect = [fake_clone_start,
fake_clone_status]
self.client.clone_lun('path', 'new_path', 'fakeLUN', 'newFakeLUN')
self.assertEqual(2, self.connection.invoke_successfully.call_count)
def test_clone_lun_api_error(self):
fake_clone_start = netapp_api.NaElement(
etree.XML("""<results status="passed">
<clone-id>
<clone-id-info>
<clone-op-id>1337</clone-op-id>
<volume-uuid>volume-uuid</volume-uuid>
</clone-id-info>
</clone-id>
</results>"""))
fake_clone_status = netapp_api.NaElement(
etree.XML("""<results status="passed">
<status>
<ops-info>
<clone-state>error</clone-state>
</ops-info>
</status>
</results>"""))
self.connection.invoke_successfully.side_effect = [fake_clone_start,
fake_clone_status]
self.assertRaises(netapp_api.NaApiError, self.client.clone_lun,
'path', 'new_path', 'fakeLUN', 'newFakeLUN')
def test_clone_lun_multiple_zapi_calls(self):
# Max block-ranges per call = 32, max blocks per range = 2^24
# Force 2 calls
bc = 2 ** 24 * 32 * 2
fake_clone_start = netapp_api.NaElement(
etree.XML("""<results status="passed">
<clone-id>
<clone-id-info>
<clone-op-id>1337</clone-op-id>
<volume-uuid>volume-uuid</volume-uuid>
</clone-id-info>
</clone-id>
</results>"""))
fake_clone_status = netapp_api.NaElement(
etree.XML("""<results status="passed">
<status>
<ops-info>
<clone-state>completed</clone-state>
</ops-info>
</status>
</results>"""))
self.connection.invoke_successfully.side_effect = [fake_clone_start,
fake_clone_status,
fake_clone_start,
fake_clone_status]
self.client.clone_lun('path', 'new_path', 'fakeLUN', 'newFakeLUN',
block_count=bc)
self.assertEqual(4, self.connection.invoke_successfully.call_count)
def test_clone_lun_wait_for_clone_to_finish(self):
# Max block-ranges per call = 32, max blocks per range = 2^24
# Force 2 calls
bc = 2 ** 24 * 32 * 2
fake_clone_start = netapp_api.NaElement(
etree.XML("""<results status="passed">
<clone-id>
<clone-id-info>
<clone-op-id>1337</clone-op-id>
<volume-uuid>volume-uuid</volume-uuid>
</clone-id-info>
</clone-id>
</results>"""))
fake_clone_status = netapp_api.NaElement(
etree.XML("""<results status="passed">
<status>
<ops-info>
<clone-state>running</clone-state>
</ops-info>
</status>
</results>"""))
fake_clone_status_completed = netapp_api.NaElement(
etree.XML("""<results status="passed">
<status>
<ops-info>
<clone-state>completed</clone-state>
</ops-info>
</status>
</results>"""))
fake_responses = [fake_clone_start,
fake_clone_status,
fake_clone_status_completed,
fake_clone_start,
fake_clone_status_completed]
self.connection.invoke_successfully.side_effect = fake_responses
with mock.patch('time.sleep') as mock_sleep:
self.client.clone_lun('path', 'new_path', 'fakeLUN',
'newFakeLUN', block_count=bc)
mock_sleep.assert_called_once_with(1)
self.assertEqual(5, self.connection.invoke_successfully.call_count)
def test_get_lun_by_args(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<luns>
<lun-info></lun-info>
</luns>
</results>"""))
self.connection.invoke_successfully.return_value = response
luns = self.client.get_lun_by_args()
self.assertEqual(1, len(luns))
def test_get_lun_by_args_no_lun_found(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<luns>
</luns>
</results>"""))
self.connection.invoke_successfully.return_value = response
luns = self.client.get_lun_by_args()
self.assertEqual(0, len(luns))
def test_get_lun_by_args_with_args_specified(self):
path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<luns>
<lun-info></lun-info>
</luns>
</results>"""))
self.connection.invoke_successfully.return_value = response
lun = self.client.get_lun_by_args(path=path)
__, _args, __ = self.connection.invoke_successfully.mock_calls[0]
actual_request = _args[0]
lun_info_args = actual_request.get_children()
# Assert request is made with correct arguments
self.assertEqual('path', lun_info_args[0].get_name())
self.assertEqual(path, lun_info_args[0].get_content())
self.assertEqual(1, len(lun))
def test_get_filer_volumes(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<volumes>
<volume-info></volume-info>
</volumes>
</results>"""))
self.connection.invoke_successfully.return_value = response
volumes = self.client.get_filer_volumes()
self.assertEqual(1, len(volumes))
def test_get_filer_volumes_no_volumes(self):
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<volumes>
</volumes>
</results>"""))
self.connection.invoke_successfully.return_value = response
volumes = self.client.get_filer_volumes()
self.assertEqual([], volumes)
def test_get_lun_map(self):
path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
self.connection.invoke_successfully.return_value = mock.Mock()
self.client.get_lun_map(path=path)
__, _args, __ = self.connection.invoke_successfully.mock_calls[0]
actual_request = _args[0]
lun_info_args = actual_request.get_children()
# Assert request is made with correct arguments
self.assertEqual('path', lun_info_args[0].get_name())
self.assertEqual(path, lun_info_args[0].get_content())
def test_set_space_reserve(self):
path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun)
self.connection.invoke_successfully.return_value = mock.Mock()
self.client.set_space_reserve(path, 'true')
__, _args, __ = self.connection.invoke_successfully.mock_calls[0]
actual_request = _args[0]
lun_info_args = actual_request.get_children()
# Assert request is made with correct arguments
self.assertEqual('path', lun_info_args[0].get_name())
self.assertEqual(path, lun_info_args[0].get_content())
self.assertEqual('enable', lun_info_args[1].get_name())
self.assertEqual('true', lun_info_args[1].get_content())
def test_get_actual_path_for_export(self):
fake_export_path = 'fake_export_path'
expected_actual_pathname = 'fake_actual_pathname'
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<actual-pathname>%(path)s</actual-pathname>
</results>""" % {'path': expected_actual_pathname}))
self.connection.invoke_successfully.return_value = response
actual_pathname = self.client.get_actual_path_for_export(
fake_export_path)
self.assertEqual(expected_actual_pathname, actual_pathname)
def test_clone_file(self):
expected_src_path = "fake_src_path"
expected_dest_path = "fake_dest_path"
fake_volume_id = '0309c748-0d94-41f0-af46-4fbbd76686cf'
fake_clone_op_id = 'c22ad299-ecec-4ec0-8de4-352b887bfce2'
fake_clone_id_response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<clone-id>
<clone-id-info>
<volume-uuid>%(volume)s</volume-uuid>
<clone-op-id>%(clone_id)s</clone-op-id>
</clone-id-info>
</clone-id>
</results>""" % {'volume': fake_volume_id,
'clone_id': fake_clone_op_id}))
fake_clone_list_response = netapp_api.NaElement(
etree.XML("""<results>
<clone-list-status>
<clone-id-info>
<volume-uuid>%(volume)s</volume-uuid>
<clone-op-id>%(clone_id)s</clone-op-id>
</clone-id-info>
<clone-op-id>%(clone_id)s</clone-op-id>
</clone-list-status>
<status>
<ops-info>
<clone-state>completed</clone-state>
</ops-info>
</status>
</results>""" % {'volume': fake_volume_id,
'clone_id': fake_clone_op_id}))
self.connection.invoke_successfully.side_effect = [
fake_clone_id_response, fake_clone_list_response]
self.client.clone_file(expected_src_path, expected_dest_path)
__, _args, __ = self.connection.invoke_successfully.mock_calls[0]
actual_request = _args[0]
actual_src_path = actual_request \
.get_child_by_name('source-path').get_content()
actual_dest_path = actual_request.get_child_by_name(
'destination-path').get_content()
self.assertEqual(expected_src_path, actual_src_path)
self.assertEqual(expected_dest_path, actual_dest_path)
self.assertEqual(actual_request.get_child_by_name(
'destination-exists'), None)
def test_clone_file_when_clone_fails(self):
"""Ensure clone is cleaned up on failure."""
expected_src_path = "fake_src_path"
expected_dest_path = "fake_dest_path"
fake_volume_id = '0309c748-0d94-41f0-af46-4fbbd76686cf'
fake_clone_op_id = 'c22ad299-ecec-4ec0-8de4-352b887bfce2'
fake_clone_id_response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<clone-id>
<clone-id-info>
<volume-uuid>%(volume)s</volume-uuid>
<clone-op-id>%(clone_id)s</clone-op-id>
</clone-id-info>
</clone-id>
</results>""" % {'volume': fake_volume_id,
'clone_id': fake_clone_op_id}))
fake_clone_list_response = netapp_api.NaElement(
etree.XML("""<results>
<clone-list-status>
<clone-id-info>
<volume-uuid>%(volume)s</volume-uuid>
<clone-op-id>%(clone_id)s</clone-op-id>
</clone-id-info>
<clone-op-id>%(clone_id)s</clone-op-id>
</clone-list-status>
<status>
<ops-info>
<clone-state>failed</clone-state>
</ops-info>
</status>
</results>""" % {'volume': fake_volume_id,
'clone_id': fake_clone_op_id}))
fake_clone_clear_response = mock.Mock()
self.connection.invoke_successfully.side_effect = [
fake_clone_id_response, fake_clone_list_response,
fake_clone_clear_response]
self.assertRaises(netapp_api.NaApiError,
self.client.clone_file,
expected_src_path,
expected_dest_path)
__, _args, __ = self.connection.invoke_successfully.mock_calls[0]
actual_request = _args[0]
actual_src_path = actual_request \
.get_child_by_name('source-path').get_content()
actual_dest_path = actual_request.get_child_by_name(
'destination-path').get_content()
self.assertEqual(expected_src_path, actual_src_path)
self.assertEqual(expected_dest_path, actual_dest_path)
self.assertEqual(actual_request.get_child_by_name(
'destination-exists'), None)
__, _args, __ = self.connection.invoke_successfully.mock_calls[1]
actual_request = _args[0]
actual_clone_id = actual_request.get_child_by_name('clone-id')
actual_clone_id_info = actual_clone_id.get_child_by_name(
'clone-id-info')
actual_clone_op_id = actual_clone_id_info.get_child_by_name(
'clone-op-id').get_content()
actual_volume_uuid = actual_clone_id_info.get_child_by_name(
'volume-uuid').get_content()
self.assertEqual(fake_clone_op_id, actual_clone_op_id)
self.assertEqual(fake_volume_id, actual_volume_uuid)
# Ensure that the clone-clear call is made upon error
__, _args, __ = self.connection.invoke_successfully.mock_calls[2]
actual_request = _args[0]
actual_clone_id = actual_request \
.get_child_by_name('clone-id').get_content()
self.assertEqual(fake_clone_op_id, actual_clone_id)
def test_get_file_usage(self):
expected_bytes = "2048"
fake_path = 'fake_path'
response = netapp_api.NaElement(
etree.XML("""<results status="passed">
<unique-bytes>%(unique-bytes)s</unique-bytes>
</results>""" % {'unique-bytes': expected_bytes}))
self.connection.invoke_successfully.return_value = response
actual_bytes = self.client.get_file_usage(fake_path)
self.assertEqual(expected_bytes, actual_bytes)
def test_get_ifconfig(self):
expected_response = mock.Mock()
self.connection.invoke_successfully.return_value = expected_response
actual_response = self.client.get_ifconfig()
__, _args, __ = self.connection.invoke_successfully.mock_calls[0]
actual_request = _args[0]
self.assertEqual('net-ifconfig-get', actual_request.get_name())
self.assertEqual(expected_response, actual_response)