def _throw_on_http_error(self, response): if response.status == 401: raise exception.QBException( _("JSON RPC failed: unauthorized user %(status)s %(reason)s" " Please check the Quobyte API service log for " "more details.") % {'status': six.text_type(response.status), 'reason': response.reason}) elif response.status >= 300: raise exception.QBException( _("JSON RPC failed: %(status)s %(reason)s" " Please check the Quobyte API service log for " "more details.") % {'status': six.text_type(response.status), 'reason': response.reason})
def _fetch_existing_access(self, context, share): volume_uuid = self._resolve_volume_name( share['name'], self._get_project_name(context, share['project_id'])) result = self.rpc.call('getConfiguration', {}) if result is None: raise exception.QBException( "Could not retrieve Quobyte configuration data!") tenant_configs = result['tenant_configuration'] qb_access_list = [] for tc in tenant_configs: for va in tc['volume_access']: if va['volume_uuid'] == volume_uuid: a_level = constants.ACCESS_LEVEL_RW if va['read_only']: a_level = constants.ACCESS_LEVEL_RO qb_access_list.append({ 'access_to': va['restrict_to_network'], 'access_level': a_level, 'access_type': 'ip' }) return qb_access_list
def create_share(self, context, share, share_server=None): """Create or export a volume that is usable as a Manila share.""" if share['share_proto'] != 'NFS': raise exception.QBException( _('Quobyte driver only supports NFS shares')) volume_uuid = self._resolve_volume_name( share['name'], self._get_project_name(context, share['project_id'])) if not volume_uuid: # create tenant, expect ERROR_GARBAGE_ARGS if it already exists self.rpc.call('setTenant', dict(tenant=dict(tenant_id=share['project_id'])), expected_errors=[jsonrpc.ERROR_GARBAGE_ARGS]) result = self.rpc.call( 'createVolume', dict(name=share['name'], tenant_domain=share['project_id'], root_user_id=self.configuration. quobyte_default_volume_user, root_group_id=self.configuration. quobyte_default_volume_group, configuration_name=( self.configuration.quobyte_volume_configuration))) volume_uuid = result['volume_uuid'] result = self.rpc.call('exportVolume', dict(volume_uuid=volume_uuid, protocol='NFS')) return '%(nfs_server_ip)s:%(nfs_export_path)s' % result
def _checked_for_application_error(self, result): if 'error' in result and result['error']: if 'message' in result['error'] and 'code' in result['error']: if result["error"]["code"] == ERROR_ENOENT: return None # No Entry else: raise exception.QBRpcException( result=result["error"]["message"], qbcode=result["error"]["code"]) else: raise exception.QBException(six.text_type(result["error"])) return result["result"]
def _checked_for_application_error(self, result, expected_errors=[]): if 'error' in result and result['error']: if 'message' in result['error'] and 'code' in result['error']: if result["error"]["code"] in expected_errors: # hit an expected error, return empty result return None else: raise exception.QBRpcException( result=result["error"]["message"], qbcode=result["error"]["code"]) else: raise exception.QBException(six.text_type(result["error"])) return result["result"]
def do_setup(self, context): """Prepares the backend.""" self.rpc = jsonrpc.JsonRpc( url=self.configuration.quobyte_api_url, ca_file=self.configuration.quobyte_api_ca, user_credentials=(self.configuration.quobyte_api_username, self.configuration.quobyte_api_password)) try: self.rpc.call('getInformation', {}) except Exception as exc: LOG.error("Could not connect to API: %s", exc) raise exception.QBException( _('Could not connect to API: %s') % exc)
def call(self, method_name, user_parameters): parameters = {'retry': 'INFINITELY'} # Backend specific setting if user_parameters: parameters.update(user_parameters) call_body = { 'jsonrpc': '2.0', 'method': method_name, 'params': parameters, 'id': six.text_type(self._id) } self.call_counter = 0 self._connection.connect() # prevents http_client timing issue while self.call_counter < CONNECTION_RETRIES: self.call_counter += 1 try: self._id += 1 call_body['id'] = six.text_type(self._id) LOG.debug("Posting to Quobyte backend: %s", jsonutils.dumps(call_body)) self._connection.request( "POST", self._url + '/', jsonutils.dumps(call_body), dict(Authorization=( self._credentials.get_authorization_header()))) response = self._connection.getresponse() self._throw_on_http_error(response) result = jsonutils.loads(response.read()) LOG.debug("Retrieved data from Quobyte backend: %s", result) return self._checked_for_application_error(result) except ssl.SSLError as e: # Generic catch because OpenSSL does not return # meaningful errors. if (not self._disabled_cert_verification and not self._require_cert_verify): LOG.warning( _LW("Could not verify server certificate of " "API service against CA.")) self._connection.close() # Core HTTPSConnection does no certificate verification. self._connection = http_client.HTTPSConnection( self._netloc) self._disabled_cert_verification = True else: raise exception.QBException( _("Client SSL subsystem returned error: %s") % e) except http_client.BadStatusLine as e: raise exception.QBException( _("If SSL is enabled for the API service, the URL must" " start with 'https://' for the URL. Failed to parse" " status code from server response. Error was %s") % e) except socket.error as se: error_code = se.errno error_msg = se.strerror composite_msg = _("Socket error No. %(code)s (%(msg)s) " "connecting to API with") % { 'code': (six.text_type(error_code)), 'msg': error_msg } if self._fail_fast: raise exception.QBException(composite_msg) else: LOG.warning(composite_msg) except http_client.HTTPException as e: with excutils.save_and_reraise_exception() as ctxt: if self._fail_fast: ctxt.reraise = True else: LOG.warning(_LW("Encountered error, retrying: %s"), six.text_type(e)) ctxt.reraise = False raise exception.QBException("Unable to connect to backend after " "%s retries" % six.text_type(CONNECTION_RETRIES))