def check_param(self): """Check parameter values and consistency among them.""" utils.check_opt_value(self.conf, _INHERITED_VOLUME_OPTS) utils.check_opts(self.conf, COMMON_VOLUME_OPTS) utils.check_opts(self.conf, self.driver_info['volume_opts']) if self.conf.hitachi_ldev_range: self.storage_info['ldev_range'] = self._range2list( 'hitachi_ldev_range') if (not self.conf.hitachi_target_ports and not self.conf.hitachi_compute_target_ports): msg = utils.output_log(MSG.INVALID_PARAMETER, param='hitachi_target_ports or ' 'hitachi_compute_target_ports') raise utils.HBSDError(msg) if (self.conf.hitachi_group_delete and not self.conf.hitachi_group_create): msg = utils.output_log(MSG.INVALID_PARAMETER, param='hitachi_group_delete or ' 'hitachi_group_create') raise utils.HBSDError(msg) for opt in _REQUIRED_COMMON_OPTS: if not self.conf.safe_get(opt): msg = utils.output_log(MSG.INVALID_PARAMETER, param=opt) raise utils.HBSDError(msg) if self.storage_info['protocol'] == 'iSCSI': self.check_param_iscsi()
def check_pool_id(self): """Check the pool id of hitachi_pool and hitachi_snap_pool.""" pool = self.conf.hitachi_pool if pool is not None: if pool.isdigit(): self.storage_info['pool_id'] = int(pool) else: self.storage_info['pool_id'] = self._get_pool_id(pool) if self.storage_info['pool_id'] is None: msg = utils.output_log(MSG.POOL_NOT_FOUND, pool=self.conf.hitachi_pool) raise utils.HBSDError(msg) snap_pool = self.conf.hitachi_snap_pool if snap_pool is not None: if snap_pool.isdigit(): self.storage_info['snap_pool_id'] = int(snap_pool) else: self.storage_info['snap_pool_id'] = ( self._get_pool_id(snap_pool)) if self.storage_info['snap_pool_id'] is None: msg = utils.output_log(MSG.POOL_NOT_FOUND, pool=self.conf.hitachi_snap_pool) raise utils.HBSDError(msg) else: self.storage_info['snap_pool_id'] = self.storage_info['pool_id']
def check_param_iscsi(self): """Check iSCSI-related parameter values and consistency among them.""" if self.conf.use_chap_auth: if not self.conf.chap_username: msg = utils.output_log(MSG.INVALID_PARAMETER, param='chap_username') raise utils.HBSDError(msg) if not self.conf.chap_password: msg = utils.output_log(MSG.INVALID_PARAMETER, param='chap_password') raise utils.HBSDError(msg)
def extend_volume(self, volume, new_size): """Extend the specified volume to the specified size.""" ldev = utils.get_ldev(volume) if ldev is None: msg = utils.output_log(MSG.INVALID_LDEV_FOR_EXTENSION, volume_id=volume['id']) raise utils.HBSDError(msg) if self.check_pair_svol(ldev): msg = utils.output_log(MSG.INVALID_VOLUME_TYPE_FOR_EXTEND, volume_id=volume['id']) raise utils.HBSDError(msg) self.delete_pair(ldev) self.extend_ldev(ldev, volume['size'], new_size)
def _create_clone_pair(self, pvol, svol): """Create a clone copy pair on the storage.""" snapshot_name = '%(prefix)s%(svol)s' % { 'prefix': CLONE_NAME, 'svol': svol % _SNAP_HASH_SIZE, } try: body = {"snapshotGroupName": snapshot_name, "snapshotPoolId": self.storage_info['snap_pool_id'], "pvolLdevId": pvol, "svolLdevId": svol, "isClone": True, "clonesAutomation": True, "copySpeed": 'medium', "isDataReductionForceCopy": True} self.client.add_snapshot(body) except utils.HBSDError as ex: if (utils.safe_get_err_code(ex.kwargs.get('errobj')) == rest_api.INVALID_SNAPSHOT_POOL and not self.conf.hitachi_snap_pool): msg = utils.output_log( MSG.INVALID_PARAMETER, param='hitachi_snap_pool') raise utils.HBSDError(msg) else: raise try: self._wait_copy_pair_status(svol, set([PSUS, SMPP, SMPL])) except Exception: with excutils.save_and_reraise_exception(): try: self._delete_pair_from_storage(pvol, svol) except utils.HBSDError: utils.output_log( MSG.DELETE_PAIR_FAILED, pvol=pvol, svol=svol)
def get_hba_ids_from_connector(self, connector): """Return the HBA ID stored in the connector.""" if self.driver_info['hba_id'] in connector: return connector[self.driver_info['hba_id']] msg = utils.output_log(MSG.RESOURCE_NOT_FOUND, resource=self.driver_info['hba_id_type']) raise utils.HBSDError(msg)
def _range2list(self, param): """Analyze a 'xxx-xxx' string and return a list of two integers.""" values = [_str2int(value) for value in self.conf.safe_get(param).split('-')] if len(values) != 2 or None in values or values[0] > values[1]: msg = utils.output_log(MSG.INVALID_PARAMETER, param=param) raise utils.HBSDError(msg) return values
def check_ports_info(self): """Check if available storage ports exist.""" if (self.conf.hitachi_target_ports and not self.storage_info['controller_ports']): msg = utils.output_log(MSG.RESOURCE_NOT_FOUND, resource="Target ports") raise utils.HBSDError(msg) if (self.conf.hitachi_compute_target_ports and not self.storage_info['compute_ports']): msg = utils.output_log(MSG.RESOURCE_NOT_FOUND, resource="Compute target ports") raise utils.HBSDError(msg) utils.output_log(MSG.SET_CONFIG_VALUE, object='target port list', value=self.storage_info['controller_ports']) utils.output_log(MSG.SET_CONFIG_VALUE, object='compute target port list', value=self.storage_info['compute_ports'])
def update_group(self, group, add_volumes=None): if add_volumes and volume_utils.is_group_a_cg_snapshot_type(group): for volume in add_volumes: ldev = utils.get_ldev(volume) if ldev is None: msg = utils.output_log(MSG.LDEV_NOT_EXIST_FOR_ADD_GROUP, volume_id=volume.id, group='consistency group', group_id=group.id) raise utils.HBSDError(msg) return None, None, None
def create_group_from_src( self, context, group, volumes, snapshots=None, source_vols=None): volumes_model_update = [] new_ldevs = [] events = [] def _create_group_volume_from_src(context, volume, src, from_snapshot): volume_model_update = {'id': volume.id} try: ldev = utils.get_ldev(src) if ldev is None: msg = utils.output_log( MSG.INVALID_LDEV_FOR_VOLUME_COPY, type='snapshot' if from_snapshot else 'volume', id=src.id) raise utils.HBSDError(msg) volume_model_update.update( self.create_volume_from_snapshot(volume, src) if from_snapshot else self.create_cloned_volume(volume, src)) except Exception as exc: volume_model_update['msg'] = utils.get_exception_msg(exc) raise loopingcall.LoopingCallDone(volume_model_update) try: from_snapshot = True if snapshots else False for volume, src in zip(volumes, snapshots if snapshots else source_vols): loop = loopingcall.FixedIntervalLoopingCall( _create_group_volume_from_src, context, volume, src, from_snapshot) event = loop.start(interval=0) events.append(event) is_success = True for e in events: volume_model_update = e.wait() if 'msg' in volume_model_update: is_success = False msg = volume_model_update['msg'] else: volumes_model_update.append(volume_model_update) ldev = utils.get_ldev(volume_model_update) if ldev is not None: new_ldevs.append(ldev) if not is_success: raise utils.HBSDError(msg) except Exception: with excutils.save_and_reraise_exception(): for new_ldev in new_ldevs: try: self.delete_ldev(new_ldev) except utils.HBSDError: utils.output_log(MSG.DELETE_LDEV_FAILED, ldev=new_ldev) return None, volumes_model_update
def check_param(self): """Check parameter values and consistency among them.""" super(HBSDREST, self).check_param() utils.check_opts(self.conf, REST_VOLUME_OPTS) utils.check_opts(self.conf, san.san_opts) LOG.debug('Setting ldev_range: %s', self.storage_info['ldev_range']) for opt in _REQUIRED_REST_OPTS: if not self.conf.safe_get(opt): msg = utils.output_log(MSG.INVALID_PARAMETER, param=opt) raise utils.HBSDError(msg) if not self.conf.safe_get('san_api_port'): self.conf.san_api_port = _REST_DEFAULT_PORT
def create_snapshot(self, snapshot): """Create a snapshot from a volume and return its properties.""" src_vref = snapshot.volume ldev = utils.get_ldev(src_vref) if ldev is None: msg = utils.output_log(MSG.INVALID_LDEV_FOR_VOLUME_COPY, type='volume', id=src_vref['id']) raise utils.HBSDError(msg) size = snapshot['volume_size'] new_ldev = self._copy_on_storage(ldev, size, True) return { 'provider_location': str(new_ldev), }
def _create_cgsnapshot_volume(snapshot): pair = {'snapshot': snapshot} try: pair['pvol'] = utils.get_ldev(snapshot.volume) if pair['pvol'] is None: msg = utils.output_log( MSG.INVALID_LDEV_FOR_VOLUME_COPY, type='volume', id=snapshot.volume_id) raise utils.HBSDError(msg) size = snapshot.volume_size pair['svol'] = self.create_ldev(size) except Exception as exc: pair['msg'] = utils.get_exception_msg(exc) raise loopingcall.LoopingCallDone(pair)
def set_hba_ids(self, port, gid, hba_ids): """Connect all specified HBAs with the specified port.""" registered_wwns = [] for wwn in hba_ids: try: self.client.add_hba_wwn(port, gid, wwn, no_log=True) registered_wwns.append(wwn) except utils.HBSDError: utils.output_log(MSG.ADD_HBA_WWN_FAILED, port=port, gid=gid, wwn=wwn) if not registered_wwns: msg = utils.output_log(MSG.NO_HBA_WWN_ADDED_TO_HOST_GRP, port=port, gid=gid) raise utils.HBSDError(msg)
def _create_ctg_snap_pair(self, pairs): snapshotgroup_name = self._create_ctg_snapshot_group_name( pairs[0]['pvol']) try: for pair in pairs: try: body = {"snapshotGroupName": snapshotgroup_name, "snapshotPoolId": self.storage_info['snap_pool_id'], "pvolLdevId": pair['pvol'], "svolLdevId": pair['svol'], "isConsistencyGroup": True, "canCascade": True, "isDataReductionForceCopy": True} self.client.add_snapshot(body) except utils.HBSDError as ex: if ((utils.safe_get_err_code(ex.kwargs.get('errobj')) == _MAX_CTG_COUNT_EXCEEDED_ADD_SNAPSHOT) or (utils.safe_get_err_code(ex.kwargs.get('errobj')) == _MAX_PAIR_COUNT_IN_CTG_EXCEEDED_ADD_SNAPSHOT)): msg = utils.output_log(MSG.FAILED_CREATE_CTG_SNAPSHOT) raise utils.HBSDError(msg) elif (utils.safe_get_err_code(ex.kwargs.get('errobj')) == rest_api.INVALID_SNAPSHOT_POOL and not self.conf.hitachi_snap_pool): msg = utils.output_log( MSG.INVALID_PARAMETER, param='hitachi_snap_pool') raise utils.HBSDError(msg) raise self._wait_copy_pair_status(pair['svol'], PAIR) self.client.split_snapshotgroup(snapshotgroup_name) for pair in pairs: self._wait_copy_pair_status(pair['svol'], PSUS) except Exception: with excutils.save_and_reraise_exception(): self._delete_pairs_from_storage(pairs)
def get_pool_info(self): """Return the total and free capacity of the storage pool.""" result = self.client.get_pool( self.storage_info['pool_id'], ignore_message_id=[rest_api.MSGID_SPECIFIED_OBJECT_DOES_NOT_EXIST]) if 'errorSource' in result: msg = utils.output_log(MSG.POOL_NOT_FOUND, pool=self.storage_info['pool_id']) raise utils.HBSDError(msg) tp_cap = result['totalPoolCapacity'] / units.Ki ta_cap = result['availableVolumeCapacity'] / units.Ki tl_cap = result['totalLocatedCapacity'] / units.Ki return tp_cap, ta_cap, tl_cap
def _wait_copy_pair_deleting(self, ldev): """Wait until the LDEV is no longer in a copy pair.""" def _wait_for_copy_pair_smpl(start_time, ldev): """Raise True if the LDEV is no longer in a copy pair.""" ldev_info = self.get_ldev_info(['status', 'attributes'], ldev) if (ldev_info['status'] != NORMAL_STS or PAIR_ATTR not in ldev_info['attributes']): raise loopingcall.LoopingCallDone() if utils.timed_out(start_time, utils.DEFAULT_PROCESS_WAITTIME): raise loopingcall.LoopingCallDone(False) loop = loopingcall.FixedIntervalLoopingCall(_wait_for_copy_pair_smpl, timeutils.utcnow(), ldev) if not loop.start(interval=10).wait(): msg = utils.output_log(MSG.PAIR_STATUS_WAIT_TIMEOUT, svol=ldev) raise utils.HBSDError(msg)
def _copy_on_storage(self, pvol, size, is_snapshot=False): """Create a copy of the specified LDEV on the storage.""" ldev_info = self.get_ldev_info(['status', 'attributes'], pvol) if ldev_info['status'] != 'NML': msg = utils.output_log(MSG.INVALID_LDEV_STATUS_FOR_COPY, ldev=pvol) raise utils.HBSDError(msg) svol = self.create_ldev(size) try: self.create_pair_on_storage(pvol, svol, is_snapshot) except Exception: with excutils.save_and_reraise_exception(): try: self.delete_ldev(svol) except utils.HBSDError: utils.output_log(MSG.DELETE_LDEV_FAILED, ldev=svol) return svol
def create_volume_from_src(self, volume, src, src_type): """Create a volume from a volume or snapshot and return its properties. """ ldev = utils.get_ldev(src) if ldev is None: msg = utils.output_log( MSG.INVALID_LDEV_FOR_VOLUME_COPY, type=src_type, id=src['id']) raise utils.HBSDError(msg) size = volume['size'] new_ldev = self._copy_on_storage(ldev, size) self.modify_ldev_name(new_ldev, volume['id'].replace("-", "")) return { 'provider_location': str(new_ldev), }
def _create_group_volume_from_src(context, volume, src, from_snapshot): volume_model_update = {'id': volume.id} try: ldev = utils.get_ldev(src) if ldev is None: msg = utils.output_log( MSG.INVALID_LDEV_FOR_VOLUME_COPY, type='snapshot' if from_snapshot else 'volume', id=src.id) raise utils.HBSDError(msg) volume_model_update.update( self.create_volume_from_snapshot(volume, src) if from_snapshot else self.create_cloned_volume(volume, src)) except Exception as exc: volume_model_update['msg'] = utils.get_exception_msg(exc) raise loopingcall.LoopingCallDone(volume_model_update)
def initialize_connection(self, volume, connector): """Initialize connection between the server and the volume.""" targets = { 'info': {}, 'list': [], 'lun': {}, 'iqns': {}, 'target_map': {}, } ldev = utils.get_ldev(volume) if ldev is None: msg = utils.output_log(MSG.INVALID_LDEV_FOR_CONNECTION, volume_id=volume['id']) raise utils.HBSDError(msg) target_lun = self.attach_ldev(volume, ldev, connector, targets) return { 'driver_volume_type': self.driver_info['volume_type'], 'data': self.get_properties(targets, target_lun, connector), }
def _wait_copy_pair_status(self, ldev, status, interval=3, timeout=utils.DEFAULT_PROCESS_WAITTIME): """Wait until the S-VOL status changes to the specified status.""" def _wait_for_copy_pair_status(start_time, ldev, status, timeout): """Raise True if the S-VOL is in the specified status.""" if not isinstance(status, set): status = set([status]) if self._get_copy_pair_status(ldev) in status: raise loopingcall.LoopingCallDone() if utils.timed_out(start_time, timeout): raise loopingcall.LoopingCallDone(False) loop = loopingcall.FixedIntervalLoopingCall(_wait_for_copy_pair_status, timeutils.utcnow(), ldev, status, timeout) if not loop.start(interval=interval).wait(): msg = utils.output_log(MSG.PAIR_STATUS_WAIT_TIMEOUT, svol=ldev) raise utils.HBSDError(msg)
def get_properties_iscsi(self, targets, multipath): """Return iSCSI-specific server-LDEV connection info.""" if not multipath: target_list = targets['list'][:1] else: target_list = targets['list'][:] for target in target_list: if target not in targets['iqns']: port, gid = target target_info = self.client.get_host_grp(port, gid) iqn = target_info.get('iscsiName') if target_info else None if not iqn: msg = utils.output_log(MSG.RESOURCE_NOT_FOUND, resource='Target IQN') raise utils.HBSDError(msg) targets['iqns'][target] = iqn LOG.debug( 'Found target iqn of host group. (port: %(port)s, ' 'gid: %(gid)s, target iqn: %(iqn)s)', {'port': port, 'gid': gid, 'iqn': iqn}) return super(HBSDRESTISCSI, self).get_properties_iscsi( targets, multipath)
def _create_cgsnapshot(self, context, cgsnapshot, snapshots): pairs = [] events = [] snapshots_model_update = [] def _create_cgsnapshot_volume(snapshot): pair = {'snapshot': snapshot} try: pair['pvol'] = utils.get_ldev(snapshot.volume) if pair['pvol'] is None: msg = utils.output_log( MSG.INVALID_LDEV_FOR_VOLUME_COPY, type='volume', id=snapshot.volume_id) raise utils.HBSDError(msg) size = snapshot.volume_size pair['svol'] = self.create_ldev(size) except Exception as exc: pair['msg'] = utils.get_exception_msg(exc) raise loopingcall.LoopingCallDone(pair) try: for snapshot in snapshots: ldev = utils.get_ldev(snapshot.volume) if ldev is None: msg = utils.output_log( MSG.INVALID_LDEV_FOR_VOLUME_COPY, type='volume', id=snapshot.volume_id) raise utils.HBSDError(msg) for snapshot in snapshots: loop = loopingcall.FixedIntervalLoopingCall( _create_cgsnapshot_volume, snapshot) event = loop.start(interval=0) events.append(event) is_success = True for e in events: pair = e.wait() if 'msg' in pair: is_success = False msg = pair['msg'] pairs.append(pair) if not is_success: raise utils.HBSDError(msg) self._create_ctg_snap_pair(pairs) except Exception: for pair in pairs: if 'svol' in pair and pair['svol'] is not None: try: self.delete_ldev(pair['svol']) except utils.HBSDError: utils.output_log( MSG.DELETE_LDEV_FAILED, ldev=pair['svol']) model_update = {'status': fields.GroupSnapshotStatus.ERROR} for snapshot in snapshots: snapshot_model_update = {'id': snapshot.id, 'status': fields.SnapshotStatus.ERROR} snapshots_model_update.append(snapshot_model_update) return model_update, snapshots_model_update for pair in pairs: snapshot_model_update = { 'id': pair['snapshot'].id, 'status': fields.SnapshotStatus.AVAILABLE, 'provider_location': str(pair['svol'])} snapshots_model_update.append(snapshot_model_update) return None, snapshots_model_update
def _request(self, method, url, params=None, body=None, async_=False, **kwargs): """Transmit the request to REST API server.""" kwargs.setdefault('ignore_error', []) kwargs['no_retry_error'] = (kwargs['ignore_error'] + REST_NO_RETRY_ERRORS) kwargs.setdefault('no_retry', False) kwargs.setdefault('do_raise', True) kwargs.setdefault('ignore_message_id', []) kwargs.setdefault('no_relogin', False) kwargs.setdefault('ignore_return_code', []) kwargs.setdefault('ignore_all_errors', False) kwargs.setdefault('timeout_message', None) kwargs.setdefault('no_log', False) kwargs.setdefault('timeout', _EXEC_MAX_WAITTIME) headers = dict(self.headers) if async_: read_timeout = (_JOB_API_RESPONSE_TIMEOUT + _RESPONSE_TIMEOUT_TOLERANCE) headers.update({ "Response-Max-Wait": str(_JOB_API_RESPONSE_TIMEOUT), "Response-Job-Status": "Completed;"}) else: read_timeout = _GET_API_RESPONSE_TIMEOUT auth_data = kwargs.get('auth', self.get_my_session()) timeout = (self.connect_timeout, read_timeout) interval = kwargs.get('interval', _EXEC_RETRY_INTERVAL) retry = True start_time = timeutils.utcnow() watch = timeutils.StopWatch() while retry: watch.restart() try: with requests.Session() as session: if self.tcp_keepalive: session.mount(_HTTPS, TCPKeepAliveAdapter()) rsp = session.request(method, url, params=params, json=body, headers=headers, auth=auth_data, timeout=timeout, verify=self.verify) except Exception as e: msg = utils.output_log( MSG.REST_SERVER_CONNECT_FAILED, exception=type(e), message=e, method=method, url=url, params=params, body=body) raise utils.HBSDError(msg) response = ResponseData(rsp) if (response['status_code'] == httpclient.INTERNAL_SERVER_ERROR and kwargs['timeout'] < _REST_SERVER_RESTART_TIMEOUT): kwargs['timeout'] = _REST_SERVER_RESTART_TIMEOUT if (response['status_code'] == httpclient.SERVICE_UNAVAILABLE and kwargs['timeout'] < _REST_SERVER_ERROR_TIMEOUT): kwargs['timeout'] = _REST_SERVER_ERROR_TIMEOUT retry, rsp_data, errobj = self._check_rest_api_response( response, start_time, method=method, url=url, params=params, body=body, **kwargs) if retry: watch.stop() idle = max(interval - watch.elapsed(), 0) greenthread.sleep(idle) if not kwargs['no_relogin'] and response.is_auth_fail(): auth_data = self.get_my_session() return rsp_data, errobj
def _check_rest_api_response( self, response, start_time, method=None, url=None, params=None, body=None, **kwargs): """Check the response from REST API server.""" rsp_body = response['rsp_body'] errobj = response['errobj'] if response.is_locked(): if (kwargs['no_retry'] or utils.timed_out(start_time, _LOCK_WAITTIME)): msg = utils.output_log(MSG.REST_API_FAILED, no_log=kwargs['no_log'], method=method, url=url, params=params, body=body, **response.get_errobj()) if kwargs['do_raise']: raise utils.HBSDError(msg, errobj=errobj) return False, rsp_body, errobj else: LOG.debug("The resource group to which the operation object ", "belongs is being locked by other software.") return True, rsp_body, errobj if response.is_success(kwargs['ignore_error'], kwargs['ignore_message_id'], kwargs['ignore_return_code'], kwargs['ignore_all_errors']): return False, rsp_body, errobj if (kwargs['no_retry'] and response['status_code'] != httpclient.INTERNAL_SERVER_ERROR or response.is_no_retry_error(kwargs['no_retry_error'])): retry = False elif response.is_auth_fail(): retry = self.relogin(kwargs['no_relogin']) else: retry = True if retry and response.is_rest_server_busy(): if utils.timed_out(start_time, _REST_SERVER_BUSY_TIMEOUT): retry = False elif retry and utils.timed_out(start_time, kwargs['timeout']): if kwargs['timeout_message']: utils.output_log(kwargs['timeout_message'][0], **kwargs['timeout_message'][1]) if response.is_json(): msg = utils.output_log(MSG.REST_API_TIMEOUT, no_log=kwargs['no_log'], method=method, url=url, params=params, body=body, **response.get_job_result()) if errobj: msg = utils.output_log(MSG.REST_API_FAILED, no_log=kwargs['no_log'], method=method, url=url, params=params, body=body, **response.get_errobj()) else: msg = utils.output_log(MSG.REST_API_HTTP_ERROR, no_log=kwargs['no_log'], status_code=response['status_code'], response_body=rsp_body, method=method, url=url, params=params, body=body) if kwargs['do_raise']: raise utils.HBSDError(msg, errobj=errobj) return False, rsp_body, errobj if errobj: LOG.debug('ERROR %s', errobj) else: LOG.debug('ERROR %s', ' '.join(str(rsp_body).splitlines())) if not retry: if response.is_json(): msg = utils.output_log(MSG.REST_API_FAILED, no_log=kwargs['no_log'], method=method, url=url, params=params, body=body, **response.get_errobj()) else: msg = utils.output_log(MSG.REST_API_HTTP_ERROR, no_log=kwargs['no_log'], status_code=response['status_code'], response_body=rsp_body, method=method, url=url, params=params, body=body) if kwargs['do_raise']: raise utils.HBSDError(msg, errobj=errobj) return retry, rsp_body, errobj