1# (c) Copyright 2016 Brocade Communications Systems Inc. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15# 16 17 18"""Unit tests for brcd fc zone client cli.""" 19 20import mock 21from oslo_concurrency import processutils 22 23from cinder import exception 24from cinder import test 25from cinder.zonemanager.drivers.brocade import (brcd_fc_zone_client_cli 26 as client_cli) 27import cinder.zonemanager.drivers.brocade.fc_zone_constants as zone_constant 28 29 30nsshow = '20:1a:00:05:1e:e8:e3:29' 31switch_data = [' N 011a00;2,3;20:1a:00:05:1e:e8:e3:29;\ 32 20:1a:00:05:1e:e8:e3:29;na', 33 ' Fabric Port Name: 20:1a:00:05:1e:e8:e3:29'] 34cfgactvshow = ['Effective configuration:\n', 35 ' cfg:\tOpenStack_Cfg\t\n', 36 ' zone:\topenstack50060b0000c26604201900051ee8e329\t\n', 37 '\t\t50:06:0b:00:00:c2:66:04\n', 38 '\t\t20:19:00:05:1e:e8:e3:29\n'] 39active_zoneset = { 40 'zones': { 41 'openstack50060b0000c26604201900051ee8e329': 42 ['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:29']}, 43 'active_zone_config': 'OpenStack_Cfg'} 44active_zoneset_multiple_zones = { 45 'zones': { 46 'openstack50060b0000c26604201900051ee8e329': 47 ['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:29'], 48 'openstack50060b0000c26602201900051ee8e327': 49 ['50:06:0b:00:00:c2:66:02', '20:19:00:05:1e:e8:e3:27']}, 50 'active_zone_config': 'OpenStack_Cfg'} 51new_zone_memb_same = { 52 'openstack50060b0000c26604201900051ee8e329': 53 ['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:29']} 54new_zone_memb_not_same = { 55 'openstack50060b0000c26604201900051ee8e330': 56 ['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:30']} 57new_zone = {'openstack10000012345678902001009876543210': 58 ['10:00:00:12:34:56:78:90', '20:01:00:98:76:54:32:10']} 59new_zones = {'openstack10000012345678902001009876543210': 60 ['10:00:00:12:34:56:78:90', '20:01:00:98:76:54:32:10'], 61 'openstack10000011111111112001001111111111': 62 ['10:00:00:11:11:11:11:11', '20:01:00:11:11:11:11:11']} 63zone_names_to_delete = 'openstack50060b0000c26604201900051ee8e329' 64supported_firmware = ['Kernel: 2.6', 'Fabric OS: v7.0.1'] 65unsupported_firmware = ['Fabric OS: v6.2.1'] 66 67 68class TestBrcdFCZoneClientCLI(client_cli.BrcdFCZoneClientCLI, test.TestCase): 69 70 # override some of the functions 71 def __init__(self, *args, **kwargs): 72 test.TestCase.__init__(self, *args, **kwargs) 73 74 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_get_switch_info') 75 def test_get_active_zone_set(self, get_switch_info_mock): 76 cmd_list = [zone_constant.GET_ACTIVE_ZONE_CFG] 77 get_switch_info_mock.return_value = cfgactvshow 78 active_zoneset_returned = self.get_active_zone_set() 79 get_switch_info_mock.assert_called_once_with(cmd_list) 80 self.assertDictEqual(active_zoneset, active_zoneset_returned) 81 82 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 83 def test_get_active_zone_set_ssh_error(self, run_ssh_mock): 84 run_ssh_mock.side_effect = processutils.ProcessExecutionError 85 self.assertRaises(exception.BrocadeZoningCliException, 86 self.get_active_zone_set) 87 88 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'get_active_zone_set') 89 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 90 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_cfg_save') 91 def test_add_zones_new_zone_no_activate(self, cfg_save_mock, 92 apply_zone_change_mock, 93 get_active_zs_mock): 94 get_active_zs_mock.return_value = active_zoneset 95 self.add_zones(new_zones, False, None) 96 self.assertEqual(1, get_active_zs_mock.call_count) 97 self.assertEqual(3, apply_zone_change_mock.call_count) 98 cfg_save_mock.assert_called_once_with() 99 100 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'get_active_zone_set') 101 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 102 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'activate_zoneset') 103 def test_add_zones_new_zone_activate(self, activate_zoneset_mock, 104 apply_zone_change_mock, 105 get_active_zs_mock): 106 get_active_zs_mock.return_value = active_zoneset 107 self.add_zones(new_zone, True, active_zoneset) 108 self.assertEqual(2, apply_zone_change_mock.call_count) 109 activate_zoneset_mock.assert_called_once_with( 110 active_zoneset['active_zone_config']) 111 112 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'get_active_zone_set') 113 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'activate_zoneset') 114 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 115 def test_update_zone_exists_memb_same(self, apply_zone_change_mock, 116 activate_zoneset_mock, 117 get_active_zs_mock): 118 get_active_zs_mock.return_value = active_zoneset 119 self.update_zones(new_zone_memb_same, True, zone_constant.ZONE_ADD, 120 active_zoneset) 121 self.assertEqual(1, apply_zone_change_mock.call_count) 122 123 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'get_active_zone_set') 124 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'activate_zoneset') 125 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 126 def test_update_zone_exists_memb_not_same(self, apply_zone_change_mock, 127 activate_zoneset_mock, 128 get_active_zs_mock): 129 get_active_zs_mock.return_value = active_zoneset 130 self.update_zones(new_zone_memb_not_same, True, 131 zone_constant.ZONE_ADD, active_zoneset) 132 self.assertEqual(1, apply_zone_change_mock.call_count) 133 134 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'get_active_zone_set') 135 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'activate_zoneset') 136 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 137 def test_add_zone_all_exists_memb_not_same(self, apply_zone_change_mock, 138 activate_zoneset_mock, 139 get_active_zs_mock): 140 141 self.add_zones(new_zone_memb_not_same, True, active_zoneset) 142 call_args = apply_zone_change_mock.call_args[0][0] 143 self.assertEqual(0, get_active_zs_mock.call_count) 144 self.assertEqual(2, apply_zone_change_mock.call_count) 145 self.assertIn(zone_constant.CFG_ADD.strip(), call_args) 146 147 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_ssh_execute') 148 def test_activate_zoneset(self, ssh_execute_mock): 149 ssh_execute_mock.return_value = True 150 return_value = self.activate_zoneset('zoneset1') 151 self.assertTrue(return_value) 152 153 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_ssh_execute') 154 def test_deactivate_zoneset(self, ssh_execute_mock): 155 ssh_execute_mock.return_value = True 156 return_value = self.deactivate_zoneset() 157 self.assertTrue(return_value) 158 159 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 160 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_cfg_save') 161 def test_delete_zones_activate_false(self, cfg_save_mock, 162 apply_zone_change_mock): 163 with mock.patch.object(self, '_zone_delete') as zone_delete_mock: 164 self.delete_zones(zone_names_to_delete, False, 165 active_zoneset_multiple_zones) 166 self.assertEqual(1, apply_zone_change_mock.call_count) 167 zone_delete_mock.assert_called_once_with(zone_names_to_delete) 168 cfg_save_mock.assert_called_once_with() 169 170 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 171 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'activate_zoneset') 172 def test_delete_zones_activate_true(self, activate_zs_mock, 173 apply_zone_change_mock): 174 with mock.patch.object(self, '_zone_delete') \ 175 as zone_delete_mock: 176 self.delete_zones(zone_names_to_delete, True, 177 active_zoneset_multiple_zones) 178 self.assertEqual(1, apply_zone_change_mock.call_count) 179 zone_delete_mock.assert_called_once_with(zone_names_to_delete) 180 activate_zs_mock.assert_called_once_with( 181 active_zoneset['active_zone_config']) 182 183 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_get_switch_info') 184 def test_get_nameserver_info(self, get_switch_info_mock): 185 ns_info_list_expected = ['20:1a:00:05:1e:e8:e3:29'] 186 get_switch_info_mock.return_value = (switch_data) 187 ns_info_list = self.get_nameserver_info() 188 self.assertEqual(ns_info_list_expected, ns_info_list) 189 190 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 191 def test_get_nameserver_info_ssh_error(self, run_ssh_mock): 192 run_ssh_mock.side_effect = processutils.ProcessExecutionError 193 self.assertRaises(exception.BrocadeZoningCliException, 194 self.get_nameserver_info) 195 196 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_ssh_execute') 197 def test__cfg_save(self, ssh_execute_mock): 198 cmd_list = [zone_constant.CFG_SAVE] 199 self._cfg_save() 200 ssh_execute_mock.assert_called_once_with(cmd_list, True, 1) 201 202 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 203 def test__zone_delete(self, apply_zone_change_mock): 204 zone_name = 'testzone' 205 cmd_list = ['zonedelete', '"testzone"'] 206 self._zone_delete(zone_name) 207 apply_zone_change_mock.assert_called_once_with(cmd_list) 208 209 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, 'apply_zone_change') 210 def test__cfg_trans_abort(self, apply_zone_change_mock): 211 cmd_list = [zone_constant.CFG_ZONE_TRANS_ABORT] 212 with mock.patch.object(self, '_is_trans_abortable') \ 213 as is_trans_abortable_mock: 214 is_trans_abortable_mock.return_value = True 215 self._cfg_trans_abort() 216 is_trans_abortable_mock.assert_called_once_with() 217 apply_zone_change_mock.assert_called_once_with(cmd_list) 218 219 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 220 def test__is_trans_abortable_true(self, run_ssh_mock): 221 cmd_list = [zone_constant.CFG_SHOW_TRANS] 222 run_ssh_mock.return_value = (Stream(zone_constant.TRANS_ABORTABLE), 223 None) 224 data = self._is_trans_abortable() 225 self.assertTrue(data) 226 run_ssh_mock.assert_called_once_with(cmd_list, True, 1) 227 228 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 229 def test__is_trans_abortable_ssh_error(self, run_ssh_mock): 230 run_ssh_mock.return_value = (Stream(), Stream()) 231 self.assertRaises(exception.BrocadeZoningCliException, 232 self._is_trans_abortable) 233 234 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 235 def test__is_trans_abortable_false(self, run_ssh_mock): 236 cmd_list = [zone_constant.CFG_SHOW_TRANS] 237 cfgtransshow = 'There is no outstanding zoning transaction' 238 run_ssh_mock.return_value = (Stream(cfgtransshow), None) 239 data = self._is_trans_abortable() 240 self.assertFalse(data) 241 run_ssh_mock.assert_called_once_with(cmd_list, True, 1) 242 243 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 244 def test_apply_zone_change(self, run_ssh_mock): 245 cmd_list = [zone_constant.CFG_SAVE] 246 run_ssh_mock.return_value = (None, None) 247 self.apply_zone_change(cmd_list) 248 run_ssh_mock.assert_called_once_with(cmd_list, True, 1) 249 250 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_run_ssh') 251 def test__get_switch_info(self, run_ssh_mock): 252 cmd_list = [zone_constant.NS_SHOW] 253 nsshow_list = [nsshow] 254 run_ssh_mock.return_value = (Stream(nsshow), Stream()) 255 switch_data = self._get_switch_info(cmd_list) 256 self.assertEqual(nsshow_list, switch_data) 257 run_ssh_mock.assert_called_once_with(cmd_list, True, 1) 258 259 def test__parse_ns_output(self): 260 invalid_switch_data = [' N 011a00;20:1a:00:05:1e:e8:e3:29'] 261 expected_wwn_list = ['20:1a:00:05:1e:e8:e3:29'] 262 return_wwn_list = self._parse_ns_output(switch_data) 263 self.assertEqual(expected_wwn_list, return_wwn_list) 264 self.assertRaises(exception.InvalidParameterValue, 265 self._parse_ns_output, invalid_switch_data) 266 267 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_execute_shell_cmd') 268 def test_is_supported_firmware(self, exec_shell_cmd_mock): 269 exec_shell_cmd_mock.return_value = (supported_firmware, None) 270 self.assertTrue(self.is_supported_firmware()) 271 272 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_execute_shell_cmd') 273 def test_is_supported_firmware_invalid(self, exec_shell_cmd_mock): 274 exec_shell_cmd_mock.return_value = (unsupported_firmware, None) 275 self.assertFalse(self.is_supported_firmware()) 276 277 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_execute_shell_cmd') 278 def test_is_supported_firmware_no_ssh_response(self, exec_shell_cmd_mock): 279 exec_shell_cmd_mock.return_value = (None, Stream()) 280 self.assertFalse(self.is_supported_firmware()) 281 282 @mock.patch.object(client_cli.BrcdFCZoneClientCLI, '_execute_shell_cmd') 283 def test_is_supported_firmware_ssh_error(self, exec_shell_cmd_mock): 284 exec_shell_cmd_mock.side_effect = processutils.ProcessExecutionError 285 self.assertRaises(exception.BrocadeZoningCliException, 286 self.is_supported_firmware) 287 288 289class Channel(object): 290 def recv_exit_status(self): 291 return 0 292 293 294class Stream(object): 295 def __init__(self, buffer=''): 296 self.buffer = buffer 297 self.channel = Channel() 298 299 def readlines(self): 300 return self.buffer 301 302 def splitlines(self): 303 return self.buffer.splitlines() 304 305 def close(self): 306 pass 307 308 def flush(self): 309 self.buffer = '' 310