299 lines
13 KiB
Python
299 lines
13 KiB
Python
# Copyright 2020 Red Hat, 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.
|
|
|
|
from unittest import mock
|
|
|
|
import ddt
|
|
|
|
from cinder.tests.unit import test
|
|
from cinder.zonemanager.drivers.brocade import brcd_rest_fc_zone_client as \
|
|
rest_client
|
|
from cinder.zonemanager.drivers.brocade import fc_zone_constants
|
|
|
|
|
|
@ddt.ddt
|
|
class TestBrcdRestClient(test.TestCase):
|
|
def setUp(self):
|
|
super(TestBrcdRestClient, self).setUp()
|
|
self.client = self._get_client()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_login', mock.Mock())
|
|
def _get_client(self,
|
|
ip=mock.sentinel.ipaddress,
|
|
user=mock.sentinel.username,
|
|
password=mock.sentinel.password,
|
|
port=mock.sentinel.port,
|
|
vfid=mock.sentinel.vfid,
|
|
protocol=mock.sentinel.protocol):
|
|
return rest_client.BrcdRestFCZoneClient(ip, user, password, port, vfid,
|
|
protocol)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_login')
|
|
def test_init(self, login_mock):
|
|
res = rest_client.BrcdRestFCZoneClient(mock.sentinel.ipaddress,
|
|
mock.sentinel.username,
|
|
mock.sentinel.password,
|
|
mock.sentinel.port,
|
|
mock.sentinel.vfid,
|
|
mock.sentinel.protocol)
|
|
|
|
self.assertEqual(mock.sentinel.ipaddress, res.sw_ip)
|
|
self.assertEqual(mock.sentinel.username, res.sw_user)
|
|
self.assertEqual(mock.sentinel.password, res.sw_pwd)
|
|
self.assertEqual(mock.sentinel.vfid, res.vfid)
|
|
self.assertEqual(mock.sentinel.protocol, res.protocol)
|
|
# Port parameter is not used by the class
|
|
self.assertEqual('', res.status_code)
|
|
self.assertIsNone(res.session)
|
|
login_mock.assert_called_once_with()
|
|
|
|
@ddt.data((False, '7.4.0'), (False, '8.2.0'),
|
|
(True, '8.2.1'), (True, '9.0.0'))
|
|
@ddt.unpack
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient,
|
|
'_get_firmware_version')
|
|
def test_is_supported_firmware(self, expected, version, mock_get_fw):
|
|
mock_get_fw.return_value = version
|
|
|
|
res = self.client.is_supported_firmware()
|
|
|
|
self.assertIs(expected, res)
|
|
mock_get_fw.assert_called_once_with()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient,
|
|
'_get_effective_zone_set')
|
|
def test_get_active_zone_set(self, get_effective_mock):
|
|
get_effective_mock.return_value = (mock.sentinel.active_zone_set,
|
|
mock.sentinel.checksum)
|
|
res = self.client.get_active_zone_set()
|
|
self.assertEqual(mock.sentinel.active_zone_set, res)
|
|
get_effective_mock.assert_called_once_with()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_get_name_server')
|
|
def test_get_nameserver_info(self, get_ns_mock):
|
|
res = self.client.get_nameserver_info()
|
|
self.assertEqual(get_ns_mock.return_value, res)
|
|
get_ns_mock.assert_called_once_with()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_add_zones')
|
|
def test_add_zones(self, add_mock):
|
|
self.client.add_zones(mock.sentinel.add_zone_map,
|
|
mock.sentinel.activate,
|
|
mock.sentinel.active_zone_set__not_used)
|
|
add_mock.assert_called_once_with(mock.sentinel.add_zone_map,
|
|
mock.sentinel.activate)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_update_zones')
|
|
def test_update_zones(self, update_mock):
|
|
self.client.update_zones(mock.sentinel.zone_map,
|
|
mock.sentinel.activate,
|
|
mock.sentinel.operation,
|
|
mock.sentinel.active_zone_set__not_used)
|
|
update_mock.assert_called_once_with(mock.sentinel.zone_map,
|
|
mock.sentinel.activate,
|
|
mock.sentinel.operation)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_delete_zones')
|
|
def test_delete_zones(self, delete_mock):
|
|
self.client.delete_zones(mock.sentinel.zone_names,
|
|
mock.sentinel.activate,
|
|
mock.sentinel.active_zone_set__not_used)
|
|
delete_mock.assert_called_once_with(mock.sentinel.zone_names,
|
|
mock.sentinel.activate)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_logout')
|
|
def test_cleanup(self, logout_mock):
|
|
self.client.cleanup()
|
|
logout_mock.assert_called_once_with()
|
|
|
|
@ddt.data(200, 400)
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
@mock.patch.object(rest_client, 'requests')
|
|
def test__login(self, status_code, requests_mock, url_mock):
|
|
session_mock = requests_mock.Session
|
|
post_mock = session_mock.return_value.post
|
|
post_mock.return_value.status_code = status_code
|
|
post_mock.return_value.headers = {'Authorization': mock.sentinel.auth}
|
|
|
|
adapter_mock = requests_mock.adapters.HTTPAdapter
|
|
adapter_mock.return_value = 'adapter'
|
|
|
|
client = self._get_client(protocol=fc_zone_constants.REST_HTTPS,
|
|
user='username', password='password')
|
|
expected_headers = {'User-Agent': 'OpenStack Zone Driver',
|
|
'Accept': 'application/yang-data+json',
|
|
'Content-Type': 'application/yang-data+json',
|
|
'Authorization': mock.sentinel.auth}
|
|
try:
|
|
res = client._login()
|
|
self.assertEqual(200, res)
|
|
except rest_client.exception.BrocadeZoningRestException:
|
|
self.assertNotEqual(200, status_code)
|
|
expected_headers['Authorization'] = ('Basic '
|
|
'dXNlcm5hbWU6cGFzc3dvcmQ=')
|
|
del expected_headers['Content-Type']
|
|
|
|
self.assertEqual(fc_zone_constants.HTTPS, client.protocol)
|
|
session_mock.assert_called_once_with()
|
|
self.assertEqual(requests_mock.Session.return_value, client.session)
|
|
adapter_mock.assert_called_once_with(pool_connections=1,
|
|
pool_maxsize=1)
|
|
session_mock.return_value.mount.assert_called_once_with('https://',
|
|
'adapter')
|
|
|
|
url_mock.assert_called_once_with('/rest/login')
|
|
post_mock.assert_called_once_with(url_mock.return_value)
|
|
|
|
self.assertEqual(expected_headers,
|
|
session_mock.return_value.headers)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test_logout(self, url_mock):
|
|
session = mock.Mock()
|
|
session.post.return_value.status_code = 204
|
|
self.client.session = session
|
|
|
|
self.client._logout()
|
|
|
|
url_mock.assert_called_once_with('/rest/logout')
|
|
session.post.assert_called_once_with(url_mock.return_value)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test_logout_fail(self, url_mock):
|
|
session = mock.Mock()
|
|
session.post.return_value.status_code = 400
|
|
self.client.session = session
|
|
|
|
self.assertRaises(rest_client.exception.BrocadeZoningRestException,
|
|
self.client._logout)
|
|
url_mock.assert_called_once_with('/rest/logout')
|
|
session.post.assert_called_once_with(url_mock.return_value)
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test_get_firmware_version(self, url_mock):
|
|
session = mock.Mock()
|
|
session.get.return_value.status_code = 200
|
|
session.get.return_value.json.return_value = {
|
|
'Response': {
|
|
'fibrechannel-switch': {
|
|
'firmware-version': mock.sentinel.fw_version}}}
|
|
self.client.session = session
|
|
|
|
res = self.client._get_firmware_version()
|
|
|
|
self.assertEqual(mock.sentinel.fw_version, res)
|
|
|
|
url_mock.assert_called_once_with(
|
|
'/rest/running/switch/fibrechannel-switch')
|
|
session.get.assert_called_once_with(url_mock.return_value)
|
|
session.get.return_value.json.assert_called_once_with()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test_get_firmware_version_fail(self, url_mock):
|
|
session = mock.Mock()
|
|
session.get.return_value.status_code = 400
|
|
self.client.session = session
|
|
|
|
self.assertRaises(rest_client.exception.BrocadeZoningRestException,
|
|
self.client._get_firmware_version)
|
|
|
|
url_mock.assert_called_once_with(
|
|
'/rest/running/switch/fibrechannel-switch')
|
|
session.get.assert_called_once_with(url_mock.return_value)
|
|
session.get.return_value.json.assert_not_called()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test__get_name_server(self, url_mock):
|
|
session = mock.Mock()
|
|
session.get.return_value.status_code = 200
|
|
session.get.return_value.json.return_value = {
|
|
'Response': {
|
|
'fibrechannel-name-server': [
|
|
{'port-name': mock.sentinel.port1},
|
|
{'port-name': mock.sentinel.port2}]}}
|
|
self.client.session = session
|
|
|
|
res = self.client._get_name_server()
|
|
|
|
self.assertEqual([mock.sentinel.port1, mock.sentinel.port2], res)
|
|
|
|
url_mock.assert_called_once_with(
|
|
'/rest/running/brocade-name-server/fibrechannel-name-server')
|
|
session.get.assert_called_once_with(url_mock.return_value)
|
|
session.get.return_value.json.assert_called_once_with()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test__get_name_server_fail(self, url_mock):
|
|
session = mock.Mock()
|
|
session.get.return_value.status_code = 400
|
|
self.client.session = session
|
|
|
|
self.assertRaises(rest_client.exception.BrocadeZoningRestException,
|
|
self.client._get_name_server)
|
|
|
|
url_mock.assert_called_once_with(
|
|
'/rest/running/brocade-name-server/fibrechannel-name-server')
|
|
session.get.assert_called_once_with(url_mock.return_value)
|
|
session.get.return_value.json.assert_not_called()
|
|
|
|
@ddt.data(([{'zone-name': 'zone1',
|
|
'member-entry': {'entry-name': 'entry1'}},
|
|
{'zone-name': 'zone2',
|
|
'member-entry': {'entry-name': 'entry2'}}],
|
|
{'zone1': 'entry1', 'zone2': 'entry2'}),
|
|
({'zone-name': 'zone1',
|
|
'member-entry': {'entry-name': 'entry1'}},
|
|
{'zone1': 'entry1'}),
|
|
({}, {}))
|
|
@ddt.unpack
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test_get_effective_zone_set(self, enabled_zone, expected_zones,
|
|
url_mock):
|
|
session = mock.Mock()
|
|
session.get.return_value.status_code = 200
|
|
session.get.return_value.json.return_value = {
|
|
'Response': {
|
|
'effective-configuration': {
|
|
'checksum': mock.sentinel.checksum,
|
|
'cfg-name': 'my-cfg',
|
|
'enabled-zone': enabled_zone}}}
|
|
self.client.session = session
|
|
|
|
res = self.client._get_effective_zone_set()
|
|
|
|
expected = ({'active_zone_config': 'my-cfg' if expected_zones else '',
|
|
'zones': expected_zones},
|
|
mock.sentinel.checksum)
|
|
self.assertEqual(expected, res)
|
|
|
|
url_mock.assert_called_once_with(
|
|
'/rest/running/zoning/effective-configuration')
|
|
session.get.assert_called_once_with(url_mock.return_value)
|
|
session.get.return_value.json.assert_called_once_with()
|
|
|
|
@mock.patch.object(rest_client.BrcdRestFCZoneClient, '_build_url')
|
|
def test_get_effective_zone_set_fail(self, url_mock):
|
|
session = mock.Mock()
|
|
session.get.return_value.status_code = 400
|
|
self.client.session = session
|
|
|
|
self.assertRaises(rest_client.exception.BrocadeZoningRestException,
|
|
self.client._get_effective_zone_set)
|
|
|
|
url_mock.assert_called_once_with(
|
|
'/rest/running/zoning/effective-configuration')
|
|
session.get.assert_called_once_with(url_mock.return_value)
|
|
session.get.return_value.json.assert_not_called()
|