def __init__(self, protocol, default_backend_name, configuration=None): self.protocol = protocol self.configuration = configuration self.configuration.append_config_values(volume_opts) vipr_utils.COOKIE = None # instantiate a few vipr cli objects for later use self.volume_obj = Volume(self.configuration.vipr_hostname, self.configuration.vipr_port) self.exportgroup_obj = ExportGroup(self.configuration.vipr_hostname, self.configuration.vipr_port) self.host_obj = Host(self.configuration.vipr_hostname, self.configuration.vipr_port) self.hostinitiator_obj = HostInitiator(self.configuration.vipr_hostname, self.configuration.vipr_port) self.varray_obj = VirtualArray(self.configuration.vipr_hostname, self.configuration.vipr_port) self.stats = {'driver_version': '1.0', 'free_capacity_gb': 'unknown', 'reserved_percentage': '0', 'storage_protocol': protocol, 'total_capacity_gb': 'unknown', 'vendor_name': 'EMC', 'volume_backend_name': self.configuration.volume_backend_name or default_backend_name}
class EMCViPRDriverCommon(): OPENSTACK_TAG = 'OpenStack' def __init__(self, protocol, default_backend_name, configuration=None): self.protocol = protocol self.configuration = configuration self.configuration.append_config_values(volume_opts) vipr_utils.COOKIE = None # instantiate a few vipr cli objects for later use self.volume_obj = Volume(self.configuration.vipr_hostname, self.configuration.vipr_port) self.exportgroup_obj = ExportGroup(self.configuration.vipr_hostname, self.configuration.vipr_port) self.host_obj = Host(self.configuration.vipr_hostname, self.configuration.vipr_port) self.hostinitiator_obj = HostInitiator(self.configuration.vipr_hostname, self.configuration.vipr_port) self.varray_obj = VirtualArray(self.configuration.vipr_hostname, self.configuration.vipr_port) self.stats = {'driver_version': '1.0', 'free_capacity_gb': 'unknown', 'reserved_percentage': '0', 'storage_protocol': protocol, 'total_capacity_gb': 'unknown', 'vendor_name': 'EMC', 'volume_backend_name': self.configuration.volume_backend_name or default_backend_name} def check_for_setup_error(self): # validate all of the vipr_* configuration values if (self.configuration.vipr_hostname is None): message="vipr_hostname is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) if (self.configuration.vipr_port is None): message="vipr_port is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) if (self.configuration.vipr_username is None): message="vipr_username is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) if (self.configuration.vipr_password is None): message="vipr_password is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) if (self.configuration.vipr_tenant is None): message="vipr_tenant is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) if (self.configuration.vipr_project is None): message="vipr_project is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) if (self.configuration.vipr_varray is None): message="vipr_varray is not set in cinder configuration" raise exception.VolumeBackendAPIException(data=message) # check the rpc_response_timeout value, should be greater than 300 if (self.configuration.rpc_response_timeout is None or self.configuration.rpc_response_timeout<300): LOG.warn(_("rpc_response_time should be set to at least 300 seconds")) def authenticate_user(self): global AUTHENTICATED # we should check to see if we are already authenticated before blindly doing it again if (AUTHENTICATED == False ): obj = Authentication(self.configuration.vipr_hostname, self.configuration.vipr_port) # cookiedir = os.getcwd() cookiedir=self.configuration.vipr_cookiedir obj.authenticate_user(self.configuration.vipr_username, self.configuration.vipr_password, cookiedir, None) AUTHENTICATED = True @retry_wrapper def create_volume(self, vol): self.authenticate_user() name = self._get_volume_name(vol) size = int(vol['size']) * 1073741824 vpool = self._get_vpool(vol) self.vpool = vpool['ViPR:VPOOL'] try: res = self.volume_obj.create(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project, name, size, self.configuration.vipr_varray, self.vpool, protocol=None, # no longer specified in volume creation sync=True, number_of_volumes=1, thin_provisioned=None, # no longer specified in volume creation protection=None, protection_varrays=None, consistent_volume_label=None, consistencygroup=None ) except SOSError as e: if(e.err_code == SOSError.SOS_FAILURE_ERR): raise SOSError(SOSError.SOS_FAILURE_ERR, "Volume " + name + ": Tag failed\n" + e.err_text) else: raise e @retry_wrapper def setTags(self, vol): self.authenticate_user() name = self._get_volume_name(vol) # first, get the current tags that start with the OPENSTACK_TAG eyecatcher removeTags=[] currentTags = self.volume_obj.getTags(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project + "/" + name) for cTag in currentTags: if (cTag.startswith(self.OPENSTACK_TAG)): removeTags.append(cTag) try: if (len(removeTags)>0): self.volume_obj.modifyTags(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project + "/" + name, None, removeTags) except SOSError as e: if (e.err_code == SOSError.SOS_FAILURE_ERR): LOG.debug("SOSError adding the tag: " + e.err_text) # now add the tags for the volume addTags=[] # put all the openstack volume properties into the ViPR volume for prop, value in vars(vol).iteritems(): try: # don't put the status in, it's always the status before the current transaction if (not prop.startswith("status")): addTags.append(self.OPENSTACK_TAG+":"+prop+":"+value) except Exception: pass try: self.volume_obj.modifyTags(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project + "/" + name, addTags, None) except SOSError as e: if (e.err_code == SOSError.SOS_FAILURE_ERR): LOG.debug("SOSError adding the tag: " + e.err_text) return self.volume_obj.getTags(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project + "/" + name) @retry_wrapper def create_cloned_volume(self, vol, src_vref): """Creates a clone of the specified volume.""" self.authenticate_user() name = self._get_volume_name(vol) srcname = self._get_volume_name(src_vref) try: res = self.volume_obj.clone(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project, name, number_of_volumes=1, srcname=srcname, sync=True ) except SOSError as e: if(e.err_code == SOSError.SOS_FAILURE_ERR): raise SOSError(SOSError.SOS_FAILURE_ERR, "Volume " + name + ": clone failed\n" + e.err_text) else: raise e @retry_wrapper def delete_volume(self, vol): self.authenticate_user() name = self._get_volume_name(vol) try: self.volume_obj.delete(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project + "/" + name, volume_name_list=None, sync=True) except SOSError as e: if e.err_code == SOSError.NOT_FOUND_ERR: LOG.info("Volume " + name + " no longer exists; volume deletion is considered success.") elif e.err_code == SOSError.SOS_FAILURE_ERR: raise SOSError(SOSError.SOS_FAILURE_ERR, "Volume " + name + ": Delete failed\n" + e.err_text) else: raise e @retry_wrapper def list_volume(self): try: uris = self.volume_obj.list_volumes(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project) if(len(uris) > 0): output = [] for uri in uris: output.append(self.volume_obj.show_by_uri(uri)) return vipr_utils.format_json_object(output) else: return except SOSError as e: raise e @retry_wrapper def create_snapshot(self, snapshot): self.authenticate_user() obj = Snapshot(self.configuration.vipr_hostname, self.configuration.vipr_port) try: snapshotname = snapshot['name'] vol = snapshot['volume'] volumename = self._get_volume_name(vol) projectname = self.configuration.vipr_project tenantname = self.configuration.vipr_tenant storageresType = 'block' storageresTypename = 'volumes' resourceUri = obj.storageResource_query(storageresType, fileshareName=None, volumeName=volumename, cgName=None, project=projectname, tenant=tenantname) inactive = False rptype = None sync = True obj.snapshot_create(storageresType, storageresTypename, resourceUri, snapshotname, inactive, rptype, sync) return except SOSError as e: if (e.err_code == SOSError.SOS_FAILURE_ERR): raise SOSError(SOSError.SOS_FAILURE_ERR, "Snapshot: " + snapshotname + ", Create Failed\n" + e.err_text) else: raise e @retry_wrapper def delete_snapshot(self, snapshot): self.authenticate_user() obj = Snapshot(self.configuration.vipr_hostname, self.configuration.vipr_port) snapshotname = snapshot['name'] try: vol = snapshot['volume'] volumename = self._get_volume_name(vol) projectname = self.configuration.vipr_project tenantname = self.configuration.vipr_tenant storageresType = 'block' storageresTypename = 'volumes' resourceUri = obj.storageResource_query(storageresType, fileshareName=None, volumeName=volumename, cgName=None, project=projectname, tenant=tenantname) if resourceUri is None: LOG.info("Snapshot " + snapshotname + " is not found; snapshot deletion is considered successful.") else: obj.snapshot_delete(storageresType, storageresTypename, resourceUri, snapshotname, sync=True) return except SOSError as e: if (e.err_code == SOSError.SOS_FAILURE_ERR): raise SOSError(SOSError.SOS_FAILURE_ERR, "Snapshot " + snapshotname + ": Delete Failed\n") else: raise e @retry_wrapper def initialize_connection(self, volume, protocol, initiatorNodes, initiatorPorts, hostname): try: self.authenticate_user() volumename = self._get_volume_name(volume) foundgroupname = self._find_exportgroup(initiatorPorts) if (foundgroupname is None): for i in xrange(len(initiatorPorts)): # check if this initiator is contained in any ViPR Host object LOG.debug("checking for initiator port:" + initiatorPorts[i]) foundhostname= self._find_host(initiatorPorts[i]) if (foundhostname is None): hostfound = self._host_exists(hostname) if ( hostfound is None): # create a host so it can be added to the export group hostfound = hostname self.host_obj.create(hostname, platform.system(), hostname, self.configuration.vipr_tenant, project=None, port=None, username=None, passwd=None, usessl=None, osversion=None, cluster=None, datacenter=None, vcenter=None) LOG.info("Created host " + hostname) # add the initiator to the host self.hostinitiator_obj.create(hostfound, protocol, initiatorNodes[i], initiatorPorts[i]); LOG.info("Initiator " + initiatorPorts[i] + " added to host " + hostfound) foundhostname = hostfound else: LOG.info("Found host " + foundhostname) # create an export group for this host foundgroupname = foundhostname + 'SG' # create a unique name foundgroupname = foundgroupname + '-' + ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(6)) res = self.exportgroup_obj.exportgroup_create(foundgroupname, self.configuration.vipr_project, self.configuration.vipr_tenant, self.configuration.vipr_varray, 'Host', foundhostname); LOG.debug("adding the volume to the exportgroup : " +volumename) res = self.exportgroup_obj.exportgroup_add_volumes(foundgroupname, self.configuration.vipr_tenant, self.configuration.vipr_project, volumename, None, None,None, True) return self._find_device_info(volume, initiatorPorts) except SOSError as e: raise SOSError(SOSError.SOS_FAILURE_ERR, "Attach volume (" + self._get_volume_name(volume) + ") to host (" + hostname + ") initiator (" + initiatorPorts[0] + ") failed: " + e.err_text) @retry_wrapper def terminate_connection(self, volume, protocol, initiatorNodes, initiatorPorts, hostname): try: self.authenticate_user() volumename = self._get_volume_name(volume) tenantproject = self.configuration.vipr_tenant+ '/' + self.configuration.vipr_project voldetails = self.volume_obj.show(tenantproject + '/' + volumename) volid = voldetails['id'] # find the exportgroups exports = self.volume_obj.get_exports_by_uri(volid) exportgroups = set() for itl in exports['itl']: itl_port = itl['initiator']['port'] if (itl_port in initiatorPorts): exportgroups.add(itl['export']['id']) for exportgroup in exportgroups: res = self.exportgroup_obj.exportgroup_remove_volumes_by_uri(exportgroup, volid, True, None, None, None, None) else: LOG.info("No export group found for the host: " + hostname + "; this is considered already detached.") except SOSError as e: raise SOSError(SOSError.SOS_FAILURE_ERR, "Detaching volume " + volumename + " from host " + hostname + " failed: " + e.err_text) @retry_wrapper def _find_device_info(self, volume, initiator_ports): ''' Returns the device_info in a list of itls that have the matched initiator (there could be multiple targets, hence a list): [ { "hlu":9, "initiator":{...,"port":"20:00:00:25:B5:49:00:22"}, "export":{...}, "device":{...,"wwn":"600601602B802D00B62236585D0BE311"}, "target":{...,"port":"50:06:01:6A:46:E0:72:EF"}, "san_zone_name":"..." }, { "hlu":9, "initiator":{...,"port":"20:00:00:25:B5:49:00:22"}, "export":{...}, "device":{...,"wwn":"600601602B802D00B62236585D0BE311"}, "target":{...,"port":"50:06:01:62:46:E0:72:EF"}, "san_zone_name":"..." } ] ''' volumename = self._get_volume_name(volume) fullname = self.configuration.vipr_project + '/' + volumename vol_uri = self.volume_obj.volume_query(fullname) ''' The itl info shall be available at the first try since now export is a synchronous call. We are trying a few more times to accommodate any delay on filling in the itl info after the export task is completed. ''' itls = [] for x in xrange(10): exports = self.volume_obj.get_exports_by_uri(vol_uri) LOG.debug(_("Volume exports: %s") % exports) for itl in exports['itl']: itl_port = itl['initiator']['port'] if (itl_port in initiator_ports): found_device_number = itl['hlu'] if found_device_number is not None and found_device_number != '-1': # 0 is a valid number for found_device_number. # Only loop if it is None or -1 LOG.debug(_("Found Device Number: %(found_device_number)s") % (locals())) itls.append(itl) if itls: break else: LOG.debug(_("Device Number not found yet. Retrying after 10 seconds...")) time.sleep(10) if itls is None: # No device number found after 10 tries; return an empty itl LOG.info(_("No device number has been found after 10 tries; this likely indicates an unsuccessful attach of volume %(volumename) to initiator %(initiatorPort).") % (locals())) return itls def _get_volume_name(self, vol): try: name = vol['display_name'] except: name = None if (name is None or len(name) == 0): name = vol['name'] return name def _get_vpool(self, volume): vpool = {} ctxt = context.get_admin_context() type_id = volume['volume_type_id'] if type_id is not None: volume_type = volume_types.get_volume_type(ctxt, type_id) specs = volume_type.get('extra_specs') for key, value in specs.iteritems(): vpool[key] = value return vpool @retry_wrapper def _find_exportgroup(self, initiator_ports): ''' Find the export group to which the given initiator ports are the same as the initiators in the group ''' foundgroupname = None grouplist = self.exportgroup_obj.exportgroup_list(self.configuration.vipr_project, self.configuration.vipr_tenant) for groupid in grouplist: groupdetails = self.exportgroup_obj.exportgroup_show(groupid, self.configuration.vipr_project, self.configuration.vipr_tenant) if groupdetails is not None: if (groupdetails['inactive']): continue initiators = groupdetails['initiators'] if initiators is not None: inits_eg = set() for initiator in initiators: inits_eg.add(initiator['initiator_port']) if (inits_eg == set(initiator_ports)): foundgroupname = groupdetails['name'] if foundgroupname is not None: # Check the associated varray if groupdetails['varray']: varray_uri = groupdetails['varray']['id'] varray_details = self.varray_obj.varray_show(varray_uri) if (varray_details['name'] == self.configuration.vipr_varray): LOG.debug("Found exportgroup " + foundgroupname) break # Not the right varray foundgroupname = None return foundgroupname @retry_wrapper def _find_host(self, initiator_port): ''' Find the host, if exists, to which the given initiator belong. ''' foundhostname = None hosts = self.host_obj.list_by_tenant(self.configuration.vipr_tenant) for host in hosts: initiators = self.host_obj.list_initiators(host['id']) for initiator in initiators: if (initiator_port == initiator['name']): foundhostname = host['name'] break if foundhostname is not None: break return foundhostname @retry_wrapper def _host_exists(self, host_name): ''' Check if a Host object with the given hostname already exists in ViPR ''' hosts = self.host_obj.search_by_name(host_name) if (len(hosts)>0): for host in hosts : hostname = host['match'] if (host_name == hostname): return hostname return hostname LOG.debug("no host found for:" +host_name) return None @retry_wrapper def update_volume_stats(self): """Retrieve stats info.""" LOG.debug(_("Updating volume stats")) self.authenticate_user() try: vols = self.volume_obj.list_volumes(self.configuration.vipr_tenant + "/" + self.configuration.vipr_project) vpairs = set() if (len(vols) > 0): for vol in vols: if(vol): vpair = (vol["vpool"]["id"], vol["varray"]["id"]) if (vpair not in vpairs): vpairs.add(vpair) if (len(vpairs) > 0): free_gb = 0.0 used_gb = 0.0 provisioned_gb = 0.0 precent_used = 0.0 percent_provisioned = 0.0 for vpair in vpairs: if(vpair): (s, h) = vipr_utils.service_json_request(self.configuration.vipr_hostname, self.configuration.vipr_port, "GET", URI_VPOOL_VARRAY_CAPACITY.format(vpair[0], vpair[1]), body=None) capacity = vipr_utils.json_decode(s) free_gb += float(capacity["free_gb"]) used_gb += float(capacity["used_gb"]) provisioned_gb += float(capacity["provisioned_gb"]) self.stats['free_capacity_gb'] = free_gb self.stats['total_capacity_gb'] = free_gb + used_gb self.stats['reserved_percentage'] = 100 * provisioned_gb/(free_gb + used_gb) return self.stats except SOSError as e: raise e