Fuel plugin that enables to configure multiple Cinder backend support for Kaminario K2 All-Flash Array
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kaminario_fc.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # Copyright (c) 2016 by Kaminario Technologies, Ltd.
  2. # All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. """Volume driver for Kaminario K2 all-flash arrays."""
  16. import six
  17. from oslo_log import log as logging
  18. from cinder import coordination
  19. from cinder import exception
  20. from cinder.i18n import _, _LE
  21. from cinder.objects import fields
  22. from cinder.volume.drivers.kaminario import kaminario_common as common
  23. from cinder.zonemanager import utils as fczm_utils
  24. LOG = logging.getLogger(__name__)
  25. kaminario_logger = common.kaminario_logger
  26. class KaminarioFCDriver(common.KaminarioCinderDriver):
  27. """Kaminario K2 FC Volume Driver.
  28. Version history:
  29. 1.0 - Initial driver
  30. 1.1 - Added manage/unmanage and extra-specs support for nodedup
  31. 1.2 - Added replication support
  32. 1.3 - Added retype support
  33. """
  34. VERSION = '1.3'
  35. # ThirdPartySystems wiki page name
  36. CI_WIKI_NAME = "Kaminario_K2_CI"
  37. @kaminario_logger
  38. def __init__(self, *args, **kwargs):
  39. super(KaminarioFCDriver, self).__init__(*args, **kwargs)
  40. self._protocol = 'FC'
  41. self.lookup_service = fczm_utils.create_lookup_service()
  42. @fczm_utils.AddFCZone
  43. @kaminario_logger
  44. @coordination.synchronized('{self.k2_lock_name}')
  45. def initialize_connection(self, volume, connector):
  46. """Attach K2 volume to host."""
  47. # Check wwpns in host connector.
  48. if not connector.get('wwpns'):
  49. msg = _("No wwpns found in host connector.")
  50. LOG.error(msg)
  51. raise exception.KaminarioCinderDriverException(reason=msg)
  52. # Get target wwpns.
  53. target_wwpns = self.get_target_info(volume)
  54. # Map volume.
  55. lun = self.k2_initialize_connection(volume, connector)
  56. # Create initiator-target mapping.
  57. target_wwpns, init_target_map = self._build_initiator_target_map(
  58. connector, target_wwpns)
  59. # Return target volume information.
  60. return {'driver_volume_type': 'fibre_channel',
  61. 'data': {"target_discovered": True,
  62. "target_lun": lun,
  63. "target_wwn": target_wwpns,
  64. "initiator_target_map": init_target_map}}
  65. @fczm_utils.RemoveFCZone
  66. @kaminario_logger
  67. @coordination.synchronized('{self.k2_lock_name}')
  68. def terminate_connection(self, volume, connector, **kwargs):
  69. super(KaminarioFCDriver, self).terminate_connection(volume, connector)
  70. properties = {"driver_volume_type": "fibre_channel", "data": {}}
  71. host_name = self.get_initiator_host_name(connector)
  72. host_rs = self.client.search("hosts", name=host_name)
  73. # In terminate_connection, host_entry is deleted if host
  74. # is not attached to any volume
  75. if host_rs.total == 0:
  76. # Get target wwpns.
  77. target_wwpns = self.get_target_info(volume)
  78. target_wwpns, init_target_map = self._build_initiator_target_map(
  79. connector, target_wwpns)
  80. properties["data"] = {"target_wwn": target_wwpns,
  81. "initiator_target_map": init_target_map}
  82. return properties
  83. @kaminario_logger
  84. def get_target_info(self, volume):
  85. rep_status = fields.ReplicationStatus.FAILED_OVER
  86. if (hasattr(volume, 'replication_status') and
  87. volume.replication_status == rep_status):
  88. self.client = self.target
  89. LOG.debug("Searching target wwpns in K2.")
  90. fc_ports_rs = self.client.search("system/fc_ports")
  91. target_wwpns = []
  92. if hasattr(fc_ports_rs, 'hits') and fc_ports_rs.total != 0:
  93. for port in fc_ports_rs.hits:
  94. if port.pwwn:
  95. target_wwpns.append((port.pwwn).replace(':', ''))
  96. if not target_wwpns:
  97. msg = _("Unable to get FC target wwpns from K2.")
  98. LOG.error(msg)
  99. raise exception.KaminarioCinderDriverException(reason=msg)
  100. return target_wwpns
  101. @kaminario_logger
  102. def _get_host_object(self, connector):
  103. host_name = self.get_initiator_host_name(connector)
  104. LOG.debug("Searching initiator hostname: %s in K2.", host_name)
  105. host_rs = self.client.search("hosts", name=host_name)
  106. host_wwpns = connector['wwpns']
  107. if host_rs.total == 0:
  108. try:
  109. LOG.debug("Creating initiator hostname: %s in K2.", host_name)
  110. host = self.client.new("hosts", name=host_name,
  111. type="Linux").save()
  112. except Exception as ex:
  113. LOG.exception(_LE("Unable to create host : %s in K2."),
  114. host_name)
  115. raise exception.KaminarioCinderDriverException(
  116. reason=six.text_type(ex.message))
  117. else:
  118. # Use existing host.
  119. LOG.debug("Use existing initiator hostname: %s in K2.", host_name)
  120. host = host_rs.hits[0]
  121. # Adding host wwpn.
  122. for wwpn in host_wwpns:
  123. wwpn = ":".join([wwpn[i:i + 2] for i in range(0, len(wwpn), 2)])
  124. if self.client.search("host_fc_ports", pwwn=wwpn,
  125. host=host).total == 0:
  126. LOG.debug("Adding wwpn: %(wwpn)s to host: "
  127. "%(host)s in K2.", {'wwpn': wwpn,
  128. 'host': host_name})
  129. try:
  130. self.client.new("host_fc_ports", pwwn=wwpn,
  131. host=host).save()
  132. except Exception as ex:
  133. if host_rs.total == 0:
  134. self._delete_host_by_name(host_name)
  135. LOG.exception(_LE("Unable to add wwpn : %(wwpn)s to "
  136. "host: %(host)s in K2."),
  137. {'wwpn': wwpn, 'host': host_name})
  138. raise exception.KaminarioCinderDriverException(
  139. reason=six.text_type(ex.message))
  140. return host, host_rs, host_name
  141. @kaminario_logger
  142. def _build_initiator_target_map(self, connector, all_target_wwns):
  143. """Build the target_wwns and the initiator target map."""
  144. target_wwns = []
  145. init_targ_map = {}
  146. if self.lookup_service is not None:
  147. # use FC san lookup.
  148. dev_map = self.lookup_service.get_device_mapping_from_network(
  149. connector.get('wwpns'),
  150. all_target_wwns)
  151. for fabric_name in dev_map:
  152. fabric = dev_map[fabric_name]
  153. target_wwns += fabric['target_port_wwn_list']
  154. for initiator in fabric['initiator_port_wwn_list']:
  155. if initiator not in init_targ_map:
  156. init_targ_map[initiator] = []
  157. init_targ_map[initiator] += fabric['target_port_wwn_list']
  158. init_targ_map[initiator] = list(set(
  159. init_targ_map[initiator]))
  160. target_wwns = list(set(target_wwns))
  161. else:
  162. initiator_wwns = connector.get('wwpns', [])
  163. target_wwns = all_target_wwns
  164. for initiator in initiator_wwns:
  165. init_targ_map[initiator] = target_wwns
  166. return target_wwns, init_targ_map