def delete_mapping(self, lun_id, host_name): if host_name and len(host_name) > constants.MAX_HOSTNAME_LENGTH: host_name = hash(host_name) host_id = self.find_host(host_name) if host_id: mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id view_id = self.find_mapping_view(mapping_view_name) if view_id: lungroup_id = self.find_lungroup_from_map(view_id) # Remove lun from lungroup. if lun_id and self.check_lun_exist(lun_id): if lungroup_id: lungroup_ids = self.get_lungroupids_by_lunid(lun_id) if lungroup_id in lungroup_ids: self.remove_lun_from_lungroup(lungroup_id, lun_id) else: LOG.info(('Lun is not in lungroup. ' 'Lun id: %(lun_id)s. ' 'lungroup id: %(lungroup_id)s.') % {"lun_id": lun_id, "lungroup_id": lungroup_id}) else: LOG.error("Can't find lun on the array.") raise VolumeBackendAPIException
def get_device_path(self, blockdevice_id): """ Return the device path that has been allocated to the block device on the host to which it is currently attached. :param unicode blockdevice_id: The unique identifier for the block device. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises UnattachedVolume: If the supplied ``blockdevice_id`` is not attached to a host. :returns: A ``FilePath`` for the device. """ LOG.info("Call get_device_path") # no mulitpath try: lun_info = self.restclient.get_lun_info(blockdevice_id) except Exception: raise UnknownVolume(blockdevice_id) if lun_info['EXPOSEDTOINITIATOR'].lower() == 'false': raise UnattachedVolume(blockdevice_id) lun_wwn = lun_info['WWN'] for bd in huawei_utils.get_all_block_device(): bd_wwn = huawei_utils.get_wwn_of_deviceblock(bd) if bd_wwn is not None and lun_wwn in bd_wwn: LOG.info("device_path finded: %s" % bd) return FilePath("/dev/"+bd) return None
def detach_volume(self, blockdevice_id): """ Detach ``blockdevice_id`` from whatever host it is attached to. :param unicode blockdevice_id: The unique identifier for the block device being detached. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises UnattachedVolume: If the supplied ``blockdevice_id`` is not attached to anything. :returns: ``None`` """ LOG.info("Call detach_volume blockdevice_id=%s" % blockdevice_id) device = self.get_device_path(blockdevice_id) if device is not None: huawei_utils.remove_scsi_device(device) lun_info = self.restclient.get_lun_info(blockdevice_id) if self.get_attached_to(lun_info) is not None: self.restclient.delete_mapping( blockdevice_id, self._compute_instance_id) else: LOG.error("Volume %s not attached." % blockdevice_id) raise UnattachedVolume(blockdevice_id)
def __init__(self, cluster_id, huawei_conf_file, compute_instance_id=None, allocation_unit=None): """ :param cluster_id: An ID that include in the names of Huawei volumes to identify cluster. :param huawei_conf_file: The path of huawei config file. :param compute_instance_id: An ID that used to create host on the array to identify node. :param allocation_unit: Allocation unit on array. :returns: A ``BlockDeviceVolume``. """ LOG.info("Huawei block device init") self._host_id = None self._hostgroup_id = None self.xml_file_path = huawei_conf_file self.configuration = huawei_utils.get_login_info( self.xml_file_path) self.restclient = rest_client.RestClient(self.configuration) self.restclient.login() if compute_instance_id is None: compute_instance_id = huawei_utils.get_instance_id( self.xml_file_path) self._compute_instance_id = compute_instance_id self._cluster_id = cluster_id if allocation_unit is None: allocation_unit = 512 self._allocation_unit = allocation_unit LOG.info("Finish huawei block device init")
def allocation_unit(self): """ The size, in bytes up to which ``IDeployer`` will round volume sizes before calling ``IBlockDeviceAPI.create_volume``. :returns: ``int`` """ LOG.info("Call allocation_unit") return self._allocation_unit
def do_mapping(self, lun_id, hostgroup_id, host_id, tgtportgroup_id=None): """Add hostgroup and lungroup to mapping view.""" lungroup_name = constants.LUNGROUP_PREFIX + host_id mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id lungroup_id = self._find_lungroup(lungroup_name) view_id = self.find_mapping_view(mapping_view_name) map_info = {} LOG.info(( 'do_mapping, lun_group: %(lun_group)s, ' 'view_id: %(view_id)s, lun_id: %(lun_id)s.') % {'lun_group': lungroup_id, 'view_id': view_id, 'lun_id': lun_id}) try: # Create lungroup and add LUN into to lungroup. if lungroup_id is None: lungroup_id = self._create_lungroup(lungroup_name) is_associated = self._is_lun_associated_to_lungroup(lungroup_id, lun_id) if not is_associated: self.associate_lun_to_lungroup(lungroup_id, lun_id) if view_id is None: view_id = self._add_mapping_view(mapping_view_name) self._associate_hostgroup_to_view(view_id, hostgroup_id) self._associate_lungroup_to_view(view_id, lungroup_id) if tgtportgroup_id: self._associate_portgroup_to_view(view_id, tgtportgroup_id) else: if not self.hostgroup_associated(view_id, hostgroup_id): self._associate_hostgroup_to_view(view_id, hostgroup_id) if not self.lungroup_associated(view_id, lungroup_id): self._associate_lungroup_to_view(view_id, lungroup_id) if tgtportgroup_id: if not self._portgroup_associated(view_id, tgtportgroup_id): self._associate_portgroup_to_view(view_id, tgtportgroup_id) version = self.find_array_version() if version >= constants.ARRAY_VERSION: aval_luns = self.find_view_by_id(view_id) map_info["lun_id"] = lun_id map_info["view_id"] = view_id map_info["aval_luns"] = aval_luns except Exception: LOG.error('Error occurred when adding hostgroup and lungroup to ' 'view. Remove lun from lungroup now.') self.remove_lun_from_lungroup(lungroup_id, lun_id) raise VolumeBackendAPIException return map_info
def get_attached_to(self, item): """ TODO: Find a way to save the attach_to information. """ LOG.info("Call get_attached_to") if item['ISADD2LUNGROUP'] == 'true': result = self.restclient.get_host_of_lun_map(item['ID']) if 'data' in result: return result['data'][0]['NAME'] return None
def create_volume(self, dataset_id, size): """ Create a new volume. When called by ``IDeployer``, the supplied size will be rounded up to the nearest ``IBlockDeviceAPI.allocation_unit()`` :param UUID dataset_id: The Flocker dataset ID of the dataset on this volume. :param int size: The size of the new volume in bytes. :returns: A ``BlockDeviceVolume``. """ LOG.info("Call create_volume, dataset_id=%s, size=%d" % (dataset_id, size)) name = huawei_utils.encode_name(dataset_id, self._cluster_id) parameters = huawei_utils.get_lun_conf_params(self.xml_file_path) if parameters is None: raise VolumeException pool_name = huawei_utils.get_pools(self.xml_file_path) if pool_name is None: raise VolumeException pools = self.restclient.find_all_pools() pool_info = self.restclient.find_pool_info(pool_name, pools) lun_param = {"TYPE": '11', "NAME": name, "PARENTTYPE": '216', "PARENTID": pool_info['ID'], "ALLOCTYPE": parameters['LUNType'], "CAPACITY": str((size/512)), "WRITEPOLICY": parameters['WriteType'], "MIRRORPOLICY": parameters['MirrorSwitch'], "PREFETCHPOLICY": parameters['PrefetchType'], "PREFETCHVALUE": parameters['PrefetchValue'], "DATATRANSFERPOLICY": parameters['policy'], "READCACHEPOLICY": parameters['readcachepolicy'], "WRITECACHEPOLICY": parameters['writecachepolicy']} url = "/lun" data = json.dumps(lun_param) result = self.restclient.call(url, data) lun_info = result['data'] volume = BlockDeviceVolume( size=int(lun_info['CAPACITY'])*512, attached_to=None, dataset_id=huawei_utils.decode_name(lun_info['NAME'], self._cluster_id), blockdevice_id=unicode(lun_info['ID']) ) return volume
def do_call(self, url=False, data=None, method=None, calltimeout=constants.SOCKET_TIMEOUT): """Send requests to server. Send HTTPS call, get response in JSON. Convert response into Python Object and return it. """ if self.url: url = self.url + url opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie)) urllib2.install_opener(opener) res_json = None LOG.info(('\n\n\n\nRequest URL: %(url)s\n\n' 'Call Method: %(method)s\n\n' 'Request Data: %(data)s\n\n') % {'url': url, 'method': method, 'data': data}) try: urllib2.socket.setdefaulttimeout(calltimeout) req = urllib2.Request(url, data, self.headers) if method: req.get_method = lambda: method res = urllib2.urlopen(req).read().decode("utf-8") if "xx/sessions" not in url: LOG.info(('\n\n\n\nRequest URL: %(url)s\n\n' 'Call Method: %(method)s\n\n' 'Request Data: %(data)s\n\n' 'Response Data:%(res)s\n\n') % {'url': url, 'method': method, 'data': data, 'res': res}) except Exception as err: LOG.error('Bad response from server: %(url)s.Error: %(err)s' % {'url': url, 'err': err}) json_msg = ('{"error":{"code": %s,"description": "Connect to ' 'server error."}}') % constants.ERROR_CONNECT_TO_SERVER res_json = json.loads(json_msg) return res_json try: res_json = json.loads(res) except Exception as err: LOG.error('JSON transfer error: %s.' % err.message) raise return res_json
def compute_instance_id(self): """ Get an identifier for this node. This will be compared against ``BlockDeviceVolume.attached_to`` to determine which volumes are locally attached and it will be used with ``attach_volume`` to locally attach volumes. :returns: A ``unicode`` object giving a provider-specific node identifier which identifies the node where the method is run. """ LOG.info("Call compute_instance_id = %s" % self._compute_instance_id) return unicode(self._compute_instance_id)
def attach_volume(self, blockdevice_id, attach_to): """ Attach ``blockdevice_id`` to the node indicated by ``attach_to``. :param unicode blockdevice_id: The unique identifier for the block device being attached. :param unicode attach_to: An identifier like the one returned by the ``compute_instance_id`` method indicating the node to which to attach the volume. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises AlreadyAttachedVolume: If the supplied ``blockdevice_id`` is already attached. :returns: A ``BlockDeviceVolume`` with a ``attached_to`` attribute set to ``attach_to``. """ LOG.info("Call attach_volume blockdevice_id=%s, attach_to=%s" % (blockdevice_id, attach_to)) try: lun_info = self.restclient.get_lun_info(blockdevice_id) except Exception: raise UnknownVolume(blockdevice_id) if lun_info['EXPOSEDTOINITIATOR'].lower() == 'true': raise AlreadyAttachedVolume(blockdevice_id) self.initialize_connection() self.restclient.do_mapping(blockdevice_id, self._hostgroup_id, self._host_id) huawei_utils.rescan_scsi() lun_info = self.restclient.get_lun_info(blockdevice_id) attached_volume = BlockDeviceVolume( size=int(lun_info['CAPACITY'])*512, attached_to=unicode(attach_to), dataset_id=huawei_utils.decode_name( lun_info['NAME'], self._cluster_id), blockdevice_id=blockdevice_id) return attached_volume
def destroy_volume(self, blockdevice_id): """ Destroy an existing volume. :param unicode blockdevice_id: The unique identifier for the volume to destroy. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :return: ``None`` """ LOG.info("Call destroy_volume blockdevice_id=%s" % blockdevice_id) try: self.restclient.delete_lun(blockdevice_id) except Exception: raise UnknownVolume(blockdevice_id)
def _associate_initiator_to_host(self, xml_file_path, initiator_name, host_id): """Associate initiator with the host.""" iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path) chapinfo = self.find_chap_info(iscsi_conf, initiator_name) multipath_type = self._find_alua_info(iscsi_conf, initiator_name) if chapinfo: LOG.info('Use CHAP when adding initiator to host.') self._use_chap(chapinfo, initiator_name, host_id) else: self._add_initiator_to_host(initiator_name, host_id) if multipath_type: LOG.info('Use ALUA when adding initiator to host.') self._use_alua(initiator_name, multipath_type)
def login(self): """Login array.""" login_info = self.configuration urlstr = login_info['RestURL'] url = urlstr + "xx/sessions" data = json.dumps({"username": login_info['UserName'], "password": login_info['UserPassword'], "scope": "0"}) self._init_http_head() result = self.do_call(url, data, calltimeout=constants.LOGIN_SOCKET_TIMEOUT) if (result['error']['code'] != 0) or ("data" not in result): LOG.error("Login error, reason is: %s." % result) return None LOG.info('Login success: %(url)s' % {'url': urlstr}) self.device_id = result['data']['deviceid'] self.url = urlstr + self.device_id self.headers['iBaseToken'] = result['data']['iBaseToken'] return self.device_id
def list_volumes(self): """ List all the block devices available via the back end API. :returns: A ``list`` of ``BlockDeviceVolume``s. """ LOG.info("Call list_volumes") volumes = [] url = "/lun?range=[0-65535]" result = self.restclient.call(url, None, "GET") if 'data' in result: for item in result['data']: if huawei_utils.is_cluster_volume( item['NAME'], self._cluster_id): volume = BlockDeviceVolume( size=int(item['CAPACITY'])*512, attached_to=self.get_attached_to(item), dataset_id=huawei_utils.decode_name( item['NAME'], self._cluster_id), blockdevice_id=unicode(item['ID']) ) volumes.append(volume) return volumes
def add_host_with_check(self, host_name): host_name_before_hash = None if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH): host_name_before_hash = host_name host_name = hash(host_name) host_id = self.find_host(host_name) if host_id: LOG.info(( 'add_host_with_check. ' 'host name: %(name)s, ' 'host id: %(id)s') % {'name': host_name, 'id': host_id}) return host_id try: host_id = self._add_host(host_name, host_name_before_hash) except Exception: LOG.info(( 'Failed to create host: %(name)s. ' 'Check if it exists on the array.') % {'name': host_name}) host_id = self.find_host(host_name) if not host_id: err_msg = (( 'Failed to create host: %(name)s. ' 'Please check if it exists on the array.') % {'name': host_name}) LOG.error(err_msg) raise VolumeBackendAPIException LOG.info(( 'add_host_with_check. ' 'create host success. ' 'host name: %(name)s, ' 'host id: %(id)s') % {'name': host_name, 'id': host_id}) return host_id
def create_hostgroup_with_check(self, hostgroup_name): """Check if host exists on the array, or create it.""" hostgroup_id = self.find_hostgroup(hostgroup_name) if hostgroup_id: LOG.info(( 'create_hostgroup_with_check. ' 'hostgroup name: %(name)s, ' 'hostgroup id: %(id)s') % {'name': hostgroup_name, 'id': hostgroup_id}) return hostgroup_id try: hostgroup_id = self._create_hostgroup(hostgroup_name) except Exception: LOG.info(( 'Failed to create hostgroup: %(name)s. ' 'Please check if it exists on the array.') % {'name': hostgroup_name}) hostgroup_id = self.find_hostgroup(hostgroup_name) if hostgroup_id is None: err_msg = (( 'Failed to create hostgroup: %(name)s. ' 'Check if it exists on the array.') % {'name': hostgroup_name}) LOG.error(err_msg) raise VolumeBackendAPIException LOG.info(( 'create_hostgroup_with_check. ' 'Create hostgroup success. ' 'hostgroup name: %(name)s, ' 'hostgroup id: %(id)s') % {'name': hostgroup_name, 'id': hostgroup_id}) return hostgroup_id