1# Copyright (c) 2016 EMC Corporation 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16 17"""Driver for EMC CoprHD iSCSI volumes.""" 18 19from oslo_log import log as logging 20 21from cinder import exception 22from cinder.i18n import _ 23from cinder import interface 24from cinder.volume import driver 25from cinder.volume.drivers.coprhd import common as coprhd_common 26from cinder.volume import utils as volume_utils 27 28LOG = logging.getLogger(__name__) 29 30 31@interface.volumedriver 32class EMCCoprHDISCSIDriver(driver.ISCSIDriver): 33 """CoprHD iSCSI Driver.""" 34 VERSION = "3.0.0.0" 35 36 # ThirdPartySystems wiki page name 37 CI_WIKI_NAME = "EMC_CoprHD_CI" 38 39 def __init__(self, *args, **kwargs): 40 super(EMCCoprHDISCSIDriver, self).__init__(*args, **kwargs) 41 self.common = self._get_common_driver() 42 43 def _get_common_driver(self): 44 return coprhd_common.EMCCoprHDDriverCommon( 45 protocol='iSCSI', 46 default_backend_name=self.__class__.__name__, 47 configuration=self.configuration) 48 49 def check_for_setup_error(self): 50 self.common.check_for_setup_error() 51 52 def create_volume(self, volume): 53 """Creates a Volume.""" 54 self.common.create_volume(volume, self) 55 self.common.set_volume_tags(volume, ['_obj_volume_type']) 56 57 def create_cloned_volume(self, volume, src_vref): 58 """Creates a cloned Volume.""" 59 self.common.create_cloned_volume(volume, src_vref) 60 self.common.set_volume_tags(volume, ['_obj_volume_type']) 61 62 def create_volume_from_snapshot(self, volume, snapshot): 63 """Creates a volume from a snapshot.""" 64 self.common.create_volume_from_snapshot(snapshot, volume) 65 self.common.set_volume_tags(volume, ['_obj_volume_type']) 66 67 def extend_volume(self, volume, new_size): 68 """expands the size of the volume.""" 69 self.common.expand_volume(volume, new_size) 70 71 def delete_volume(self, volume): 72 """Deletes a volume.""" 73 self.common.delete_volume(volume) 74 75 def create_snapshot(self, snapshot): 76 """Creates a snapshot.""" 77 self.common.create_snapshot(snapshot) 78 79 def delete_snapshot(self, snapshot): 80 """Deletes a snapshot.""" 81 self.common.delete_snapshot(snapshot) 82 83 def ensure_export(self, context, volume): 84 """Driver entry point to get the export info for an existing volume.""" 85 pass 86 87 def create_export(self, context, volume, connector=None): 88 """Driver entry point to get the export info for a new volume.""" 89 pass 90 91 def remove_export(self, context, volume): 92 """Driver entry point to remove an export for a volume.""" 93 pass 94 95 def create_group(self, context, group): 96 """Creates a group.""" 97 if volume_utils.is_group_a_cg_snapshot_type(group): 98 return self.common.create_consistencygroup(context, group) 99 100 # If the group is not consistency group snapshot enabled, then 101 # we shall rely on generic volume group implementation 102 raise NotImplementedError() 103 104 def create_group_from_src(self, ctxt, group, volumes, 105 group_snapshot=None, snapshots=None, 106 source_group=None, source_vols=None): 107 """Creates a group from source.""" 108 if volume_utils.is_group_a_cg_snapshot_type(group): 109 message = _("create group from source is not supported " 110 "for CoprHD if the group type supports " 111 "consistent group snapshot.") 112 raise exception.VolumeBackendAPIException(data=message) 113 else: 114 raise NotImplementedError() 115 116 def update_group(self, context, group, add_volumes=None, 117 remove_volumes=None): 118 """Updates volumes in group.""" 119 if volume_utils.is_group_a_cg_snapshot_type(group): 120 return self.common.update_consistencygroup(group, add_volumes, 121 remove_volumes) 122 123 # If the group is not consistency group snapshot enabled, then 124 # we shall rely on generic volume group implementation 125 raise NotImplementedError() 126 127 def delete_group(self, context, group, volumes): 128 """Deletes a group.""" 129 if volume_utils.is_group_a_cg_snapshot_type(group): 130 return self.common.delete_consistencygroup(context, group, volumes) 131 132 # If the group is not consistency group snapshot enabled, then 133 # we shall rely on generic volume group implementation 134 raise NotImplementedError() 135 136 def create_group_snapshot(self, context, group_snapshot, snapshots): 137 """Creates a group snapshot.""" 138 if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): 139 LOG.debug("creating a group snapshot") 140 return self.common.create_cgsnapshot(group_snapshot, snapshots) 141 142 # If the group is not consistency group snapshot enabled, then 143 # we shall rely on generic volume group implementation 144 raise NotImplementedError() 145 146 def delete_group_snapshot(self, context, group_snapshot, snapshots): 147 """Deletes a group snapshot.""" 148 if volume_utils.is_group_a_cg_snapshot_type(group_snapshot): 149 return self.common.delete_cgsnapshot(group_snapshot, snapshots) 150 151 # If the group is not consistency group snapshot enabled, then 152 # we shall rely on generic volume group implementation 153 raise NotImplementedError() 154 155 def check_for_export(self, context, volume_id): 156 """Make sure volume is exported.""" 157 pass 158 159 def initialize_connection(self, volume, connector): 160 """Initializes the connection and returns connection info.""" 161 162 initiator_ports = [] 163 initiator_ports.append(connector['initiator']) 164 itls = self.common.initialize_connection(volume, 165 'iSCSI', 166 initiator_ports, 167 connector['host']) 168 properties = {} 169 properties['target_discovered'] = False 170 properties['volume_id'] = volume.id 171 if itls: 172 properties['target_iqn'] = itls[0]['target']['port'] 173 properties['target_portal'] = '%s:%s' % ( 174 itls[0]['target']['ip_address'], 175 itls[0]['target']['tcp_port']) 176 properties['target_lun'] = itls[0]['hlu'] 177 178 auth = None 179 try: 180 auth = volume.provider_auth 181 except AttributeError: 182 pass 183 184 if auth: 185 (auth_method, auth_username, auth_secret) = auth.split() 186 properties['auth_method'] = auth_method 187 properties['auth_username'] = auth_username 188 properties['auth_password'] = auth_secret 189 190 LOG.debug("ISCSI properties: %s", properties) 191 return { 192 'driver_volume_type': 'iscsi', 193 'data': properties, 194 } 195 196 def terminate_connection(self, volume, connector, **kwargs): 197 """Disallow connection from connector.""" 198 199 init_ports = [] 200 init_ports.append(connector['initiator']) 201 self.common.terminate_connection(volume, 202 'iSCSI', 203 init_ports, 204 connector['host']) 205 206 def get_volume_stats(self, refresh=False): 207 """Get volume status. 208 209 If 'refresh' is True, run update the stats first. 210 """ 211 if refresh: 212 self.update_volume_stats() 213 214 return self._stats 215 216 def update_volume_stats(self): 217 """Retrieve stats info from virtual pool/virtual array.""" 218 LOG.debug("Updating volume stats") 219 self._stats = self.common.update_volume_stats() 220 221 def retype(self, ctxt, volume, new_type, diff, host): 222 """Change the volume type.""" 223 return self.common.retype(ctxt, volume, new_type, diff, host) 224