Remove Cisco FC Zone Manager Driver
After many attempts to get a Cisco CI running to validate the Cisco FCZM driver over a few releases there is still no official third party CI reporting. This was covered for some time due to the support of it being covered by another CI, but that is no longer the case. We can't include drivers that do not have test coverage and no report of whether it is working as expected. Until there is test coverage, we will need to remove this driver due to lack of CI. DocImpact: Remove Cisci FCZM documentation Change-Id: Idb7dfea74ce248d8f1209c55b62412be4f2e16e9
This commit is contained in:
parent
e88f0a486a
commit
0f5c5d8cf7
@ -168,10 +168,6 @@ from cinder.zonemanager.drivers.brocade import brcd_fabric_opts as \
|
||||
cinder_zonemanager_drivers_brocade_brcdfabricopts
|
||||
from cinder.zonemanager.drivers.brocade import brcd_fc_zone_driver as \
|
||||
cinder_zonemanager_drivers_brocade_brcdfczonedriver
|
||||
from cinder.zonemanager.drivers.cisco import cisco_fabric_opts as \
|
||||
cinder_zonemanager_drivers_cisco_ciscofabricopts
|
||||
from cinder.zonemanager.drivers.cisco import cisco_fc_zone_driver as \
|
||||
cinder_zonemanager_drivers_cisco_ciscofczonedriver
|
||||
from cinder.zonemanager import fc_zone_manager as \
|
||||
cinder_zonemanager_fczonemanager
|
||||
|
||||
@ -182,7 +178,6 @@ def list_opts():
|
||||
itertools.chain(
|
||||
cinder_zonemanager_fczonemanager.zone_manager_opts,
|
||||
cinder_zonemanager_drivers_brocade_brcdfczonedriver.brcd_opts,
|
||||
cinder_zonemanager_drivers_cisco_ciscofczonedriver.cisco_opts,
|
||||
)),
|
||||
('KEYMGR',
|
||||
itertools.chain(
|
||||
@ -342,11 +337,6 @@ def list_opts():
|
||||
itertools.chain(
|
||||
cinder_service.profiler_opts,
|
||||
)),
|
||||
('CISCO_FABRIC_EXAMPLE',
|
||||
itertools.chain(
|
||||
cinder_zonemanager_drivers_cisco_ciscofabricopts.
|
||||
cisco_zone_opts,
|
||||
)),
|
||||
('BRCD_FABRIC_EXAMPLE',
|
||||
itertools.chain(
|
||||
cinder_zonemanager_drivers_brocade_brcdfabricopts.
|
||||
|
@ -1,149 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""Unit tests for Cisco fc san lookup service."""
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.volume import configuration as conf
|
||||
import cinder.zonemanager.drivers.cisco.cisco_fc_san_lookup_service \
|
||||
as cisco_lookup
|
||||
import cinder.zonemanager.drivers.cisco.fc_zone_constants as ZoneConstant
|
||||
from cinder.zonemanager import utils as zm_utils
|
||||
|
||||
nsshow = '20:1a:00:05:1e:e8:e3:29'
|
||||
switch_data = ['VSAN 304\n',
|
||||
'------------------------------------------------------\n',
|
||||
'FCID TYPE PWWN (VENDOR) \n',
|
||||
'------------------------------------------------------\n',
|
||||
'0x030001 N 20:1a:00:05:1e:e8:e3:29 (Cisco) ipfc\n',
|
||||
'0x030101 NL 10:00:00:00:77:99:60:2c (Interphase)\n',
|
||||
'0x030200 N 10:00:00:49:c9:28:c7:01\n']
|
||||
|
||||
nsshow_data = ['10:00:8c:7c:ff:52:3b:01', '20:24:00:02:ac:00:0a:50']
|
||||
|
||||
_device_map_to_verify = {
|
||||
'304': {
|
||||
'initiator_port_wwn_list': ['10008c7cff523b01'],
|
||||
'target_port_wwn_list': ['20240002ac000a50']}}
|
||||
|
||||
|
||||
class TestCiscoFCSanLookupService(cisco_lookup.CiscoFCSanLookupService,
|
||||
test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCiscoFCSanLookupService, self).setUp()
|
||||
self.configuration = conf.Configuration(None)
|
||||
self.configuration.set_default('fc_fabric_names', 'CISCO_FAB_2',
|
||||
'fc-zone-manager')
|
||||
self.configuration.fc_fabric_names = 'CISCO_FAB_2'
|
||||
self.create_configuration()
|
||||
self.fabric_vsan = '304'
|
||||
|
||||
# override some of the functions
|
||||
def __init__(self, *args, **kwargs):
|
||||
test.TestCase.__init__(self, *args, **kwargs)
|
||||
|
||||
def create_configuration(self):
|
||||
fc_fabric_opts = []
|
||||
fc_fabric_opts.append(cfg.StrOpt('cisco_fc_fabric_address',
|
||||
default='172.24.173.142', help=''))
|
||||
fc_fabric_opts.append(cfg.StrOpt('cisco_fc_fabric_user',
|
||||
default='admin', help=''))
|
||||
fc_fabric_opts.append(cfg.StrOpt('cisco_fc_fabric_password',
|
||||
default='admin1234', help='',
|
||||
secret=True))
|
||||
fc_fabric_opts.append(cfg.PortOpt('cisco_fc_fabric_port',
|
||||
default=22, help=''))
|
||||
fc_fabric_opts.append(cfg.StrOpt('cisco_zoning_vsan',
|
||||
default='304', help=''))
|
||||
config = conf.Configuration(fc_fabric_opts, 'CISCO_FAB_2')
|
||||
self.fabric_configs = {'CISCO_FAB_2': config}
|
||||
|
||||
@mock.patch.object(cisco_lookup.CiscoFCSanLookupService,
|
||||
'get_nameserver_info')
|
||||
def test_get_device_mapping_from_network(self, get_nameserver_info_mock):
|
||||
initiator_list = ['10008c7cff523b01']
|
||||
target_list = ['20240002ac000a50', '20240002ac000a40']
|
||||
get_nameserver_info_mock.return_value = (nsshow_data)
|
||||
device_map = self.get_device_mapping_from_network(
|
||||
initiator_list, target_list)
|
||||
self.assertDictMatch(_device_map_to_verify, device_map)
|
||||
|
||||
@mock.patch.object(cisco_lookup.CiscoFCSanLookupService,
|
||||
'_get_switch_info')
|
||||
def test_get_nameserver_info(self, get_switch_data_mock):
|
||||
ns_info_list = []
|
||||
ns_info_list_expected = ['20:1a:00:05:1e:e8:e3:29',
|
||||
'10:00:00:49:c9:28:c7:01']
|
||||
get_switch_data_mock.return_value = (switch_data)
|
||||
ns_info_list = self.get_nameserver_info('304')
|
||||
self.assertEqual(ns_info_list_expected, ns_info_list)
|
||||
|
||||
def test_parse_ns_output(self):
|
||||
invalid_switch_data = [' N 011a00;20:1a:00:05:1e:e8:e3:29']
|
||||
return_wwn_list = []
|
||||
expected_wwn_list = ['20:1a:00:05:1e:e8:e3:29',
|
||||
'10:00:00:49:c9:28:c7:01']
|
||||
return_wwn_list = self._parse_ns_output(switch_data)
|
||||
self.assertEqual(expected_wwn_list, return_wwn_list)
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self._parse_ns_output, invalid_switch_data)
|
||||
|
||||
def test_get_formatted_wwn(self):
|
||||
wwn_list = ['10008c7cff523b01']
|
||||
return_wwn_list = []
|
||||
expected_wwn_list = ['10:00:8c:7c:ff:52:3b:01']
|
||||
return_wwn_list.append(zm_utils.get_formatted_wwn(wwn_list[0]))
|
||||
self.assertEqual(expected_wwn_list, return_wwn_list)
|
||||
|
||||
@mock.patch.object(cisco_lookup.CiscoFCSanLookupService,
|
||||
'_run_ssh')
|
||||
def test__get_switch_info(self, run_ssh_mock):
|
||||
cmd_list = [ZoneConstant.FCNS_SHOW, self.fabric_vsan,
|
||||
' | no-more']
|
||||
nsshow_list = [nsshow]
|
||||
run_ssh_mock.return_value = (Stream(nsshow), Stream())
|
||||
switch_data = self._get_switch_info(cmd_list)
|
||||
self.assertEqual(nsshow_list, switch_data)
|
||||
run_ssh_mock.assert_called_once_with(cmd_list, True, 1)
|
||||
|
||||
|
||||
class Channel(object):
|
||||
def recv_exit_status(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Stream(object):
|
||||
def __init__(self, buffer=''):
|
||||
self.buffer = buffer
|
||||
self.channel = Channel()
|
||||
|
||||
def readlines(self):
|
||||
return self.buffer
|
||||
|
||||
def splitlines(self):
|
||||
return self.buffer.splitlines()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def flush(self):
|
||||
self.buffer = ''
|
@ -1,303 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""Unit tests for Cisco fc zone client cli."""
|
||||
|
||||
import time
|
||||
|
||||
import mock
|
||||
from oslo_concurrency import processutils
|
||||
from six.moves import range
|
||||
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.zonemanager.drivers.cisco \
|
||||
import cisco_fc_zone_client_cli as cli
|
||||
import cinder.zonemanager.drivers.cisco.fc_zone_constants as ZoneConstant
|
||||
|
||||
nsshow = '20:1a:00:05:1e:e8:e3:29'
|
||||
switch_data = ['VSAN 303\n',
|
||||
'----------------------------------------------------------\n',
|
||||
'FCID TYPE PWWN (VENDOR) FC4-TYPE:FEATURE\n',
|
||||
'----------------------------------------------------------\n',
|
||||
'0x030001 N 20:1a:00:05:1e:e8:e3:29 (Cisco) ipfc\n',
|
||||
'0x030101 NL 10:00:00:00:77:99:60:2c (Interphase)\n',
|
||||
'0x030200 NL 10:00:00:49:c9:28:c7:01\n']
|
||||
|
||||
cfgactv = ['zoneset name OpenStack_Cfg vsan 303\n',
|
||||
'zone name openstack50060b0000c26604201900051ee8e329 vsan 303\n',
|
||||
'pwwn 50:06:0b:00:00:c2:66:04\n',
|
||||
'pwwn 20:19:00:05:1e:e8:e3:29\n']
|
||||
|
||||
active_zoneset = {
|
||||
'zones': {
|
||||
'openstack50060b0000c26604201900051ee8e329':
|
||||
['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:29']},
|
||||
'active_zone_config': 'OpenStack_Cfg'}
|
||||
|
||||
zoning_status_data_basic = [
|
||||
'VSAN: 303 default-zone: deny distribute: active only Interop: default\n',
|
||||
' mode: basic merge-control: allow\n',
|
||||
' session: none\n',
|
||||
' hard-zoning: enabled broadcast: unsupported\n',
|
||||
' smart-zoning: disabled\n',
|
||||
' rscn-format: fabric-address\n',
|
||||
'Default zone:\n',
|
||||
' qos: none broadcast: unsupported ronly: unsupported\n',
|
||||
'Full Zoning Database :\n',
|
||||
' DB size: 220 bytes\n',
|
||||
' Zonesets:2 Zones:2 Aliases: 0\n',
|
||||
'Active Zoning Database :\n',
|
||||
' DB size: 80 bytes\n',
|
||||
' Name: test-zs-test Zonesets:1 Zones:1\n',
|
||||
'Status:\n']
|
||||
|
||||
zoning_status_basic = {'mode': 'basic', 'session': 'none'}
|
||||
|
||||
zoning_status_data_enhanced_nosess = [
|
||||
'VSAN: 303 default-zone: deny distribute: active only Interop: default\n',
|
||||
' mode: enhanced merge-control: allow\n',
|
||||
' session: none\n',
|
||||
' hard-zoning: enabled broadcast: unsupported\n',
|
||||
' smart-zoning: disabled\n',
|
||||
' rscn-format: fabric-address\n',
|
||||
'Default zone:\n',
|
||||
' qos: none broadcast: unsupported ronly: unsupported\n',
|
||||
'Full Zoning Database :\n',
|
||||
' DB size: 220 bytes\n',
|
||||
' Zonesets:2 Zones:2 Aliases: 0\n',
|
||||
'Active Zoning Database :\n',
|
||||
' DB size: 80 bytes\n',
|
||||
' Name: test-zs-test Zonesets:1 Zones:1\n',
|
||||
'Status:\n']
|
||||
|
||||
zoning_status_enhanced_nosess = {'mode': 'enhanced', 'session': 'none'}
|
||||
|
||||
zoning_status_data_enhanced_sess = [
|
||||
'VSAN: 303 default-zone: deny distribute: active only Interop: default\n',
|
||||
' mode: enhanced merge-control: allow\n',
|
||||
' session: otherthannone\n',
|
||||
' hard-zoning: enabled broadcast: unsupported\n',
|
||||
' smart-zoning: disabled\n',
|
||||
' rscn-format: fabric-address\n',
|
||||
'Default zone:\n',
|
||||
' qos: none broadcast: unsupported ronly: unsupported\n',
|
||||
'Full Zoning Database :\n',
|
||||
' DB size: 220 bytes\n',
|
||||
' Zonesets:2 Zones:2 Aliases: 0\n',
|
||||
'Active Zoning Database :\n',
|
||||
' DB size: 80 bytes\n',
|
||||
' Name: test-zs-test Zonesets:1 Zones:1\n',
|
||||
'Status:\n']
|
||||
|
||||
zoning_status_enhanced_sess = {'mode': 'enhanced', 'session': 'otherthannone'}
|
||||
|
||||
active_zoneset_multiple_zones = {
|
||||
'zones': {
|
||||
'openstack50060b0000c26604201900051ee8e329':
|
||||
['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:29'],
|
||||
'openstack10000012345678902001009876543210':
|
||||
['50:06:0b:00:00:c2:66:02', '20:19:00:05:1e:e8:e3:27']},
|
||||
'active_zone_config': 'OpenStack_Cfg'}
|
||||
|
||||
new_zone = {'openstack10000012345678902001009876543210':
|
||||
['10:00:00:12:34:56:78:90', '20:01:00:98:76:54:32:10']}
|
||||
|
||||
new_zones = {'openstack10000012345678902001009876543210':
|
||||
['10:00:00:12:34:56:78:90', '20:01:00:98:76:54:32:10'],
|
||||
'openstack10000011111111112001001111111111':
|
||||
['10:00:00:11:11:11:11:11', '20:01:00:11:11:11:11:11']}
|
||||
|
||||
zone_names_to_delete = 'openstack50060b0000c26604201900051ee8e329'
|
||||
|
||||
|
||||
class TestCiscoFCZoneClientCLI(cli.CiscoFCZoneClientCLI, test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCiscoFCZoneClientCLI, self).setUp()
|
||||
self.fabric_vsan = '303'
|
||||
|
||||
# override some of the functions
|
||||
def __init__(self, *args, **kwargs):
|
||||
test.TestCase.__init__(self, *args, **kwargs)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_get_switch_info')
|
||||
def test_get_active_zone_set(self, get_switch_info_mock):
|
||||
cmd_list = [ZoneConstant.GET_ACTIVE_ZONE_CFG, self.fabric_vsan,
|
||||
' | no-more']
|
||||
get_switch_info_mock.return_value = cfgactv
|
||||
active_zoneset_returned = self.get_active_zone_set()
|
||||
get_switch_info_mock.assert_called_once_with(cmd_list)
|
||||
self.assertDictMatch(active_zoneset, active_zoneset_returned)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_run_ssh')
|
||||
def test_get_active_zone_set_ssh_error(self, run_ssh_mock):
|
||||
run_ssh_mock.side_effect = processutils.ProcessExecutionError
|
||||
self.assertRaises(exception.CiscoZoningCliException,
|
||||
self.get_active_zone_set)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_get_switch_info')
|
||||
def test_get_zoning_status_basic(self, get_zoning_status_mock):
|
||||
cmd_list = [ZoneConstant.GET_ZONE_STATUS, self.fabric_vsan]
|
||||
get_zoning_status_mock.return_value = zoning_status_data_basic
|
||||
zoning_status_returned = self.get_zoning_status()
|
||||
get_zoning_status_mock.assert_called_once_with(cmd_list)
|
||||
self.assertDictMatch(zoning_status_basic, zoning_status_returned)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_get_switch_info')
|
||||
def test_get_zoning_status_enhanced_nosess(self, get_zoning_status_mock):
|
||||
cmd_list = [ZoneConstant.GET_ZONE_STATUS, self.fabric_vsan]
|
||||
get_zoning_status_mock.return_value =\
|
||||
zoning_status_data_enhanced_nosess
|
||||
zoning_status_returned = self.get_zoning_status()
|
||||
get_zoning_status_mock.assert_called_once_with(cmd_list)
|
||||
self.assertDictMatch(zoning_status_enhanced_nosess,
|
||||
zoning_status_returned)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_get_switch_info')
|
||||
def test_get_zoning_status_enhanced_sess(self, get_zoning_status_mock):
|
||||
cmd_list = [ZoneConstant.GET_ZONE_STATUS, self.fabric_vsan]
|
||||
get_zoning_status_mock.return_value = zoning_status_data_enhanced_sess
|
||||
zoning_status_returned = self.get_zoning_status()
|
||||
get_zoning_status_mock.assert_called_once_with(cmd_list)
|
||||
self.assertDictMatch(zoning_status_enhanced_sess,
|
||||
zoning_status_returned)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_get_switch_info')
|
||||
def test_get_nameserver_info(self, get_switch_info_mock):
|
||||
ns_info_list = []
|
||||
ns_info_list_expected = ['20:1a:00:05:1e:e8:e3:29']
|
||||
get_switch_info_mock.return_value = (switch_data)
|
||||
ns_info_list = self.get_nameserver_info()
|
||||
self.assertEqual(ns_info_list_expected, ns_info_list)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_run_ssh')
|
||||
def test_get_nameserver_info_ssh_error(self, run_ssh_mock):
|
||||
run_ssh_mock.side_effect = processutils.ProcessExecutionError
|
||||
self.assertRaises(exception.CiscoZoningCliException,
|
||||
self.get_nameserver_info)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_run_ssh')
|
||||
def test__cfg_save(self, run_ssh_mock):
|
||||
cmd_list = ['copy', 'running-config', 'startup-config']
|
||||
self._cfg_save()
|
||||
run_ssh_mock.assert_called_once_with(cmd_list, True)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_run_ssh')
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test__cfg_save_with_retry(self, mock_sleep, run_ssh_mock):
|
||||
cmd_list = ['copy', 'running-config', 'startup-config']
|
||||
run_ssh_mock.side_effect = [
|
||||
processutils.ProcessExecutionError,
|
||||
('', None)
|
||||
]
|
||||
|
||||
self._cfg_save()
|
||||
|
||||
self.assertEqual(2, run_ssh_mock.call_count)
|
||||
run_ssh_mock.assert_has_calls([
|
||||
mock.call(cmd_list, True),
|
||||
mock.call(cmd_list, True)
|
||||
])
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_run_ssh')
|
||||
@mock.patch.object(time, 'sleep')
|
||||
def test__cfg_save_with_error(self, mock_sleep, run_ssh_mock):
|
||||
cmd_list = ['copy', 'running-config', 'startup-config']
|
||||
run_ssh_mock.side_effect = processutils.ProcessExecutionError
|
||||
|
||||
self.assertRaises(processutils.ProcessExecutionError, self._cfg_save)
|
||||
|
||||
expected_num_calls = 5
|
||||
expected_calls = []
|
||||
for i in range(expected_num_calls):
|
||||
expected_calls.append(mock.call(cmd_list, True))
|
||||
|
||||
self.assertEqual(expected_num_calls, run_ssh_mock.call_count)
|
||||
run_ssh_mock.assert_has_calls(expected_calls)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_run_ssh')
|
||||
def test__get_switch_info(self, run_ssh_mock):
|
||||
cmd_list = [ZoneConstant.FCNS_SHOW, self.fabric_vsan]
|
||||
nsshow_list = [nsshow]
|
||||
run_ssh_mock.return_value = (Stream(nsshow), Stream())
|
||||
switch_data = self._get_switch_info(cmd_list)
|
||||
self.assertEqual(nsshow_list, switch_data)
|
||||
run_ssh_mock.assert_called_once_with(cmd_list, True)
|
||||
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_ssh_execute')
|
||||
@mock.patch.object(cli.CiscoFCZoneClientCLI, '_cfg_save')
|
||||
def test__add_zones_with_update(self, ssh_execute_mock, cfg_save_mock):
|
||||
self.add_zones(new_zone, False, self.fabric_vsan,
|
||||
active_zoneset_multiple_zones,
|
||||
zoning_status_basic)
|
||||
self.assertEqual(2, ssh_execute_mock.call_count)
|
||||
self.assertEqual(2, cfg_save_mock.call_count)
|
||||
|
||||
def test__parse_ns_output(self):
|
||||
return_wwn_list = []
|
||||
expected_wwn_list = ['20:1a:00:05:1e:e8:e3:29']
|
||||
return_wwn_list = self._parse_ns_output(switch_data)
|
||||
self.assertEqual(expected_wwn_list, return_wwn_list)
|
||||
|
||||
|
||||
class TestCiscoFCZoneClientCLISSH(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCiscoFCZoneClientCLISSH, self).setUp()
|
||||
self.client = cli.CiscoFCZoneClientCLI(None, None, None, None, None)
|
||||
self.client.sshpool = mock.MagicMock()
|
||||
self.mock_ssh = self.client.sshpool.item().__enter__()
|
||||
|
||||
@mock.patch('oslo_concurrency.processutils.ssh_execute')
|
||||
def test__run_ssh(self, mock_execute):
|
||||
mock_execute.return_value = 'ssh output'
|
||||
ret = self.client._run_ssh(['cat', 'foo'])
|
||||
self.assertEqual('ssh output', ret)
|
||||
mock_execute.assert_called_once_with(self.mock_ssh,
|
||||
'cat foo',
|
||||
check_exit_code=True)
|
||||
|
||||
@mock.patch('oslo_concurrency.processutils.ssh_execute')
|
||||
def test__run_ssh_with_error(self, mock_execute):
|
||||
mock_execute.side_effect = processutils.ProcessExecutionError()
|
||||
self.assertRaises(processutils.ProcessExecutionError,
|
||||
self.client._run_ssh,
|
||||
['cat', 'foo'])
|
||||
|
||||
|
||||
class Channel(object):
|
||||
def recv_exit_status(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Stream(object):
|
||||
def __init__(self, buffer=''):
|
||||
self.buffer = buffer
|
||||
self.channel = Channel()
|
||||
|
||||
def readlines(self):
|
||||
return self.buffer
|
||||
|
||||
def splitlines(self):
|
||||
return self.buffer.splitlines()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def flush(self):
|
||||
self.buffer = ''
|
@ -1,208 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""Unit tests for Cisco FC zone driver."""
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.volume import configuration as conf
|
||||
|
||||
_active_cfg_before_add = {}
|
||||
_active_cfg_before_delete = {
|
||||
'zones': {
|
||||
'openstack10008c7cff523b0120240002ac000a50': (
|
||||
['10:00:8c:7c:ff:52:3b:01',
|
||||
'20:24:00:02:ac:00:0a:50'])},
|
||||
'active_zone_config': 'cfg1'}
|
||||
_activate = True
|
||||
_zone_name = 'openstack10008c7cff523b0120240002ac000a50'
|
||||
_target_ns_map = {'100000051e55a100': ['20240002ac000a50']}
|
||||
_zoning_status = {'mode': 'basis', 'session': 'none'}
|
||||
_initiator_ns_map = {'100000051e55a100': ['10008c7cff523b01']}
|
||||
_zone_map_to_add = {'openstack10008c7cff523b0120240002ac000a50': (
|
||||
['10:00:8c:7c:ff:52:3b:01', '20:24:00:02:ac:00:0a:50'])}
|
||||
|
||||
_initiator_target_map = {'10008c7cff523b01': ['20240002ac000a50']}
|
||||
_device_map_to_verify = {
|
||||
'304': {
|
||||
'initiator_port_wwn_list': [
|
||||
'10008c7cff523b01'], 'target_port_wwn_list': ['20240002ac000a50']}}
|
||||
_fabric_wwn = '304'
|
||||
|
||||
|
||||
class CiscoFcZoneDriverBaseTest(object):
|
||||
|
||||
def setup_config(self, is_normal, mode):
|
||||
fc_test_opts = [
|
||||
cfg.StrOpt('fc_fabric_address_CISCO_FAB_1', default='10.24.48.213',
|
||||
help='FC Fabric names'),
|
||||
]
|
||||
configuration = conf.Configuration(fc_test_opts)
|
||||
# fill up config
|
||||
configuration.zoning_mode = 'fabric'
|
||||
configuration.zone_driver = ('cinder.tests.unit.zonemanager.'
|
||||
'test_cisco_fc_zone_driver.'
|
||||
'FakeCiscoFCZoneDriver')
|
||||
configuration.cisco_sb_connector = ('cinder.tests.unit.zonemanager.'
|
||||
'test_cisco_fc_zone_driver'
|
||||
'.FakeCiscoFCZoneClientCLI')
|
||||
configuration.zoning_policy = 'initiator-target'
|
||||
configuration.zone_activate = True
|
||||
configuration.zone_name_prefix = 'openstack'
|
||||
configuration.fc_san_lookup_service = ('cinder.tests.unit.zonemanager.'
|
||||
'test_cisco_fc_zone_driver.'
|
||||
'FakeCiscoFCSanLookupService')
|
||||
|
||||
configuration.fc_fabric_names = 'CISCO_FAB_1'
|
||||
configuration.fc_fabric_address_CISCO_FAB_1 = '172.21.60.220'
|
||||
if (is_normal):
|
||||
configuration.fc_fabric_user_CISCO_FAB_1 = 'admin'
|
||||
else:
|
||||
configuration.fc_fabric_user_CISCO_FAB_1 = 'invaliduser'
|
||||
configuration.fc_fabric_password_CISCO_FAB_1 = 'admin1234'
|
||||
|
||||
if (mode == 1):
|
||||
configuration.zoning_policy_CISCO_FAB_1 = 'initiator-target'
|
||||
elif (mode == 2):
|
||||
configuration.zoning_policy_CISCO_FAB_1 = 'initiator'
|
||||
else:
|
||||
configuration.zoning_policy_CISCO_FAB_1 = 'initiator-target'
|
||||
configuration.zone_activate_CISCO_FAB_1 = True
|
||||
configuration.zone_name_prefix_CISCO_FAB_1 = 'openstack'
|
||||
configuration.zoning_vsan_CISCO_FAB_1 = '304'
|
||||
return configuration
|
||||
|
||||
|
||||
class TestCiscoFcZoneDriver(CiscoFcZoneDriverBaseTest, test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCiscoFcZoneDriver, self).setUp()
|
||||
# setup config for normal flow
|
||||
self.setup_driver(self.setup_config(True, 1))
|
||||
GlobalVars._zone_state = []
|
||||
|
||||
def setup_driver(self, config):
|
||||
self.driver = importutils.import_object(
|
||||
'cinder.zonemanager.drivers.cisco.cisco_fc_zone_driver'
|
||||
'.CiscoFCZoneDriver', configuration=config)
|
||||
|
||||
def fake_get_active_zone_set(self, fabric_ip, fabric_user, fabric_pwd,
|
||||
zoning_vsan):
|
||||
return GlobalVars._active_cfg
|
||||
|
||||
def fake_get_san_context(self, target_wwn_list):
|
||||
fabric_map = {}
|
||||
return fabric_map
|
||||
|
||||
def test_delete_connection(self):
|
||||
GlobalVars._is_normal_test = True
|
||||
GlobalVars._active_cfg = _active_cfg_before_delete
|
||||
self.driver.delete_connection(
|
||||
'CISCO_FAB_1', _initiator_target_map)
|
||||
self.assertFalse(_zone_name in GlobalVars._zone_state)
|
||||
|
||||
def test_delete_connection_for_initiator_mode(self):
|
||||
GlobalVars._is_normal_test = True
|
||||
GlobalVars._active_cfg = _active_cfg_before_delete
|
||||
self.setup_driver(self.setup_config(True, 2))
|
||||
self.driver.delete_connection(
|
||||
'CISCO_FAB_1', _initiator_target_map)
|
||||
self.assertFalse(_zone_name in GlobalVars._zone_state)
|
||||
|
||||
def test_add_connection_for_invalid_fabric(self):
|
||||
"""Test abnormal flows."""
|
||||
GlobalVars._is_normal_test = True
|
||||
GlobalVars._active_cfg = _active_cfg_before_add
|
||||
GlobalVars._is_normal_test = False
|
||||
self.setup_driver(self.setup_config(False, 1))
|
||||
self.assertRaises(exception.FCZoneDriverException,
|
||||
self.driver.add_connection,
|
||||
'CISCO_FAB_1',
|
||||
_initiator_target_map)
|
||||
|
||||
def test_delete_connection_for_invalid_fabric(self):
|
||||
GlobalVars._active_cfg = _active_cfg_before_delete
|
||||
GlobalVars._is_normal_test = False
|
||||
self.setup_driver(self.setup_config(False, 1))
|
||||
self.assertRaises(exception.FCZoneDriverException,
|
||||
self.driver.delete_connection,
|
||||
'CISCO_FAB_1',
|
||||
_initiator_target_map)
|
||||
|
||||
|
||||
class FakeCiscoFCZoneClientCLI(object):
|
||||
def __init__(self, ipaddress, username, password, port, vsan):
|
||||
if not GlobalVars._is_normal_test:
|
||||
raise processutils.ProcessExecutionError(
|
||||
"Unable to connect to fabric")
|
||||
|
||||
def get_active_zone_set(self):
|
||||
return GlobalVars._active_cfg
|
||||
|
||||
def add_zones(self, zones, isActivate):
|
||||
GlobalVars._zone_state.extend(zones.keys())
|
||||
|
||||
def delete_zones(self, zone_names, isActivate):
|
||||
zone_list = zone_names.split(';')
|
||||
GlobalVars._zone_state = [
|
||||
x for x in GlobalVars._zone_state if x not in zone_list]
|
||||
|
||||
def get_nameserver_info(self):
|
||||
return _target_ns_map
|
||||
|
||||
def get_zoning_status(self):
|
||||
return _zoning_status
|
||||
|
||||
def close_connection(self):
|
||||
pass
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
|
||||
class FakeCiscoFCSanLookupService(object):
|
||||
def get_device_mapping_from_network(self,
|
||||
initiator_wwn_list,
|
||||
target_wwn_list):
|
||||
device_map = {}
|
||||
initiators = []
|
||||
targets = []
|
||||
for i in initiator_wwn_list:
|
||||
if (i in _initiator_ns_map[_fabric_wwn]):
|
||||
initiators.append(i)
|
||||
for t in target_wwn_list:
|
||||
if (t in _target_ns_map[_fabric_wwn]):
|
||||
targets.append(t)
|
||||
device_map[_fabric_wwn] = {
|
||||
'initiator_port_wwn_list': initiators,
|
||||
'target_port_wwn_list': targets}
|
||||
return device_map
|
||||
|
||||
|
||||
class GlobalVars(object):
|
||||
global _active_cfg
|
||||
_active_cfg = {}
|
||||
global _zone_state
|
||||
_zone_state = list()
|
||||
global _is_normal_test
|
||||
_is_normal_test = True
|
||||
global _zoning_status
|
||||
_zoning_status = {}
|
@ -1,96 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""Unit tests for Cisco FC san lookup service."""
|
||||
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.zonemanager import fc_san_lookup_service as san_service
|
||||
|
||||
_target_ns_map = {'100000051e55a100': ['20240002ac000a50']}
|
||||
_initiator_ns_map = {'100000051e55a100': ['10008c7cff523b01']}
|
||||
_device_map_to_verify = {
|
||||
'100000051e55a100': {
|
||||
'initiator_port_wwn_list': [
|
||||
'10008c7cff523b01'], 'target_port_wwn_list': ['20240002ac000a50']}}
|
||||
_fabric_wwn = '100000051e55a100'
|
||||
|
||||
|
||||
class TestFCSanLookupService(san_service.FCSanLookupService, test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFCSanLookupService, self).setUp()
|
||||
self.configuration = self.setup_config()
|
||||
|
||||
# override some of the functions
|
||||
def __init__(self, *args, **kwargs):
|
||||
test.TestCase.__init__(self, *args, **kwargs)
|
||||
|
||||
def setup_config(self):
|
||||
configuration = conf.Configuration(None)
|
||||
# fill up config
|
||||
configuration.fc_san_lookup_service = ('cinder.tests.unit.zonemanager'
|
||||
'.test_cisco_lookup_service'
|
||||
'.FakeCiscoFCSanLookupService')
|
||||
return configuration
|
||||
|
||||
def test_get_device_mapping_from_network(self):
|
||||
GlobalParams._is_normal_test = True
|
||||
initiator_list = ['10008c7cff523b01']
|
||||
target_list = ['20240002ac000a50', '20240002ac000a40']
|
||||
device_map = self.get_device_mapping_from_network(
|
||||
initiator_list, target_list)
|
||||
self.assertDictMatch(_device_map_to_verify, device_map)
|
||||
|
||||
def test_get_device_mapping_from_network_for_invalid_config(self):
|
||||
GlobalParams._is_normal_test = False
|
||||
initiator_list = ['10008c7cff523b01']
|
||||
target_list = ['20240002ac000a50', '20240002ac000a40']
|
||||
self.assertRaises(exception.FCSanLookupServiceException,
|
||||
self.get_device_mapping_from_network,
|
||||
initiator_list, target_list)
|
||||
|
||||
|
||||
class FakeCiscoFCSanLookupService(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
def get_device_mapping_from_network(self,
|
||||
initiator_wwn_list,
|
||||
target_wwn_list):
|
||||
if not GlobalParams._is_normal_test:
|
||||
raise exception.FCSanLookupServiceException("Error")
|
||||
device_map = {}
|
||||
initiators = []
|
||||
targets = []
|
||||
for i in initiator_wwn_list:
|
||||
if (i in _initiator_ns_map[_fabric_wwn]):
|
||||
initiators.append(i)
|
||||
for t in target_wwn_list:
|
||||
if (t in _target_ns_map[_fabric_wwn]):
|
||||
targets.append(t)
|
||||
device_map[_fabric_wwn] = {
|
||||
'initiator_port_wwn_list': initiators,
|
||||
'target_port_wwn_list': targets}
|
||||
return device_map
|
||||
|
||||
|
||||
class GlobalParams(object):
|
||||
global _is_normal_test
|
||||
_is_normal_test = True
|
@ -1,56 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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 oslo_config import cfg
|
||||
|
||||
from cinder.volume import configuration
|
||||
|
||||
cisco_zone_opts = [
|
||||
cfg.StrOpt('cisco_fc_fabric_address',
|
||||
default='',
|
||||
help='Management IP of fabric'),
|
||||
cfg.StrOpt('cisco_fc_fabric_user',
|
||||
default='',
|
||||
help='Fabric user ID'),
|
||||
cfg.StrOpt('cisco_fc_fabric_password',
|
||||
default='',
|
||||
help='Password for user',
|
||||
secret=True),
|
||||
cfg.PortOpt('cisco_fc_fabric_port',
|
||||
default=22,
|
||||
help='Connecting port'),
|
||||
cfg.StrOpt('cisco_zoning_policy',
|
||||
default='initiator-target',
|
||||
help='overridden zoning policy'),
|
||||
cfg.BoolOpt('cisco_zone_activate',
|
||||
default=True,
|
||||
help='overridden zoning activation state'),
|
||||
cfg.StrOpt('cisco_zone_name_prefix',
|
||||
help='overridden zone name prefix'),
|
||||
cfg.StrOpt('cisco_zoning_vsan',
|
||||
help='VSAN of the Fabric'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(cisco_zone_opts, group='CISCO_FABRIC_EXAMPLE')
|
||||
|
||||
|
||||
def load_fabric_configurations(fabric_names):
|
||||
fabric_configs = {}
|
||||
for fabric_name in fabric_names:
|
||||
config = configuration.Configuration(cisco_zone_opts, fabric_name)
|
||||
fabric_configs[fabric_name] = config
|
||||
|
||||
return fabric_configs
|
@ -1,355 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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 random
|
||||
|
||||
from eventlet import greenthread
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
import six
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE
|
||||
from cinder import ssh_utils
|
||||
from cinder import utils
|
||||
from cinder.zonemanager.drivers.cisco import cisco_fabric_opts as fabric_opts
|
||||
import cinder.zonemanager.drivers.cisco.fc_zone_constants as zone_constant
|
||||
from cinder.zonemanager import fc_san_lookup_service as fc_service
|
||||
from cinder.zonemanager import utils as zm_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CiscoFCSanLookupService(fc_service.FCSanLookupService):
|
||||
"""The SAN lookup service that talks to Cisco switches.
|
||||
|
||||
Version History:
|
||||
1.0.0 - Initial version
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "1.0.0"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initializing the client."""
|
||||
super(CiscoFCSanLookupService, self).__init__(**kwargs)
|
||||
self.configuration = kwargs.get('configuration', None)
|
||||
self.create_configuration()
|
||||
|
||||
self.switch_user = ""
|
||||
self.switch_port = ""
|
||||
self.switch_pwd = ""
|
||||
self.switch_ip = ""
|
||||
self.sshpool = None
|
||||
|
||||
def create_configuration(self):
|
||||
"""Configuration specific to SAN context values."""
|
||||
config = self.configuration
|
||||
|
||||
fabric_names = [x.strip() for x in config.fc_fabric_names.split(',')]
|
||||
LOG.debug('Fabric Names: %s', fabric_names)
|
||||
|
||||
# There can be more than one SAN in the network and we need to
|
||||
# get credentials for each for SAN context lookup later.
|
||||
# Cisco Zonesets require VSANs
|
||||
if fabric_names:
|
||||
self.fabric_configs = fabric_opts.load_fabric_configurations(
|
||||
fabric_names)
|
||||
|
||||
def get_device_mapping_from_network(self,
|
||||
initiator_wwn_list,
|
||||
target_wwn_list):
|
||||
"""Provides the initiator/target map for available SAN contexts.
|
||||
|
||||
Looks up fcns database of each fc SAN configured to find logged in
|
||||
devices and returns a map of initiator and target port WWNs for each
|
||||
fabric.
|
||||
|
||||
:param initiator_wwn_list: List of initiator port WWN
|
||||
:param target_wwn_list: List of target port WWN
|
||||
:returns: List -- device wwn map in following format
|
||||
{
|
||||
<San name>: {
|
||||
'initiator_port_wwn_list':
|
||||
('200000051e55a100', '200000051e55a121'..)
|
||||
'target_port_wwn_list':
|
||||
('100000051e55a100', '100000051e55a121'..)
|
||||
}
|
||||
}
|
||||
:raises: Exception when connection to fabric is failed
|
||||
"""
|
||||
device_map = {}
|
||||
formatted_target_list = []
|
||||
formatted_initiator_list = []
|
||||
fabric_map = {}
|
||||
fabric_names = self.configuration.fc_fabric_names
|
||||
|
||||
if not fabric_names:
|
||||
raise exception.InvalidParameterValue(
|
||||
err=_("Missing Fibre Channel SAN configuration "
|
||||
"param - fc_fabric_names"))
|
||||
|
||||
fabrics = [x.strip() for x in fabric_names.split(',')]
|
||||
|
||||
LOG.debug("FC Fabric List: %s", fabrics)
|
||||
if fabrics:
|
||||
for t in target_wwn_list:
|
||||
formatted_target_list.append(zm_utils.get_formatted_wwn(t))
|
||||
|
||||
for i in initiator_wwn_list:
|
||||
formatted_initiator_list.append(zm_utils.get_formatted_wwn(i))
|
||||
|
||||
for fabric_name in fabrics:
|
||||
self.switch_ip = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_address')
|
||||
self.switch_user = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_user')
|
||||
self.switch_pwd = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_password')
|
||||
self.switch_port = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_port')
|
||||
zoning_vsan = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_zoning_vsan')
|
||||
|
||||
# Get name server data from fabric and find the targets
|
||||
# logged in
|
||||
nsinfo = ''
|
||||
LOG.debug("show fcns database for vsan %s", zoning_vsan)
|
||||
nsinfo = self.get_nameserver_info(zoning_vsan)
|
||||
|
||||
LOG.debug("Lookup service:fcnsdatabase-%s", nsinfo)
|
||||
LOG.debug("Lookup service:initiator list from caller-%s",
|
||||
formatted_initiator_list)
|
||||
LOG.debug("Lookup service:target list from caller-%s",
|
||||
formatted_target_list)
|
||||
visible_targets = [x for x in nsinfo
|
||||
if x in formatted_target_list]
|
||||
visible_initiators = [x for x in nsinfo
|
||||
if x in formatted_initiator_list]
|
||||
|
||||
if visible_targets:
|
||||
LOG.debug("Filtered targets is: %s", visible_targets)
|
||||
# getting rid of the : before returning
|
||||
for idx, elem in enumerate(visible_targets):
|
||||
elem = str(elem).replace(':', '')
|
||||
visible_targets[idx] = elem
|
||||
else:
|
||||
LOG.debug("No targets are in the fcns database"
|
||||
" for vsan %s", zoning_vsan)
|
||||
|
||||
if visible_initiators:
|
||||
# getting rid of the : before returning ~sk
|
||||
for idx, elem in enumerate(visible_initiators):
|
||||
elem = str(elem).replace(':', '')
|
||||
visible_initiators[idx] = elem
|
||||
else:
|
||||
LOG.debug("No initiators are in the fcns database"
|
||||
" for vsan %s", zoning_vsan)
|
||||
|
||||
fabric_map = {'initiator_port_wwn_list': visible_initiators,
|
||||
'target_port_wwn_list': visible_targets
|
||||
}
|
||||
device_map[zoning_vsan] = fabric_map
|
||||
LOG.debug("Device map for SAN context: %s", device_map)
|
||||
return device_map
|
||||
|
||||
def get_nameserver_info(self, fabric_vsan):
|
||||
"""Get fcns database info from fabric.
|
||||
|
||||
This method will return the connected node port wwn list(local
|
||||
and remote) for the given switch fabric
|
||||
"""
|
||||
cli_output = None
|
||||
nsinfo_list = []
|
||||
try:
|
||||
cmd = ([zone_constant.FCNS_SHOW, fabric_vsan, ' | no-more'])
|
||||
cli_output = self._get_switch_info(cmd)
|
||||
except exception.FCSanLookupServiceException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Failed collecting show fcns database for"
|
||||
" fabric"))
|
||||
if cli_output:
|
||||
nsinfo_list = self._parse_ns_output(cli_output)
|
||||
|
||||
LOG.debug("Connector returning fcns info-%s", nsinfo_list)
|
||||
return nsinfo_list
|
||||
|
||||
def _get_switch_info(self, cmd_list):
|
||||
stdout, stderr, sw_data = None, None, None
|
||||
try:
|
||||
stdout, stderr = self._run_ssh(cmd_list, True, 1)
|
||||
LOG.debug("CLI output from ssh - output: %s", stdout)
|
||||
if (stdout):
|
||||
sw_data = stdout.splitlines()
|
||||
return sw_data
|
||||
except processutils.ProcessExecutionError as e:
|
||||
msg = _("Error while getting data via ssh: (command=%(cmd)s "
|
||||
"error=%(err)s).") % {'cmd': cmd_list,
|
||||
'err': six.text_type(e)}
|
||||
LOG.error(msg)
|
||||
raise exception.CiscoZoningCliException(reason=msg)
|
||||
|
||||
def _parse_ns_output(self, switch_data):
|
||||
"""Parses name server data.
|
||||
|
||||
Parses nameserver raw data and adds the device port wwns to the list
|
||||
|
||||
:returns: list of device port wwn from ns info
|
||||
"""
|
||||
nsinfo_list = []
|
||||
for line in switch_data:
|
||||
if not(" N " in line):
|
||||
continue
|
||||
linesplit = line.split()
|
||||
if len(linesplit) > 2:
|
||||
node_port_wwn = linesplit[2]
|
||||
nsinfo_list.append(node_port_wwn)
|
||||
else:
|
||||
msg = _("Malformed fcns output string: %s") % line
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
return nsinfo_list
|
||||
|
||||
def _run_ssh(self, cmd_list, check_exit_code=True, attempts=1):
|
||||
|
||||
command = ' '.join(cmd_list)
|
||||
|
||||
if not self.sshpool:
|
||||
self.sshpool = ssh_utils.SSHPool(self.switch_ip,
|
||||
self.switch_port,
|
||||
None,
|
||||
self.switch_user,
|
||||
self.switch_pwd,
|
||||
min_size=1,
|
||||
max_size=5)
|
||||
last_exception = None
|
||||
try:
|
||||
with self.sshpool.item() as ssh:
|
||||
while attempts > 0:
|
||||
attempts -= 1
|
||||
try:
|
||||
return processutils.ssh_execute(
|
||||
ssh,
|
||||
command,
|
||||
check_exit_code=check_exit_code)
|
||||
except Exception as e:
|
||||
msg = _("Exception: %s") % six.text_type(e)
|
||||
LOG.error(msg)
|
||||
last_exception = e
|
||||
greenthread.sleep(random.randint(20, 500) / 100.0)
|
||||
try:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=last_exception.exit_code,
|
||||
stdout=last_exception.stdout,
|
||||
stderr=last_exception.stderr,
|
||||
cmd=last_exception.cmd)
|
||||
except AttributeError:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=-1,
|
||||
stdout="",
|
||||
stderr="Error running SSH command",
|
||||
cmd=command)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Error running SSH command: %s"), command)
|
||||
|
||||
def _ssh_execute(self, cmd_list, check_exit_code=True, attempts=1):
|
||||
"""Execute cli with status update.
|
||||
|
||||
Executes CLI commands where status return is expected.
|
||||
|
||||
cmd_list is a list of commands, where each command is itself
|
||||
a list of parameters. We use utils.check_ssh_injection to check each
|
||||
command, but then join then with " ; " to form a single command.
|
||||
"""
|
||||
|
||||
# Check that each command is secure
|
||||
for cmd in cmd_list:
|
||||
utils.check_ssh_injection(cmd)
|
||||
|
||||
# Combine into a single command.
|
||||
command = ' ; '.join(map(lambda x: ' '.join(x), cmd_list))
|
||||
|
||||
if not self.sshpool:
|
||||
self.sshpool = ssh_utils.SSHPool(self.switch_ip,
|
||||
self.switch_port,
|
||||
None,
|
||||
self.switch_user,
|
||||
self.switch_pwd,
|
||||
min_size=1,
|
||||
max_size=5)
|
||||
stdin, stdout, stderr = None, None, None
|
||||
LOG.debug("Executing command via ssh: %s", command)
|
||||
last_exception = None
|
||||
try:
|
||||
with self.sshpool.item() as ssh:
|
||||
while attempts > 0:
|
||||
attempts -= 1
|
||||
try:
|
||||
stdin, stdout, stderr = ssh.exec_command(command)
|
||||
greenthread.sleep(random.randint(20, 500) / 100.0)
|
||||
channel = stdout.channel
|
||||
exit_status = channel.recv_exit_status()
|
||||
LOG.debug("Exit Status from ssh:%s", exit_status)
|
||||
# exit_status == -1 if no exit code was returned
|
||||
if exit_status != -1:
|
||||
LOG.debug('Result was %s', exit_status)
|
||||
if check_exit_code and exit_status != 0:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=exit_status,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
cmd=command)
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
except Exception as e:
|
||||
msg = _("Exception: %s") % six.text_type(e)
|
||||
LOG.error(msg)
|
||||
last_exception = e
|
||||
greenthread.sleep(random.randint(20, 500) / 100.0)
|
||||
LOG.debug("Handling error case after SSH:%s", last_exception)
|
||||
try:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=last_exception.exit_code,
|
||||
stdout=last_exception.stdout,
|
||||
stderr=last_exception.stderr,
|
||||
cmd=last_exception.cmd)
|
||||
except AttributeError:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=-1,
|
||||
stdout="",
|
||||
stderr="Error running SSH command",
|
||||
cmd=command)
|
||||
except Exception as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
msg = (_("Error executing command via ssh: %s") %
|
||||
six.text_type(e))
|
||||
LOG.error(msg)
|
||||
finally:
|
||||
if stdin:
|
||||
stdin.flush()
|
||||
stdin.close()
|
||||
if stdout:
|
||||
stdout.close()
|
||||
if stderr:
|
||||
stderr.close()
|
||||
|
||||
def cleanup(self):
|
||||
self.sshpool = None
|
@ -1,460 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""
|
||||
Script to push the zone configuration to Cisco SAN switches.
|
||||
"""
|
||||
import random
|
||||
import re
|
||||
|
||||
from eventlet import greenthread
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
import six
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE, _LI, _LW
|
||||
from cinder import ssh_utils
|
||||
from cinder import utils
|
||||
import cinder.zonemanager.drivers.cisco.fc_zone_constants as ZoneConstant
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CiscoFCZoneClientCLI(object):
|
||||
"""Cisco FC zone client cli implementation.
|
||||
|
||||
OpenStack Fibre Channel zone client cli connector
|
||||
to manage FC zoning in Cisco SAN fabrics.
|
||||
|
||||
Version history:
|
||||
1.0 - Initial Cisco FC zone client cli
|
||||
"""
|
||||
|
||||
switch_ip = None
|
||||
switch_port = '22'
|
||||
switch_user = 'admin'
|
||||
switch_pwd = 'none'
|
||||
|
||||
def __init__(self, ipaddress, username, password, port, vsan):
|
||||
"""initializing the client."""
|
||||
self.switch_ip = ipaddress
|
||||
self.switch_port = port
|
||||
self.switch_user = username
|
||||
self.switch_pwd = password
|
||||
self.fabric_vsan = vsan
|
||||
self.sshpool = None
|
||||
|
||||
def get_active_zone_set(self):
|
||||
"""Return the active zone configuration.
|
||||
|
||||
Return active zoneset from fabric. When none of the configurations
|
||||
are active then it will return empty map.
|
||||
|
||||
:returns: Map -- active zone set map in the following format
|
||||
{
|
||||
'zones':
|
||||
{'openstack50060b0000c26604201900051ee8e329':
|
||||
['50060b0000c26604', '201900051ee8e329']
|
||||
},
|
||||
'active_zone_config': 'OpenStack_Cfg'
|
||||
}
|
||||
"""
|
||||
zone_set = {}
|
||||
zone = {}
|
||||
zone_member = None
|
||||
zone_name = None
|
||||
switch_data = None
|
||||
zone_set_name = None
|
||||
try:
|
||||
switch_data = self._get_switch_info(
|
||||
[ZoneConstant.GET_ACTIVE_ZONE_CFG, self.fabric_vsan,
|
||||
' | no-more'])
|
||||
except exception.CiscoZoningCliException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Failed getting active zone set "
|
||||
"from fabric %s"), self.switch_ip)
|
||||
try:
|
||||
for line in switch_data:
|
||||
# Split on non-word characters,
|
||||
line_split = re.split('[\s\[\]]+', line)
|
||||
if ZoneConstant.CFG_ZONESET in line_split:
|
||||
# zoneset name [name] vsan [vsan]
|
||||
zone_set_name = \
|
||||
line_split[line_split.index(ZoneConstant.CFG_ZONESET)
|
||||
+ 2]
|
||||
continue
|
||||
if ZoneConstant.CFG_ZONE in line_split:
|
||||
# zone name [name] vsan [vsan]
|
||||
zone_name = \
|
||||
line_split[line_split.index(ZoneConstant.CFG_ZONE) + 2]
|
||||
zone[zone_name] = list()
|
||||
continue
|
||||
if ZoneConstant.CFG_ZONE_MEMBER in line_split:
|
||||
# Examples:
|
||||
# pwwn c0:50:76:05:15:9f:00:12
|
||||
# * fcid 0x1e01c0 [pwwn 50:05:07:68:02:20:48:04] [V7K_N1P2]
|
||||
zone_member = \
|
||||
line_split[
|
||||
line_split.index(ZoneConstant.CFG_ZONE_MEMBER) + 1]
|
||||
zone_member_list = zone.get(zone_name)
|
||||
zone_member_list.append(zone_member)
|
||||
|
||||
zone_set[ZoneConstant.CFG_ZONES] = zone
|
||||
zone_set[ZoneConstant.ACTIVE_ZONE_CONFIG] = zone_set_name
|
||||
except Exception as ex:
|
||||
# In case of parsing error here, it should be malformed cli output.
|
||||
msg = _("Malformed zone configuration: (switch=%(switch)s "
|
||||
"zone_config=%(zone_config)s)."
|
||||
) % {'switch': self.switch_ip,
|
||||
'zone_config': switch_data}
|
||||
LOG.error(msg)
|
||||
exc_msg = _("Exception: %s") % six.text_type(ex)
|
||||
LOG.error(exc_msg)
|
||||
raise exception.FCZoneDriverException(reason=msg)
|
||||
|
||||
return zone_set
|
||||
|
||||
def add_zones(self, zones, activate, fabric_vsan, active_zone_set,
|
||||
zone_status):
|
||||
"""Add zone configuration.
|
||||
|
||||
This method will add the zone configuration passed by user.
|
||||
input params:
|
||||
zones - zone names mapped to members and VSANs.
|
||||
zone members are colon separated but case-insensitive
|
||||
{ zonename1:[zonememeber1,zonemember2,...],
|
||||
zonename2:[zonemember1, zonemember2,...]...}
|
||||
e.g: {'openstack50060b0000c26604201900051ee8e329':
|
||||
['50:06:0b:00:00:c2:66:04', '20:19:00:05:1e:e8:e3:29']
|
||||
}
|
||||
activate - True/False
|
||||
"""
|
||||
LOG.debug("Add Zones - Zones passed: %s", zones)
|
||||
|
||||
LOG.debug("Active zone set: %s", active_zone_set)
|
||||
zone_list = active_zone_set[ZoneConstant.CFG_ZONES]
|
||||
LOG.debug("zone list: %s", zone_list)
|
||||
LOG.debug("zone status: %s", zone_status)
|
||||
|
||||
cfg_name = active_zone_set[ZoneConstant.ACTIVE_ZONE_CONFIG]
|
||||
|
||||
zone_cmds = [['conf'],
|
||||
['zoneset', 'name', cfg_name, 'vsan', fabric_vsan]]
|
||||
|
||||
for zone in zones.keys():
|
||||
# if zone exists, its an update. Delete & insert
|
||||
LOG.debug("Update call")
|
||||
if zone in zone_list:
|
||||
# Response from get_active_zone_set strips colons from WWPNs
|
||||
current_zone = set(zone_list[zone])
|
||||
new_wwpns = map(lambda x: x.lower().replace(':', ''),
|
||||
zones[zone])
|
||||
new_zone = set(new_wwpns)
|
||||
|
||||
if current_zone != new_zone:
|
||||
try:
|
||||
self.delete_zones(zone, activate, fabric_vsan,
|
||||
active_zone_set, zone_status)
|
||||
except exception.CiscoZoningCliException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Deleting zone failed %s"), zone)
|
||||
LOG.debug("Deleted Zone before insert : %s", zone)
|
||||
|
||||
zone_cmds.append(['zone', 'name', zone])
|
||||
|
||||
for member in zones[zone]:
|
||||
zone_cmds.append(['member', 'pwwn', member])
|
||||
|
||||
zone_cmds.append(['end'])
|
||||
|
||||
try:
|
||||
LOG.debug("Add zones: Config cmd to run: %s", zone_cmds)
|
||||
self._ssh_execute(zone_cmds, True, 1)
|
||||
|
||||
if activate:
|
||||
self.activate_zoneset(cfg_name, fabric_vsan, zone_status)
|
||||
self._cfg_save()
|
||||
except Exception as e:
|
||||
|
||||
msg = _("Creating and activating zone set failed: "
|
||||
"(Zone set=%(zoneset)s error=%(err)s)."
|
||||
) % {'zoneset': cfg_name, 'err': six.text_type(e)}
|
||||
LOG.error(msg)
|
||||
raise exception.CiscoZoningCliException(reason=msg)
|
||||
|
||||
def activate_zoneset(self, cfgname, fabric_vsan, zone_status):
|
||||
"""Method to Activate the zone config. Param cfgname - ZonesetName."""
|
||||
|
||||
LOG.debug("zone status: %s", zone_status)
|
||||
|
||||
cmd_list = [['conf'],
|
||||
['zoneset', 'activate', 'name', cfgname, 'vsan',
|
||||
self.fabric_vsan]]
|
||||
if zone_status['mode'] == 'enhanced':
|
||||
cmd_list.append(['zone', 'commit', 'vsan', fabric_vsan])
|
||||
|
||||
cmd_list.append(['end'])
|
||||
|
||||
return self._ssh_execute(cmd_list, True, 1)
|
||||
|
||||
def get_zoning_status(self):
|
||||
"""Return the zoning mode and session for a zoneset."""
|
||||
zone_status = {}
|
||||
|
||||
try:
|
||||
switch_data = self._get_switch_info(
|
||||
[ZoneConstant.GET_ZONE_STATUS, self.fabric_vsan])
|
||||
except exception.CiscoZoningCliException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Failed getting zone status "
|
||||
"from fabric %s"), self.switch_ip)
|
||||
try:
|
||||
for line in switch_data:
|
||||
# Split on non-word characters,
|
||||
line_split = re.split('[\s\[\]]+', line)
|
||||
if 'mode:' in line_split:
|
||||
# mode: <enhanced|basic>
|
||||
zone_status['mode'] = line_split[line_split.index('mode:')
|
||||
+ 1]
|
||||
continue
|
||||
if 'session:' in line_split:
|
||||
# session: <none|a value other than none>
|
||||
zone_status['session'] = \
|
||||
line_split[line_split.index('session:') + 1]
|
||||
continue
|
||||
except Exception as ex:
|
||||
# In case of parsing error here, it should be malformed cli output.
|
||||
msg = _("Malformed zone status: (switch=%(switch)s "
|
||||
"zone_config=%(zone_config)s)."
|
||||
) % {'switch': self.switch_ip,
|
||||
'zone_status': switch_data}
|
||||
LOG.error(msg)
|
||||
exc_msg = _("Exception: %s") % six.text_type(ex)
|
||||
LOG.error(exc_msg)
|
||||
raise exception.FCZoneDriverException(reason=msg)
|
||||
|
||||
return zone_status
|
||||
|
||||
def delete_zones(self, zone_names, activate, fabric_vsan, active_zone_set,
|
||||
zone_status):
|
||||
"""Delete zones from fabric.
|
||||
|
||||
Method to delete the active zone config zones
|
||||
|
||||
params zone_names: zoneNames separated by semicolon
|
||||
params activate: True/False
|
||||
"""
|
||||
|
||||
LOG.debug("zone_names %s", zone_names)
|
||||
active_zoneset_name = active_zone_set[ZoneConstant.ACTIVE_ZONE_CONFIG]
|
||||
|
||||
cmds = [['conf'],
|
||||
['zoneset', 'name', active_zoneset_name, 'vsan',
|
||||
fabric_vsan]]
|
||||
|
||||
try:
|
||||
for zone in set(zone_names.split(';')):
|
||||
cmds.append(['no', 'zone', 'name', zone])
|
||||
|
||||
cmds.append(['end'])
|
||||
|
||||
LOG.debug("Delete zones: Config cmd to run: %s", cmds)
|
||||
self._ssh_execute(cmds, True, 1)
|
||||
|
||||
if activate:
|
||||
self.activate_zoneset(active_zoneset_name, fabric_vsan,
|
||||
zone_status)
|
||||
self._cfg_save()
|
||||
|
||||
except Exception as e:
|
||||
msg = _("Deleting zones failed: (command=%(cmd)s error=%(err)s)."
|
||||
) % {'cmd': cmds, 'err': six.text_type(e)}
|
||||
LOG.error(msg)
|
||||
raise exception.CiscoZoningCliException(reason=msg)
|
||||
|
||||
def get_nameserver_info(self):
|
||||
"""Get name server data from fabric.
|
||||
|
||||
This method will return the connected node port wwn list(local
|
||||
and remote) for the given switch fabric
|
||||
|
||||
show fcns database
|
||||
"""
|
||||
cli_output = None
|
||||
return_list = []
|
||||
try:
|
||||
cli_output = self._get_switch_info([ZoneConstant.FCNS_SHOW,
|
||||
self.fabric_vsan])
|
||||
except exception.CiscoZoningCliException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Failed collecting fcns database "
|
||||
"info for fabric %s"), self.switch_ip)
|
||||
|
||||
if (cli_output):
|
||||
return_list = self._parse_ns_output(cli_output)
|
||||
|
||||
LOG.info(_LI("Connector returning fcnsinfo-%s"), return_list)
|
||||
|
||||
return return_list
|
||||
|
||||
@utils.retry(processutils.ProcessExecutionError, retries=5)
|
||||
def _cfg_save(self):
|
||||
cmd = ['copy', 'running-config', 'startup-config']
|
||||
self._run_ssh(cmd, True)
|
||||
|
||||
def _get_switch_info(self, cmd_list):
|
||||
stdout, stderr, sw_data = None, None, None
|
||||
try:
|
||||
stdout, stderr = self._run_ssh(cmd_list, True)
|
||||
LOG.debug("CLI output from ssh - output: %s", stdout)
|
||||
if (stdout):
|
||||
sw_data = stdout.splitlines()
|
||||
return sw_data
|
||||
except processutils.ProcessExecutionError as e:
|
||||
msg = _("Error while getting data via ssh: (command=%(cmd)s "
|
||||
"error=%(err)s).") % {'cmd': cmd_list,
|
||||
'err': six.text_type(e)}
|
||||
LOG.error(msg)
|
||||
raise exception.CiscoZoningCliException(reason=msg)
|
||||
|
||||
def _parse_ns_output(self, switch_data):
|
||||
"""Parses name server data.
|
||||
|
||||
Parses nameserver raw data and adds the device port wwns to the list
|
||||
|
||||
:returns: List -- list of device port wwn from ns info
|
||||
"""
|
||||
return_list = []
|
||||
for line in switch_data:
|
||||
if not(" N " in line):
|
||||
continue
|
||||
linesplit = line.split()
|
||||
if len(linesplit) > 2:
|
||||
node_port_wwn = linesplit[2]
|
||||
return_list.append(node_port_wwn)
|
||||
else:
|
||||
msg = _("Malformed show fcns database string: %s") % line
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
return return_list
|
||||
|
||||
def _run_ssh(self, cmd_list, check_exit_code=True):
|
||||
|
||||
command = ' '.join(cmd_list)
|
||||
|
||||
if not self.sshpool:
|
||||
self.sshpool = ssh_utils.SSHPool(self.switch_ip,
|
||||
self.switch_port,
|
||||
None,
|
||||
self.switch_user,
|
||||
self.switch_pwd,
|
||||
min_size=1,
|
||||
max_size=5)
|
||||
try:
|
||||
with self.sshpool.item() as ssh:
|
||||
return processutils.ssh_execute(
|
||||
ssh,
|
||||
command,
|
||||
check_exit_code=check_exit_code)
|
||||
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.warning(_LW("Error running SSH command: %s"), command)
|
||||
|
||||
def _ssh_execute(self, cmd_list, check_exit_code=True, attempts=1):
|
||||
"""Execute cli with status update.
|
||||
|
||||
Executes CLI commands where status return is expected.
|
||||
|
||||
cmd_list is a list of commands, where each command is itself
|
||||
a list of parameters. We use utils.check_ssh_injection to check each
|
||||
command, but then join then with " ; " to form a single command.
|
||||
"""
|
||||
|
||||
# Check that each command is secure
|
||||
for cmd in cmd_list:
|
||||
utils.check_ssh_injection(cmd)
|
||||
|
||||
# Combine into a single command.
|
||||
command = ' ; '.join(map(lambda x: ' '.join(x), cmd_list))
|
||||
|
||||
if not self.sshpool:
|
||||
self.sshpool = ssh_utils.SSHPool(self.switch_ip,
|
||||
self.switch_port,
|
||||
None,
|
||||
self.switch_user,
|
||||
self.switch_pwd,
|
||||
min_size=1,
|
||||
max_size=5)
|
||||
stdin, stdout, stderr = None, None, None
|
||||
LOG.debug("Executing command via ssh: %s", command)
|
||||
last_exception = None
|
||||
try:
|
||||
with self.sshpool.item() as ssh:
|
||||
while attempts > 0:
|
||||
attempts -= 1
|
||||
try:
|
||||
stdin, stdout, stderr = ssh.exec_command(command)
|
||||
channel = stdout.channel
|
||||
exit_status = channel.recv_exit_status()
|
||||
LOG.debug("Exit Status from ssh: %s", exit_status)
|
||||
# exit_status == -1 if no exit code was returned
|
||||
if exit_status != -1:
|
||||
LOG.debug('Result was %s', exit_status)
|
||||
if check_exit_code and exit_status != 0:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=exit_status,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
cmd=command)
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
except Exception as e:
|
||||
LOG.exception(_LE('Error executing SSH command.'))
|
||||
last_exception = e
|
||||
greenthread.sleep(random.randint(20, 500) / 100.0)
|
||||
LOG.debug("Handling error case after SSH: %s", last_exception)
|
||||
try:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=last_exception.exit_code,
|
||||
stdout=last_exception.stdout,
|
||||
stderr=last_exception.stderr,
|
||||
cmd=last_exception.cmd)
|
||||
except AttributeError:
|
||||
raise processutils.ProcessExecutionError(
|
||||
exit_code=-1,
|
||||
stdout="",
|
||||
stderr="Error running SSH command",
|
||||
cmd=command)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error executing command via ssh."))
|
||||
finally:
|
||||
if stdin:
|
||||
stdin.flush()
|
||||
stdin.close()
|
||||
if stdout:
|
||||
stdout.close()
|
||||
if stderr:
|
||||
stderr.close()
|
||||
|
||||
def cleanup(self):
|
||||
self.sshpool = None
|
@ -1,518 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""
|
||||
Cisco Zone Driver is responsible to manage access control using FC zoning
|
||||
for Cisco FC fabrics.
|
||||
This is a concrete implementation of FCZoneDriver interface implementing
|
||||
add_connection and delete_connection interfaces.
|
||||
|
||||
**Related Flags**
|
||||
|
||||
:zone_activate: Used by: class: 'FCZoneDriver'. Defaults to True
|
||||
:zone_name_prefix: Used by: class: 'FCZoneDriver'. Defaults to 'openstack'
|
||||
"""
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
import six
|
||||
import string
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE, _LI
|
||||
from cinder.zonemanager.drivers.cisco import cisco_fabric_opts as fabric_opts
|
||||
from cinder.zonemanager.drivers import driver_utils
|
||||
from cinder.zonemanager.drivers import fc_zone_driver
|
||||
from cinder.zonemanager import utils as zm_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SUPPORTED_CHARS = string.ascii_letters + string.digits + '$' + '-' + '^' + '_'
|
||||
cisco_opts = [
|
||||
cfg.StrOpt('cisco_sb_connector',
|
||||
default='cinder.zonemanager.drivers.cisco'
|
||||
'.cisco_fc_zone_client_cli.CiscoFCZoneClientCLI',
|
||||
help='Southbound connector for zoning operation'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(cisco_opts, group='fc-zone-manager')
|
||||
|
||||
|
||||
class CiscoFCZoneDriver(fc_zone_driver.FCZoneDriver):
|
||||
"""Cisco FC zone driver implementation.
|
||||
|
||||
OpenStack Fibre Channel zone driver to manage FC zoning in
|
||||
Cisco SAN fabrics.
|
||||
|
||||
Version history:
|
||||
1.0 - Initial Cisco FC zone driver
|
||||
1.1 - Added friendly zone name support
|
||||
"""
|
||||
|
||||
VERSION = "1.1.0"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(CiscoFCZoneDriver, self).__init__(**kwargs)
|
||||
self.configuration = kwargs.get('configuration', None)
|
||||
if self.configuration:
|
||||
self.configuration.append_config_values(cisco_opts)
|
||||
|
||||
# Adding a hack to handle parameters from super classes
|
||||
# in case configured with multi backends.
|
||||
fabric_names = self.configuration.safe_get('fc_fabric_names')
|
||||
activate = self.configuration.safe_get('cisco_zone_activate')
|
||||
prefix = self.configuration.safe_get('cisco_zone_name_prefix')
|
||||
base_san_opts = []
|
||||
if not fabric_names:
|
||||
base_san_opts.append(
|
||||
cfg.StrOpt('fc_fabric_names',
|
||||
help='Comma separated list of fibre channel '
|
||||
'fabric names. This list of names is used to'
|
||||
' retrieve other SAN credentials for connecting'
|
||||
' to each SAN fabric'
|
||||
))
|
||||
if not activate:
|
||||
base_san_opts.append(
|
||||
cfg.BoolOpt('cisco_zone_activate',
|
||||
default=True,
|
||||
help='Indicates whether zone should '
|
||||
'be activated or not'))
|
||||
if not prefix:
|
||||
base_san_opts.append(
|
||||
cfg.StrOpt('cisco_zone_name_prefix',
|
||||
default="openstack",
|
||||
help="A prefix to be used when naming zone"))
|
||||
if len(base_san_opts) > 0:
|
||||
CONF.register_opts(base_san_opts)
|
||||
self.configuration.append_config_values(base_san_opts)
|
||||
fabric_names = [x.strip() for x in self.
|
||||
configuration.fc_fabric_names.split(',')]
|
||||
|
||||
# There can be more than one SAN in the network and we need to
|
||||
# get credentials for each SAN.
|
||||
if fabric_names:
|
||||
self.fabric_configs = fabric_opts.load_fabric_configurations(
|
||||
fabric_names)
|
||||
|
||||
@lockutils.synchronized('cisco', 'fcfabric-', True)
|
||||
def add_connection(self, fabric, initiator_target_map, host_name=None,
|
||||
storage_system=None):
|
||||
"""Concrete implementation of add_connection.
|
||||
|
||||
Based on zoning policy and state of each I-T pair, list of zone
|
||||
members are created and pushed to the fabric to add zones. The
|
||||
new zones created or zones updated are activated based on isActivate
|
||||
flag set in cinder.conf returned by volume driver after attach
|
||||
operation.
|
||||
|
||||
:param fabric: Fabric name from cinder.conf file
|
||||
:param initiator_target_map: Mapping of initiator to list of targets
|
||||
"""
|
||||
|
||||
LOG.debug("Add connection for Fabric: %s", fabric)
|
||||
LOG.info(_LI("CiscoFCZoneDriver - Add connection "
|
||||
"for I-T map: %s"), initiator_target_map)
|
||||
fabric_ip = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_address')
|
||||
fabric_user = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_user')
|
||||
fabric_pwd = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_password')
|
||||
fabric_port = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_port')
|
||||
zoning_policy = self.configuration.zoning_policy
|
||||
zoning_policy_fab = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_zoning_policy')
|
||||
if zoning_policy_fab:
|
||||
zoning_policy = zoning_policy_fab
|
||||
|
||||
zoning_vsan = self.fabric_configs[fabric].safe_get('cisco_zoning_vsan')
|
||||
|
||||
LOG.info(_LI("Zoning policy for Fabric %s"), zoning_policy)
|
||||
|
||||
statusmap_from_fabric = self.get_zoning_status(
|
||||
fabric_ip, fabric_user, fabric_pwd, fabric_port, zoning_vsan)
|
||||
|
||||
if statusmap_from_fabric.get('session') == 'none':
|
||||
|
||||
cfgmap_from_fabric = self.get_active_zone_set(
|
||||
fabric_ip, fabric_user, fabric_pwd, fabric_port, zoning_vsan)
|
||||
zone_names = []
|
||||
if cfgmap_from_fabric.get('zones'):
|
||||
zone_names = cfgmap_from_fabric['zones'].keys()
|
||||
# based on zoning policy, create zone member list and
|
||||
# push changes to fabric.
|
||||
for initiator_key in initiator_target_map.keys():
|
||||
zone_map = {}
|
||||
initiator = initiator_key.lower()
|
||||
t_list = initiator_target_map[initiator_key]
|
||||
if zoning_policy == 'initiator-target':
|
||||
for t in t_list:
|
||||
target = t.lower()
|
||||
zone_members = [
|
||||
zm_utils.get_formatted_wwn(initiator),
|
||||
zm_utils.get_formatted_wwn(target)]
|
||||
zone_name = (
|
||||
driver_utils.get_friendly_zone_name(
|
||||
zoning_policy,
|
||||
initiator,
|
||||
target,
|
||||
host_name,
|
||||
storage_system,
|
||||
self.configuration.cisco_zone_name_prefix,
|
||||
SUPPORTED_CHARS))
|
||||
if (len(cfgmap_from_fabric) == 0 or (
|
||||
zone_name not in zone_names)):
|
||||
zone_map[zone_name] = zone_members
|
||||
else:
|
||||
# This is I-T zoning, skip if zone exists.
|
||||
LOG.info(_LI("Zone exists in I-T mode. "
|
||||
"Skipping zone creation %s"),
|
||||
zone_name)
|
||||
elif zoning_policy == 'initiator':
|
||||
zone_members = [
|
||||
zm_utils.get_formatted_wwn(initiator)]
|
||||
for t in t_list:
|
||||
target = t.lower()
|
||||
zone_members.append(
|
||||
zm_utils.get_formatted_wwn(target))
|
||||
|
||||
zone_name = (
|
||||
driver_utils.get_friendly_zone_name(
|
||||
zoning_policy,
|
||||
initiator,
|
||||
target,
|
||||
host_name,
|
||||
storage_system,
|
||||
self.configuration.cisco_zone_name_prefix,
|
||||
SUPPORTED_CHARS))
|
||||
|
||||
if len(zone_names) > 0 and (zone_name in zone_names):
|
||||
zone_members = zone_members + filter(
|
||||
lambda x: x not in zone_members,
|
||||
cfgmap_from_fabric['zones'][zone_name])
|
||||
zone_map[zone_name] = zone_members
|
||||
else:
|
||||
msg = _("Zoning Policy: %s, not"
|
||||
" recognized") % zoning_policy
|
||||
LOG.error(msg)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
|
||||
LOG.info(_LI("Zone map to add: %s"), zone_map)
|
||||
|
||||
if len(zone_map) > 0:
|
||||
conn = None
|
||||
try:
|
||||
conn = importutils.import_object(
|
||||
self.configuration.cisco_sb_connector,
|
||||
ipaddress=fabric_ip,
|
||||
username=fabric_user,
|
||||
password=fabric_pwd,
|
||||
port=fabric_port,
|
||||
vsan=zoning_vsan)
|
||||
conn.add_zones(
|
||||
zone_map, self.configuration.cisco_zone_activate,
|
||||
zoning_vsan, cfgmap_from_fabric,
|
||||
statusmap_from_fabric)
|
||||
conn.cleanup()
|
||||
except exception.CiscoZoningCliException as cisco_ex:
|
||||
msg = _("Exception: %s") % six.text_type(cisco_ex)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
except Exception:
|
||||
msg = _("Failed to add zoning configuration.")
|
||||
LOG.exception(msg)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
LOG.debug("Zones added successfully: %s", zone_map)
|
||||
else:
|
||||
LOG.debug("Zoning session exists VSAN: %s", zoning_vsan)
|
||||
|
||||
@lockutils.synchronized('cisco', 'fcfabric-', True)
|
||||
def delete_connection(self, fabric, initiator_target_map, host_name=None,
|
||||
storage_system=None):
|
||||
"""Concrete implementation of delete_connection.
|
||||
|
||||
Based on zoning policy and state of each I-T pair, list of zones
|
||||
are created for deletion. The zones are either updated deleted based
|
||||
on the policy and attach/detach state of each I-T pair.
|
||||
|
||||
:param fabric: Fabric name from cinder.conf file
|
||||
:param initiator_target_map: Mapping of initiator to list of targets
|
||||
"""
|
||||
LOG.debug("Delete connection for fabric: %s", fabric)
|
||||
LOG.info(_LI("CiscoFCZoneDriver - Delete connection for I-T map: %s"),
|
||||
initiator_target_map)
|
||||
fabric_ip = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_address')
|
||||
fabric_user = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_user')
|
||||
fabric_pwd = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_password')
|
||||
fabric_port = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_fc_fabric_port')
|
||||
zoning_policy = self.configuration.zoning_policy
|
||||
zoning_policy_fab = self.fabric_configs[fabric].safe_get(
|
||||
'cisco_zoning_policy')
|
||||
|
||||
if zoning_policy_fab:
|
||||
zoning_policy = zoning_policy_fab
|
||||
|
||||
zoning_vsan = self.fabric_configs[fabric].safe_get('cisco_zoning_vsan')
|
||||
|
||||
LOG.info(_LI("Zoning policy for fabric %s"), zoning_policy)
|
||||
|
||||
statusmap_from_fabric = self.get_zoning_status(
|
||||
fabric_ip, fabric_user, fabric_pwd, fabric_port, zoning_vsan)
|
||||
|
||||
if statusmap_from_fabric.get('session') == 'none':
|
||||
cfgmap_from_fabric = self.get_active_zone_set(
|
||||
fabric_ip, fabric_user, fabric_pwd, fabric_port, zoning_vsan)
|
||||
|
||||
zone_names = []
|
||||
if cfgmap_from_fabric.get('zones'):
|
||||
zone_names = cfgmap_from_fabric['zones'].keys()
|
||||
|
||||
# Based on zoning policy, get zone member list and push
|
||||
# changes to fabric. This operation could result in an update
|
||||
# for zone config with new member list or deleting zones from
|
||||
# active cfg.
|
||||
|
||||
LOG.debug("zone config from Fabric: %s", cfgmap_from_fabric)
|
||||
for initiator_key in initiator_target_map.keys():
|
||||
initiator = initiator_key.lower()
|
||||
formatted_initiator = zm_utils.get_formatted_wwn(initiator)
|
||||
zone_map = {}
|
||||
zones_to_delete = []
|
||||
t_list = initiator_target_map[initiator_key]
|
||||
if zoning_policy == 'initiator-target':
|
||||
# In this case, zone needs to be deleted.
|
||||
for t in t_list:
|
||||
target = t.lower()
|
||||
zone_name = (
|
||||
driver_utils.get_friendly_zone_name(
|
||||
zoning_policy,
|
||||
initiator,
|
||||
target,
|
||||
host_name,
|
||||
storage_system,
|
||||
self.configuration.cisco_zone_name_prefix,
|
||||
SUPPORTED_CHARS))
|
||||
LOG.debug("Zone name to del: %s", zone_name)
|
||||
if (len(zone_names) > 0 and (zone_name in zone_names)):
|
||||
# delete zone.
|
||||
LOG.debug("Added zone to delete to list: %s",
|
||||
zone_name)
|
||||
zones_to_delete.append(zone_name)
|
||||
|
||||
elif zoning_policy == 'initiator':
|
||||
zone_members = [formatted_initiator]
|
||||
for t in t_list:
|
||||
target = t.lower()
|
||||
zone_members.append(
|
||||
zm_utils.get_formatted_wwn(target))
|
||||
|
||||
zone_name = driver_utils.get_friendly_zone_name(
|
||||
zoning_policy,
|
||||
initiator,
|
||||
target,
|
||||
host_name,
|
||||
storage_system,
|
||||
self.configuration.cisco_zone_name_prefix,
|
||||
SUPPORTED_CHARS)
|
||||
|
||||
if (zone_names and (zone_name in zone_names)):
|
||||
filtered_members = filter(
|
||||
lambda x: x not in zone_members,
|
||||
cfgmap_from_fabric['zones'][zone_name])
|
||||
|
||||
# The assumption here is that initiator is always
|
||||
# there in the zone as it is 'initiator' policy.
|
||||
# We find the filtered list and if it is non-empty,
|
||||
# add initiator to it and update zone if filtered
|
||||
# list is empty, we remove that zone.
|
||||
LOG.debug("Zone delete - I mode: filtered targets: %s",
|
||||
filtered_members)
|
||||
if filtered_members:
|
||||
filtered_members.append(formatted_initiator)
|
||||
LOG.debug("Filtered zone members to update: %s",
|
||||
filtered_members)
|
||||
zone_map[zone_name] = filtered_members
|
||||
LOG.debug("Filtered zone Map to update: %s",
|
||||
zone_map)
|
||||
else:
|
||||
zones_to_delete.append(zone_name)
|
||||
else:
|
||||
LOG.info(_LI("Zoning Policy: %s, not recognized"),
|
||||
zoning_policy)
|
||||
LOG.debug("Final Zone map to update: %s", zone_map)
|
||||
LOG.debug("Final Zone list to delete: %s", zones_to_delete)
|
||||
conn = None
|
||||
try:
|
||||
conn = importutils.import_object(
|
||||
self.configuration.cisco_sb_connector,
|
||||
ipaddress=fabric_ip,
|
||||
username=fabric_user,
|
||||
password=fabric_pwd,
|
||||
port=fabric_port,
|
||||
vsan=zoning_vsan)
|
||||
# Update zone membership.
|
||||
if zone_map:
|
||||
conn.add_zones(
|
||||
zone_map, self.configuration.cisco_zone_activate,
|
||||
zoning_vsan, cfgmap_from_fabric,
|
||||
statusmap_from_fabric)
|
||||
# Delete zones ~sk.
|
||||
if zones_to_delete:
|
||||
zone_name_string = ''
|
||||
num_zones = len(zones_to_delete)
|
||||
for i in range(0, num_zones):
|
||||
if i == 0:
|
||||
zone_name_string = ('%s%s' % (
|
||||
zone_name_string,
|
||||
zones_to_delete[i]))
|
||||
else:
|
||||
zone_name_string = ('%s%s%s' % (
|
||||
zone_name_string, ';',
|
||||
zones_to_delete[i]))
|
||||
|
||||
conn.delete_zones(zone_name_string,
|
||||
self.configuration.
|
||||
cisco_zone_activate,
|
||||
zoning_vsan, cfgmap_from_fabric,
|
||||
statusmap_from_fabric)
|
||||
conn.cleanup()
|
||||
except Exception:
|
||||
msg = _("Failed to update or delete zoning configuration")
|
||||
LOG.exception(msg)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
LOG.debug("Zones deleted successfully: %s", zone_map)
|
||||
else:
|
||||
LOG.debug("Zoning session exists VSAN: %s", zoning_vsan)
|
||||
|
||||
def get_san_context(self, target_wwn_list):
|
||||
"""Lookup SAN context for visible end devices.
|
||||
|
||||
Look up each SAN configured and return a map of SAN (fabric IP) to
|
||||
list of target WWNs visible to the fabric.
|
||||
"""
|
||||
formatted_target_list = []
|
||||
fabric_map = {}
|
||||
fabrics = [x.strip() for x in self.
|
||||
configuration.fc_fabric_names.split(',')]
|
||||
LOG.debug("Fabric List: %s", fabrics)
|
||||
LOG.debug("Target wwn List: %s", target_wwn_list)
|
||||
if len(fabrics) > 0:
|
||||
for t in target_wwn_list:
|
||||
formatted_target_list.append(
|
||||
zm_utils.get_formatted_wwn(t.lower()))
|
||||
LOG.debug("Formatted Target wwn List: %s", formatted_target_list)
|
||||
for fabric_name in fabrics:
|
||||
fabric_ip = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_address')
|
||||
fabric_user = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_user')
|
||||
fabric_pwd = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_password')
|
||||
fabric_port = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_fc_fabric_port')
|
||||
zoning_vsan = self.fabric_configs[fabric_name].safe_get(
|
||||
'cisco_zoning_vsan')
|
||||
|
||||
# Get name server data from fabric and get the targets
|
||||
# logged in.
|
||||
nsinfo = None
|
||||
try:
|
||||
conn = importutils.import_object(
|
||||
self.configuration.cisco_sb_connector,
|
||||
ipaddress=fabric_ip,
|
||||
username=fabric_user,
|
||||
password=fabric_pwd, port=fabric_port,
|
||||
vsan=zoning_vsan)
|
||||
nsinfo = conn.get_nameserver_info()
|
||||
LOG.debug("show fcns database info from fabric: %s",
|
||||
nsinfo)
|
||||
conn.cleanup()
|
||||
except exception.CiscoZoningCliException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error getting show fcns database "
|
||||
"info."))
|
||||
except Exception:
|
||||
msg = _("Failed to get show fcns database info.")
|
||||
LOG.exception(msg)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
visible_targets = filter(
|
||||
lambda x: x in formatted_target_list, nsinfo)
|
||||
|
||||
if visible_targets:
|
||||
LOG.info(_LI("Filtered targets for SAN is: %s"),
|
||||
{fabric_name: visible_targets})
|
||||
# getting rid of the ':' before returning
|
||||
for idx, elem in enumerate(visible_targets):
|
||||
visible_targets[idx] = six.text_type(
|
||||
visible_targets[idx]).replace(':', '')
|
||||
fabric_map[fabric_name] = visible_targets
|
||||
else:
|
||||
LOG.debug("No targets are in the fcns info for SAN %s",
|
||||
fabric_name)
|
||||
LOG.debug("Return SAN context output: %s", fabric_map)
|
||||
return fabric_map
|
||||
|
||||
def get_active_zone_set(self, fabric_ip,
|
||||
fabric_user, fabric_pwd, fabric_port,
|
||||
zoning_vsan):
|
||||
"""Gets active zoneset config for vsan."""
|
||||
cfgmap = {}
|
||||
conn = None
|
||||
try:
|
||||
LOG.debug("Southbound connector: %s",
|
||||
self.configuration.cisco_sb_connector)
|
||||
conn = importutils.import_object(
|
||||
self.configuration.cisco_sb_connector,
|
||||
ipaddress=fabric_ip, username=fabric_user,
|
||||
password=fabric_pwd, port=fabric_port, vsan=zoning_vsan)
|
||||
cfgmap = conn.get_active_zone_set()
|
||||
conn.cleanup()
|
||||
except Exception:
|
||||
msg = _("Failed to access active zoning configuration.")
|
||||
LOG.exception(msg)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
LOG.debug("Active zone set from fabric: %s", cfgmap)
|
||||
return cfgmap
|
||||
|
||||
def get_zoning_status(self, fabric_ip, fabric_user, fabric_pwd,
|
||||
fabric_port, zoning_vsan):
|
||||
"""Gets zoneset status and mode."""
|
||||
statusmap = {}
|
||||
conn = None
|
||||
try:
|
||||
LOG.debug("Southbound connector: %s",
|
||||
self.configuration.cisco_sb_connector)
|
||||
conn = importutils.import_object(
|
||||
self.configuration.cisco_sb_connector,
|
||||
ipaddress=fabric_ip, username=fabric_user,
|
||||
password=fabric_pwd, port=fabric_port, vsan=zoning_vsan)
|
||||
statusmap = conn.get_zoning_status()
|
||||
conn.cleanup()
|
||||
except Exception:
|
||||
msg = _("Failed to access zoneset status:%s")
|
||||
LOG.exception(msg)
|
||||
raise exception.FCZoneDriverException(msg)
|
||||
LOG.debug("Zoneset status from fabric: %s", statusmap)
|
||||
return statusmap
|
@ -1,32 +0,0 @@
|
||||
# (c) Copyright 2014 Cisco Systems 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.
|
||||
#
|
||||
|
||||
|
||||
"""
|
||||
Common constants used by Cisco FC Zone Driver.
|
||||
"""
|
||||
ACTIVE_ZONE_CONFIG = 'active_zone_config'
|
||||
CFG_ZONESET = 'zoneset'
|
||||
CFG_ZONE = 'zone'
|
||||
CFG_ZONE_MEMBER = 'pwwn'
|
||||
CFG_ZONES = 'zones'
|
||||
|
||||
"""
|
||||
CLI Commands for FC zoning operations.
|
||||
"""
|
||||
GET_ACTIVE_ZONE_CFG = 'show zoneset active vsan '
|
||||
FCNS_SHOW = 'show fcns database vsan '
|
||||
GET_ZONE_STATUS = 'show zone status vsan '
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
upgrade:
|
||||
- The Cisco FC Zone Manager driver is no longer supported
|
||||
and not included in-tree.
|
Loading…
Reference in New Issue
Block a user