Provide Kaminario K2 all-flash array FC driver
The purpose is to provide cinder driver for Kaminario K2 all-flash FC array. It will include the minimum set of features required by the Newton(N) release. run-Kaminario K2FC CI DocImpact Implements: blueprint kaminario-fc-cinder-driver Co-Authored-By: Nikesh Mahalka <Nikesh.Mahalka.ctr@kaminario.com> Co-Authored-By: Sreedhar Varma<Sreedhar.Varma.ctr@kaminario.com> Co-Authored-By: Ido Benda<Ido.Benda@kaminario.com> Change-Id: Ib8154c720537a86a3fa9c8f7429e27f5ee8db151
This commit is contained in:
parent
c897d350e3
commit
bfae22b69b
@ -22,12 +22,14 @@ from cinder.tests.unit import fake_snapshot
|
||||
from cinder.tests.unit import fake_volume
|
||||
from cinder import utils
|
||||
from cinder.volume import configuration
|
||||
from cinder.volume.drivers.kaminario import kaminario_fc
|
||||
from cinder.volume.drivers.kaminario import kaminario_iscsi
|
||||
from cinder.volume import utils as vol_utils
|
||||
|
||||
|
||||
CONNECTOR = {'initiator': 'iqn.1993-08.org.debian:01:12aa12aa12aa',
|
||||
'ip': '192.168.2.5', 'platform': 'x86_64', 'host': 'test-k2',
|
||||
'wwpns': ['12341a2a00001234', '12341a2a00001235'],
|
||||
'wwnns': ['12351a2a00001234', '12361a2a00001234'],
|
||||
'os_type': 'linux2', 'multipath': False}
|
||||
|
||||
|
||||
@ -43,6 +45,7 @@ class FakeSaveObject(FakeK2Obj):
|
||||
self.iscsi_qualified_target_name = "xyztlnxyz"
|
||||
self.snapshot = FakeK2Obj()
|
||||
self.name = 'test'
|
||||
self.pwwn = '50024f4053300300'
|
||||
|
||||
def save(self):
|
||||
return FakeSaveObject()
|
||||
@ -261,3 +264,30 @@ class TestKaminarioISCSI(test.TestCase):
|
||||
"""Test k2_initialize_connection."""
|
||||
result = self.driver.k2_initialize_connection(self.vol, CONNECTOR)
|
||||
self.assertEqual(548, result)
|
||||
|
||||
|
||||
class TestKaminarioFC(TestKaminarioISCSI):
|
||||
|
||||
def _setup_driver(self):
|
||||
self.driver = (kaminario_fc.
|
||||
KaminarioFCDriver(configuration=self.conf))
|
||||
device = mock.Mock(return_value={'device': {'path': '/dev'}})
|
||||
self.driver._connect_device = device
|
||||
self.driver.client = FakeKrest()
|
||||
self.driver._lookup_service = mock.Mock()
|
||||
|
||||
def test_initialize_connection(self):
|
||||
"""Test initialize_connection."""
|
||||
conn_info = self.driver.initialize_connection(self.vol, CONNECTOR)
|
||||
self.assertIn('data', conn_info)
|
||||
self.assertIn('target_wwn', conn_info['data'])
|
||||
|
||||
def test_get_target_info(self):
|
||||
"""Test get_target_info."""
|
||||
target_wwpn = self.driver.get_target_info()
|
||||
self.assertEqual(['50024f4053300300'], target_wwpn)
|
||||
|
||||
def test_terminate_connection(self):
|
||||
"""Test terminate_connection."""
|
||||
result = self.driver.terminate_connection(self.vol, CONNECTOR)
|
||||
self.assertIn('data', result)
|
||||
|
163
cinder/volume/drivers/kaminario/kaminario_fc.py
Normal file
163
cinder/volume/drivers/kaminario/kaminario_fc.py
Normal file
@ -0,0 +1,163 @@
|
||||
# Copyright (c) 2016 by Kaminario Technologies, Ltd.
|
||||
# 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.
|
||||
"""Volume driver for Kaminario K2 all-flash arrays."""
|
||||
import six
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE
|
||||
from cinder.volume.drivers.kaminario import kaminario_common as common
|
||||
from cinder.zonemanager import utils as fczm_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
kaminario_logger = common.kaminario_logger
|
||||
|
||||
|
||||
class KaminarioFCDriver(common.KaminarioCinderDriver):
|
||||
"""Kaminario K2 FC Volume Driver."""
|
||||
|
||||
@kaminario_logger
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(KaminarioFCDriver, self).__init__(*args, **kwargs)
|
||||
self._protocol = 'FC'
|
||||
self.lookup_service = fczm_utils.create_lookup_service()
|
||||
|
||||
@fczm_utils.AddFCZone
|
||||
@kaminario_logger
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Attach K2 volume to host."""
|
||||
# Check wwpns in host connector.
|
||||
if not connector['wwpns']:
|
||||
msg = _("No wwpns found in host connector.")
|
||||
LOG.error(msg)
|
||||
raise exception.KaminarioCinderDriverException(reason=msg)
|
||||
# Get target wwpns.
|
||||
target_wwpns = self.get_target_info()
|
||||
# Map volume.
|
||||
lun = self.k2_initialize_connection(volume, connector)
|
||||
# Create initiator-target mapping.
|
||||
target_wwpns, init_target_map = self._build_initiator_target_map(
|
||||
connector, target_wwpns)
|
||||
# Return target volume information.
|
||||
return {'driver_volume_type': 'fibre_channel',
|
||||
'data': {"target_discovered": True,
|
||||
"target_lun": lun,
|
||||
"target_wwn": target_wwpns,
|
||||
"initiator_target_map": init_target_map}}
|
||||
|
||||
@fczm_utils.RemoveFCZone
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
super(KaminarioFCDriver, self).terminate_connection(volume, connector)
|
||||
properties = {"driver_volume_type": "fibre_channel", "data": {}}
|
||||
host_name = self.get_initiator_host_name(connector)
|
||||
host_rs = self.client.search("hosts", name=host_name)
|
||||
# In terminate_connection, host_entry is deleted if host
|
||||
# is not attached to any volume
|
||||
if host_rs.total == 0:
|
||||
# Get target wwpns.
|
||||
target_wwpns = self.get_target_info()
|
||||
target_wwpns, init_target_map = self._build_initiator_target_map(
|
||||
connector, target_wwpns)
|
||||
properties["data"] = {"target_wwn": target_wwpns,
|
||||
"initiator_target_map": init_target_map}
|
||||
return properties
|
||||
|
||||
@kaminario_logger
|
||||
def get_target_info(self):
|
||||
LOG.debug("Searching target wwpns in K2.")
|
||||
fc_ports_rs = self.client.search("system/fc_ports")
|
||||
target_wwpns = []
|
||||
if hasattr(fc_ports_rs, 'hits') and fc_ports_rs.total != 0:
|
||||
for port in fc_ports_rs.hits:
|
||||
if port.pwwn:
|
||||
target_wwpns.append((port.pwwn).replace(':', ''))
|
||||
if not target_wwpns:
|
||||
msg = _("Unable to get FC target wwpns from K2.")
|
||||
LOG.error(msg)
|
||||
raise exception.KaminarioCinderDriverException(reason=msg)
|
||||
return target_wwpns
|
||||
|
||||
@kaminario_logger
|
||||
def _get_host_object(self, connector):
|
||||
host_name = self.get_initiator_host_name(connector)
|
||||
LOG.debug("Searching initiator hostname: %s in K2.", host_name)
|
||||
host_rs = self.client.search("hosts", name=host_name)
|
||||
host_wwpns = connector['wwpns']
|
||||
if host_rs.total == 0:
|
||||
try:
|
||||
LOG.debug("Creating initiator hostname: %s in K2.", host_name)
|
||||
host = self.client.new("hosts", name=host_name,
|
||||
type="Linux").save()
|
||||
except Exception as ex:
|
||||
LOG.exception(_LE("Unable to create host : %s in K2."),
|
||||
host_name)
|
||||
raise exception.KaminarioCinderDriverException(
|
||||
reason=six.text_type(ex.message))
|
||||
else:
|
||||
# Use existing host.
|
||||
LOG.debug("Use existing initiator hostname: %s in K2.", host_name)
|
||||
host = host_rs.hits[0]
|
||||
# Adding host wwpn.
|
||||
for wwpn in host_wwpns:
|
||||
wwpn = ":".join([wwpn[i:i + 2] for i in range(0, len(wwpn), 2)])
|
||||
if self.client.search("host_fc_ports", pwwn=wwpn,
|
||||
host=host).total == 0:
|
||||
LOG.debug("Adding wwpn: %(wwpn)s to host: "
|
||||
"%(host)s in K2.", {'wwpn': wwpn,
|
||||
'host': host_name})
|
||||
try:
|
||||
self.client.new("host_fc_ports", pwwn=wwpn,
|
||||
host=host).save()
|
||||
except Exception as ex:
|
||||
if host_rs.total == 0:
|
||||
self._delete_host_by_name(host_name)
|
||||
LOG.exception(_LE("Unable to add wwpn : %(wwpn)s to "
|
||||
"host: %(host)s in K2."),
|
||||
{'wwpn': wwpn, 'host': host_name})
|
||||
raise exception.KaminarioCinderDriverException(
|
||||
reason=six.text_type(ex.message))
|
||||
return host, host_rs, host_name
|
||||
|
||||
@kaminario_logger
|
||||
def _build_initiator_target_map(self, connector, all_target_wwns):
|
||||
"""Build the target_wwns and the initiator target map."""
|
||||
target_wwns = []
|
||||
init_targ_map = {}
|
||||
|
||||
if self.lookup_service is not None:
|
||||
# use FC san lookup.
|
||||
dev_map = self.lookup_service.get_device_mapping_from_network(
|
||||
connector['wwpns'],
|
||||
all_target_wwns)
|
||||
|
||||
for fabric_name in dev_map:
|
||||
fabric = dev_map[fabric_name]
|
||||
target_wwns += fabric['target_port_wwn_list']
|
||||
for initiator in fabric['initiator_port_wwn_list']:
|
||||
if initiator not in init_targ_map:
|
||||
init_targ_map[initiator] = []
|
||||
init_targ_map[initiator] += fabric['target_port_wwn_list']
|
||||
init_targ_map[initiator] = list(set(
|
||||
init_targ_map[initiator]))
|
||||
target_wwns = list(set(target_wwns))
|
||||
else:
|
||||
initiator_wwns = connector['wwpns']
|
||||
target_wwns = all_target_wwns
|
||||
|
||||
for initiator in initiator_wwns:
|
||||
init_targ_map[initiator] = target_wwns
|
||||
|
||||
return target_wwns, init_targ_map
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add FC cinder volume driver for Kaminario K2 all-flash arrays.
|
||||
|
Loading…
x
Reference in New Issue
Block a user