def _marshaled_single_dispatch(self, request): # TODO - Use the multiprocessing and skip the response if # it is a notification # Put in support for custom dispatcher here # (See SimpleXMLRPCServer._marshaled_dispatch) method = request.get('method') params = request.get('params') try: response = self._dispatch(method, params) except: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response() if 'id' not in request.keys() or request['id'] == None: # It's a notification return None try: response = jsonrpclib.dumps(response, methodresponse=True, rpcid=request['id'] ) return response except: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response()
def _dispatch(self, method, params, config=None): """ Default method resolver and caller :param method: Name of the method to call :param params: List of arguments to give to the method :param config: Request-specific configuration :return: The result of the method """ config = config or self.json_config func = None try: # Look into registered methods func = self.funcs[method] except KeyError: if self.instance is not None: # Try with the registered instance try: # Instance has a custom dispatcher return getattr(self.instance, '_dispatch')(method, params) except AttributeError: # Resolve the method name in the instance try: func = xmlrpcserver.resolve_dotted_attribute( self.instance, method, True) except AttributeError: # Unknown method pass if func is not None: try: # Call the method if isinstance(params, utils.ListType): return func(*params) else: return func(**params) except TypeError as ex: # Maybe the parameters are wrong fault = Fault(-32602, 'Invalid parameters: {0}'.format(ex), config=config) _logger.warning("Invalid call parameters: %s", fault) return fault except: # Method exception err_lines = traceback.format_exc().splitlines() trace_string = '{0} | {1}'.format(err_lines[-3], err_lines[-1]) fault = Fault(-32603, 'Server error: {0}'.format(trace_string), config=config) _logger.exception("Server-side exception: %s", fault) return fault else: # Unknown method fault = Fault(-32601, 'Method {0} not supported.'.format(method), config=config) _logger.warning("Unknown method: %s", fault) return fault
def _unmarshaled_dispatch(self, request, dispatch_method=None): """ Loads the request dictionary (unmarshaled), calls the method(s) accordingly and returns a JSON-RPC dictionary (not marshaled) :param request: JSON-RPC request dictionary (or list of) :param dispatch_method: Custom dispatch method (for method resolution) :return: A JSON-RPC dictionary (or an array of) or None if the request was a notification :raise NoMulticallResult: No result in batch """ if not request: # Invalid request dictionary fault = Fault(-32600, 'Request invalid -- no request data.', config=self.json_config) _logger.warning("Invalid request: %s", fault) return fault.dump() if isinstance(request, utils.ListType): # This SHOULD be a batch, by spec responses = [] for req_entry in request: # Validate the request result = validate_request(req_entry, self.json_config) if isinstance(result, Fault): responses.append(result.dump()) continue # Call the method resp_entry = self._marshaled_single_dispatch(req_entry, dispatch_method) # Store its result if isinstance(resp_entry, Fault): # pylint: disable=E1103 responses.append(resp_entry.dump()) elif resp_entry is not None: responses.append(resp_entry) if not responses: # No non-None result _logger.error("No result in Multicall") raise NoMulticallResult("No result") return responses else: # Single call result = validate_request(request, self.json_config) if isinstance(result, Fault): return result.dump() # Call the method response = self._marshaled_single_dispatch(request, dispatch_method) if isinstance(response, Fault): # pylint: disable=E1103 return response.dump() return response
def _dispatch(self, method, params): func = None try: func = self.funcs[method] except KeyError: if self.instance is not None: if hasattr(self.instance, '_dispatch'): return self.instance._dispatch(method, params) else: try: func = SimpleXMLRPCServer.resolve_dotted_attribute( self.instance, method, True) except AttributeError: pass if func is not None: try: if type(params) is types.ListType: response = func(*params) else: response = func(**params) return response except TypeError: return Fault(-32602, 'Invalid parameters: ') except: err_lines = traceback.format_exc().splitlines() trace_string = '%s | %s' % (err_lines[-3], err_lines[-1]) fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string) return fault else: return Fault(-32601, 'Method %s not supported.' % method)
def _marshaled_dispatch(self, data, dispatch_method=None, path=None): """ Parses the request data (marshaled), calls method(s) and returns a JSON string (marshaled) :param data: A JSON request string :param dispatch_method: Custom dispatch method (for method resolution) :param path: Unused parameter, to keep compatibility with xmlrpclib :return: A JSON-RPC response string (marshaled) """ # Parse the request try: request = jsonrpclib.loads(data, self.json_config) except Exception as ex: # Parsing/loading error fault = Fault(-32700, 'Request {0} invalid. ({1}:{2})' .format(data, type(ex).__name__, ex), config=self.json_config) _logger.warning("Error parsing request: %s", fault) return fault.response() # Get the response dictionary try: response = self._unmarshaled_dispatch(request, dispatch_method) if response is not None: # Compute the string representation of the dictionary/list return jsonrpclib.jdumps(response, self.encoding) else: # No result (notification) return '' except NoMulticallResult: # Return an empty string (jsonrpclib internal behaviour) return ''
def _marshaled_dispatch(self, data, dispatch_method=None): """Convert the data to a JSON RPC method request and dispatch it.""" try: request = jsonrpclib.loads(data) except Exception as e: fault = Fault(-32700, "Request %s invalid. (%s)" % (data, e)) response = fault.response() return response if not request: fault = Fault(-32600, "Request invalid -- no request data.") return fault.response() if isinstance(request, types.ListType): # batch of requests responses = [] for req_entry in request: result = validate_request(req_entry) if type(result) is Fault: responses.append(result.response()) continue resp_entry = self._marshaled_single_dispatch(req_entry) if resp_entry is not None: responses.append(resp_entry) if len(responses) > 0: response = "[%s]" % ",".join(responses) else: response = "" else: # single request result = validate_request(request) if type(result) is Fault: return result.response() response = self._marshaled_single_dispatch(request) return response
def _marshaled_dispatch(self, session_id, data, dispatch_method=None): response = None try: request = jsonrpclib.loads(data) except Exception, e: fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e)) response = fault.response() return response
def validate_request(request, json_config): """ Validates the format of a request dictionary :param request: A request dictionary :param json_config: A JSONRPClib Config instance :return: True if the dictionary is valid, else a Fault object """ if not isinstance(request, utils.DictType): # Invalid request type fault = Fault( -32600, "Request must be a dict, not {0}".format(type(request).__name__), config=json_config, ) _logger.warning("Invalid request content: %s", fault) return fault # Get the request ID rpcid = request.get("id", None) # Check request version version = get_version(request) if not version: fault = Fault( -32600, "Request {0} invalid.".format(request), rpcid=rpcid, config=json_config, ) _logger.warning("No version in request: %s", fault) return fault # Default parameters: empty list request.setdefault("params", []) # Check parameters method = request.get("method", None) params = request.get("params") param_types = (utils.ListType, utils.DictType, utils.TupleType) if (not method or not isinstance(method, utils.STRING_TYPES) or not isinstance(params, param_types)): # Invalid type of method name or parameters fault = Fault( -32600, "Invalid request parameters or method.", rpcid=rpcid, config=json_config, ) _logger.warning("Invalid request content: %s", fault) return fault # Valid request return True
def _marshaled_single_dispatch(self, request, dispatch_method=None): """ Dispatches a single method call :param request: A validated request dictionary :param dispatch_method: Custom dispatch method (for method resolution) :return: A JSON-RPC response dictionary, or None if it was a notification request """ # TODO - Use the multiprocessing and skip the response if # it is a notification method = request.get('method') params = request.get('params') # Prepare a request-specific configuration if 'jsonrpc' not in request and self.json_config.version >= 2: # JSON-RPC 1.0 request on a JSON-RPC 2.0 # => compatibility needed config = self.json_config.copy() config.version = 1.0 else: # Keep server configuration as is config = self.json_config try: # Call the method if dispatch_method is not None: response = dispatch_method(method, params) else: response = self._dispatch(method, params, config) except: # Return a fault exc_type, exc_value, _ = sys.exc_info() fault = Fault(-32603, '{0}:{1}'.format(exc_type, exc_value), config=config) return fault.dump() if 'id' not in request or request['id'] in (None, ''): # It's a notification, no result needed # Do not use 'not id' as it might be the integer 0 return None # Prepare a JSON-RPC dictionary try: # TODO: use a copy of the configuration, to JSON-RPC version return jsonrpclib.dump(response, rpcid=request['id'], is_response=True, config=config) except: # JSON conversion exception exc_type, exc_value, _ = sys.exc_info() fault = Fault(-32603, '{0}:{1}'.format(exc_type, exc_value), config=config) return fault.dump()
def _marshaled_dispatch(self, data, client_ip=None): response = None try: request = jsonrpclib.loads(data) if not request.has_key('params'): request['params'] = {} request['params']['client_ip'] = client_ip except Exception, e: fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e)) response = fault.response() return response
def _marshaled_dispatch(self, data, client_ip=None): response = None try: request = jsonrpclib.loads(data) if not request.has_key("params"): request["params"] = {} request["params"]["client_ip"] = client_ip except Exception, e: fault = Fault(-32700, "Request %s invalid. (%s)" % (data, e)) response = fault.response() return response
def get_running_info (self): if self.status == TRexStatus.Running: return self.get_latest_dump() else: logger.info("TRex isn't running. Running information isn't available.") if self.status == TRexStatus.Idle: if self.errcode is not None: # some error occured logger.info("TRex is in Idle state, with errors. returning fault") return Fault(self.errcode, self.verbose_status) # raise at client relevant exception, depending on the reason the error occured else: logger.info("TRex is in Idle state, no errors. returning {}") return u'{}' return Fault(-12, self.verbose_status) # raise at client TRexWarning, indicating TRex is back to Idle state or still in Starting state
def validate_request(request): if not isinstance(request, types.DictType): return Fault(-32600, 'Request must be {}, not %s.' % type(request)) rpcid = request.get('id', None) version = get_version(request) if not version: return Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid) request.setdefault('params', []) method = request.get('method', None) params = request.get('params') param_types = (types.ListType, types.DictType, types.TupleType) if not method or type(method) not in types.StringTypes or type(params) not in param_types: return Fault(-32600, 'Invalid request parameters or method.', rpcid=rpcid) return True
def cancel_reservation(self, user): with self.start_lock: logger.info("Processing cancel_reservation() command.") if self.is_reserved(): if self.__reservation['user'] == user: logger.info( "TRex reservation to {res_user} has been canceled successfully." .format(res_user=self.__reservation['user'])) self.__reservation = None return True else: logger.warning( "TRex is reserved to different user than the provided one. Reservation wasn't canceled." ) return Fault( -33, "Cancel reservation request is available to the user that holds the reservation. Request denied" ) # raise at client TRexRequestDenied else: logger.info( "TRex is not reserved to anyone. No need to cancel anything" ) assert (self.__reservation is None) return False
def get_trex_config_metadata(self): logger.info("Processing get_trex_config_metadata() command.") metadata_json_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'config_metadata.json')) try: with open(metadata_json_path) as f: return json.load(f) except Exception as e: return Fault(-33, "Can't load config metadata contents: %s" % e)
def start_trex(self, trex_cmd_options, user, block_to_success = True, timeout = 40, stateless = False, debug_image = False, trex_args = ''): self.trex.zmq_error = None self.assert_zmq_ok() with self.start_lock: logger.info("Processing start_trex() command.") if self.is_reserved(): # check if this is not the user to which TRex is reserved if self.__reservation['user'] != user: logger.info("TRex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user'])) return Fault(-33, "TRex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user'])) # raise at client TRexRequestDenied elif self.trex.get_status() != TRexStatus.Idle: err = 'TRex is already taken, cannot create another run until done.' logger.info(err) return Fault(-13, err) # raise at client TRexInUseError try: server_cmd_data = self.generate_run_cmd(stateless = stateless, debug_image = debug_image, trex_args = trex_args, **trex_cmd_options) self.zmq_monitor.first_dump = True self.trex.start_trex(self.TREX_PATH, server_cmd_data) logger.info("TRex session has been successfully initiated.") if block_to_success: # delay server response until TRex is at 'Running' state. start_time = time.time() trex_state = None while (time.time() - start_time) < timeout : trex_state = self.trex.get_status() if trex_state != TRexStatus.Starting: break else: time.sleep(0.5) self.assert_zmq_ok() # check for TRex run started normally if trex_state == TRexStatus.Starting: # reached timeout logger.warning("TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.") return Fault(-12, 'TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.') # raise at client TRexWarning elif trex_state == TRexStatus.Idle: return Fault(-11, self.trex.get_verbose_status()) # raise at client TRexError # reach here only if TRex is at 'Running' state self.trex.gen_seq() return self.trex.get_seq() # return unique seq number to client except TypeError as e: logger.error("TRex command generation failed, probably because either -f (traffic generation .yaml file) and -c (num of cores) was not specified correctly.\nReceived params: {params}".format( params = trex_cmd_options) ) raise TypeError('TRex -f (traffic generation .yaml file) and -c (num of cores) must be specified. %s' % e)
def _pull_file(filepath): try: with open(filepath, 'rb') as f: file_content = f.read() return binascii.b2a_base64(file_content).decode(errors='replace') except Exception as e: err_str = "Can't get requested file %s: %s" % (filepath, e) logger.error(err_str) return Fault(-33, err_str)
def validate_request(request, json_config): """ Validates the format of a request dictionary :param request: A request dictionary :param json_config: A JSONRPClib Config instance :return: True if the dictionary is valid, else a Fault object """ if not isinstance(request, utils.DictType): # Invalid request type return Fault(-32600, 'Request must be a dict, not {0}'.format( type(request).__name__), config=json_config) # Get the request ID rpcid = request.get('id', None) # Check request version version = get_version(request) if not version: return Fault(-32600, 'Request {0} invalid.'.format(request), rpcid=rpcid, config=json_config) # Default parameters: empty list request.setdefault('params', []) # Check parameters method = request.get('method', None) params = request.get('params') param_types = (utils.ListType, utils.DictType, utils.TupleType) if not method or not isinstance(method, utils.string_types) or \ not isinstance(params, param_types): # Invalid type of method name or parameters return Fault(-32600, 'Invalid request parameters or method.', rpcid=rpcid, config=json_config) # Valid request return True
def validate_request(request): if not isinstance(request, dict): fault = Fault(-32600, 'Request must be {}, not %s.' % type(request)) return fault rpcid = request.get('id', None) version = get_version(request) if not version: fault = Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid) return fault request.setdefault('params', []) method = request.get('method', None) params = request.get('params') if not method or not isinstance(method, basestring) or \ not isinstance(params, (list, dict, tuple)): fault = Fault(-32600, 'Invalid request parameters or method.', rpcid=rpcid) return fault return True
def wait_until_kickoff_finish (self, timeout = 40): # block until T-Rex exits Starting state logger.info("Processing wait_until_kickoff_finish() command.") trex_state = None start_time = time.time() while (time.time() - start_time) < timeout : trex_state = self.trex.get_status() if trex_state != TRexStatus.Starting: return return Fault(-12, 'TimeoutError: T-Rex initiation outcome could not be obtained, since T-Rex stays at Starting state beyond defined timeout.') # raise at client TRexWarning
def get_devices_info(self): logger.info("Processing get_devices_info() command.") try: args = [os.path.join(self.TREX_PATH, 'dpdk_nic_bind.py'), '-s', '--json'] result = subprocess.check_output(args, cwd=self.TREX_PATH, universal_newlines=True) return json.loads(result) except Exception as e: err_str = "Error processing get_devices_info(): %s" % e logger.error(e) return Fault(-33, err_str)
def get_file(self, filepath): try: logger.info("Processing get_file() command.") if not self._check_path_under_TRex_or_temp(filepath): raise Exception('Given path should be under current TRex package or /tmp/trex_files') return self._pull_file(filepath) except Exception as e: err_str = "Can't get requested file %s: %s" % (filepath, e) logger.error(err_str) return Fault(-33, err_str)
def reserve_trex(self, user): if user == "": logger.info( "TRex reservation cannot apply to empty string user. Request denied." ) return Fault( -33, "TRex reservation cannot apply to empty string user. Request denied." ) with self.start_lock: logger.info("Processing reserve_trex() command.") if self.is_reserved(): if user == self.__reservation['user']: # return True is the same user is asking and already has the resrvation logger.info( "the same user is asking and already has the resrvation. Re-reserving TRex." ) return True logger.info( "TRex is already reserved to another user ({res_user}), cannot reserve to another user." .format(res_user=self.__reservation['user'])) return Fault( -33, "TRex is already reserved to another user ({res_user}). Please make sure TRex is free before reserving it." .format(res_user=self.__reservation['user'] )) # raise at client TRexInUseError elif self.trex.get_status() != TRexStatus.Idle: logger.info( "TRex is currently running, cannot reserve TRex unless in Idle state." ) return Fault( -13, 'TRex is currently running, cannot reserve TRex unless in Idle state. Please try again when TRex run finished.' ) # raise at client TRexInUseError else: logger.info( "TRex is now reserved for user ({res_user}).".format( res_user=user)) self.__reservation = {'user': user, 'since': time.ctime()} logger.debug("Reservation details: " + str(self.__reservation)) return True
def _pull_file(filepath): try: with open(filepath, 'rb') as f: file_content = f.read() return binascii.b2a_base64(file_content) except Exception as e: err_str = "Can't get requested file: {0}, possibly due to TRex that did not run".format( filepath) logger.error('{0}, error: {1}'.format(err_str, e)) return Fault(-33, err_str)
def get_files_list(self, path): try: logger.info("Processing get_files_list() command, given path: %s" % path) if not self._check_path_under_TRex_or_temp(path): raise Exception('Given path should be under current TRex package or /tmp/trex_files') return os.walk(path).next()[1:3] except Exception as e: err_str = "Error processing get_files_list(): %s" % e logger.error(err_str) return Fault(-33, err_str)
def do_POST(self): """ HTTP POST method processor. """ if not self.is_rpc_path_valid(): logger.warning("[Response] HTTP 404 - The path requested is not " "a valid address.") self.report_404() return try: size_remaining = int(self.headers["content-length"]) L = [] while size_remaining: chunk_size = min(size_remaining, self.MAX_CHUNK_SIZE) L.append(self.rfile.read(chunk_size)) size_remaining -= len(L[-1]) data = "".join(L) c_ip, c_port = self.client_address logger.info("[Request] Client %s:%s method POST: %s" % (c_ip, c_port, data)) response = self.server._marshaled_dispatch(data) status = 200 message = "Request accepted." except Exception: logger.exception("Exception processing request:") status = 500 message = "Internal Server Error." err_lines = traceback.format_exc().splitlines() trace_string = "%s | %s" % (err_lines[-3], err_lines[-1]) fault = Fault(-32603, "Server error: %s" % trace_string) response = fault.response() finally: logger.info("[Response] HTTP %d - %s" % (status, message)) self.send_response(status) logger.info("[Response] Content: %s" % repr(response)) self.send_header("Content-type", "application/json-rpc") self.send_header("Content-length", str(len(response))) self.end_headers() self.wfile.write(response) self.wfile.flush() self.connection.shutdown(1)
def prepare_astf_profile(self, filepath): logger.info("Processing prepare_astf_profile() command.") try: if not self._check_path_under_TRex_or_temp(filepath): raise Exception('Given path should be under current TRex package or /tmp/trex_files') profile = ASTFProfile.load(filepath) return ASTFProfile.to_json(profile) except Exception as e: err_str = "Error processing prepare_astf_profile(): %s" % e logger.error(err_str) return Fault(-33, err_str)
def _marshaled_dispatch(self, session_id, data, dispatch_method=None): response = None try: request = jsonrpclib.loads(data) except Exception as e: fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e)) response = fault.response() return response session = self.dispatcher.get_session_by_address(session_id) if not session: return 'Error: session not found' session.time = time.time() responses = [] if not isinstance(request, types.ListType): request = [request] for req_entry in request: result = validate_request(req_entry) if type(result) is Fault: responses.append(result.response()) continue self.dispatcher.do_dispatch(session, req_entry) if req_entry['method'] == 'server.stop': return json.dumps({'result': 'ok'}) r = self.poll_session(session) for item in r: responses.append(json.dumps(item)) if len(responses) > 1: response = '[%s]' % ','.join(responses) elif len(responses) == 1: response = responses[0] else: response = '' return response
def _marshaled_single_dispatch(self, request, dispatch_method=None): """ Dispatches a single method call :param request: A validated request dictionary :param dispatch_method: Custom dispatch method (for method resolution) :return: A JSON-RPC response dictionary, or None if it was a notification request """ # TODO - Use the multiprocessing and skip the response if # it is a notification method = request.get('method') params = request.get('params') try: # Call the method if dispatch_method is not None: response = dispatch_method(method, params) else: response = self._dispatch(method, params) except: # Return a fault exc_type, exc_value, _ = sys.exc_info() fault = Fault(-32603, '{0}:{1}'.format(exc_type, exc_value)) return fault.dump() if 'id' not in request or request['id'] in (None, ''): # It's a notification, no result needed # Do not use 'not id' as it might be the integer 0 return None # Prepare a JSON-RPC dictionary try: return jsonrpclib.dump(response, rpcid=request['id'], is_response=True) except: # JSON conversion exception exc_type, exc_value, _ = sys.exc_info() fault = Fault(-32603, '{0}:{1}'.format(exc_type, exc_value)) return fault.dump()
def stop_trex(self, seq): logger.info("Processing stop_trex() command.") if self.trex.get_seq()== seq: logger.debug("Abort request legit since seq# match") return self.trex.stop_trex() else: if self.trex.get_status() != TRexStatus.Idle: logger.warning("Abort request is only allowed to process initiated the run. Request denied.") return Fault(-33, 'Abort request is only allowed to process initiated the run. Request denied.') # raise at client TRexRequestDenied else: return False
def stop_trex(self): if self.status == TRexStatus.Idle: # TRex isn't running, nothing to abort logger.info("TRex isn't running. No need to stop anything.") if self.errcode is not None: # some error occurred, notify client despite TRex already stopped return Fault(self.errcode, self.verbose_status) # raise at client relevant exception, depending on the reason the error occured return False else: # handle stopping TRex's run self.session.join() logger.info("TRex session has been successfully aborted.") return True
def _marshaled_single_dispatch(self, request): # TODO - Use the multiprocessing and skip the response if # it is a notification # Put in support for custom dispatcher here # (See SimpleXMLRPCServer._marshaled_dispatch) method = request.get('method') params = request.get('params') try: response = self._dispatch(method, params) except: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response() if 'id' not in request.keys() or request['id'] == None: # It's a notification return None try: response = jsonrpclib.dumps(response, methodresponse=True, rpcid=request['id']) return response except: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response()
def _marshaled_dispatch(self, data, dispatch_method=None): response = None try: request = jsonrpclib.loads(data) except Exception as e: fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e)) response = fault.response() return response if not request: fault = Fault(-32600, 'Request invalid -- no request data.') return fault.response() if isinstance(request, list): # This SHOULD be a batch, by spec responses = [] for req_entry in request: result = validate_request(req_entry) if type(result) is Fault: responses.append(result.response()) continue resp_entry = self._marshaled_single_dispatch(req_entry) if resp_entry is not None: responses.append(resp_entry) if len(responses) > 0: response = '[%s]' % ','.join(responses) else: response = '' else: result = validate_request(request) if type(result) is Fault: return result.response() response = self._marshaled_single_dispatch(request) return response
def _marshaled_single_dispatch(self, request, dispatch_method=None): """ Dispatches a single method call :param request: A validated request dictionary :param dispatch_method: Custom dispatch method (for method resolution) :return: A JSON-RPC response dictionary, or None if it was a notification request """ method = request.get('method') params = request.get('params') # Prepare a request-specific configuration if 'jsonrpc' not in request and self.json_config.version >= 2: # JSON-RPC 1.0 request on a JSON-RPC 2.0 # => compatibility needed config = self.json_config.copy() config.version = 1.0 else: # Keep server configuration as is config = self.json_config try: # TODO - Use the multiprocessing if it is a notification # Call the method if dispatch_method is not None: response = dispatch_method(method, params) else: response = self._dispatch(method, params, config) except: # Return a fault exc_type, exc_value, _ = sys.exc_info() fault = Fault(-32603, '{0}:{1}'.format(exc_type, exc_value), config=config) return fault.dump() if 'id' not in request or request['id'] in (None, ''): # It's a notification, no result needed # Do not use 'not id' as it might be the integer 0 return None # Prepare a JSON-RPC dictionary try: return jsonrpclib.dump(response, rpcid=request['id'], is_response=True, config=config) except: # JSON conversion exception exc_type, exc_value, _ = sys.exc_info() fault = Fault(-32603, '{0}:{1}'.format(exc_type, exc_value), config=config) return fault.dump()
def _marshaled_dispatch(self, data, dispatch_method = None): response = None try: request = jsonrpclib.loads(data) except: fault = Fault(-32600, 'Request %s invalid.' % data) response = fault.response() return response version = get_version(request) if not version: fault = Fault(-32600, 'Request %s invalid.' % data) response = fault.response() return response if type(request) is types.ListType: # This SHOULD be a batch, by spec responses = [] for req_entry in request: resp_entry = self._marshaled_single_dispatch(req_entry) if resp_entry is not None: responses.append(resp_entry) response = '[%s]' % ','.join(responses) else: response = self._marshaled_single_dispatch(request) return response
class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): def __init__(self, encoding=None): SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, allow_none=True, encoding=encoding) def _marshaled_dispatch(self, data, dispatch_method=None, isNotification=[False]): response = None try: request = jsonrpclib.loads(data) except Exception, e: fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e)) response = fault.response() return response if not request: fault = Fault(-32600, 'Request invalid -- no request data.') return fault.response() if type(request) is types.ListType: # This SHOULD be a batch, by spec responses = [] for req_entry in request: result = validate_request(req_entry) if type(result) is Fault: responses.append(result.response()) continue isNotification[0] = 'id' not in request.keys( ) or request['id'] == None resp_entry = self._marshaled_single_dispatch( isNotification[0], req_entry) if resp_entry is not None: responses.append(resp_entry) if len(responses) > 0: response = '[%s]' % ','.join(responses) else: response = '' else: result = validate_request(request) if type(result) is Fault: return result.response() isNotification[0] = 'id' not in request.keys( ) or request['id'] == None response = self._marshaled_single_dispatch(isNotification[0], request) return response
def get_trex_version (self, base64 = True): try: logger.info("Processing get_trex_version() command.") if not self.trex_version: ret_code, stdout, stderr = run_command('./t-rex-64 --help', cwd = self.TREX_PATH) search_result = re.search('\n\s*(Version\s*:.+)', stdout, re.DOTALL) if not search_result: raise Exception('Could not determine version from ./t-rex-64 --help') self.trex_version = binascii.b2a_base64(search_result.group(1).encode(errors='replace')) if base64: return self.trex_version.decode(errors='replace') else: return binascii.a2b_base64(self.trex_version).decode(errors='replace') except Exception as e: err_str = "Can't get trex version, error: %s" % e logger.error(err_str) return Fault(-33, err_str)
def _marshaled_single_dispatch(self, request, dispatch_method=None): """ Dispatches a single method call :param request: A validated request dictionary :param dispatch_method: Custom dispatch method (for method resolution) :return: A JSON-RPC response dictionary, or None if it was a notification request """ method = request.get('method') params = request.get('params') # Prepare a request-specific configuration if 'jsonrpc' not in request and self.json_config.version >= 2: # JSON-RPC 1.0 request on a JSON-RPC 2.0 # => compatibility needed config = self.json_config.copy() config.version = 1.0 else: # Keep server configuration as is config = self.json_config # Test if this is a notification request is_notification = 'id' not in request or request['id'] in (None, '') if is_notification and self.__notification_pool is not None: # Use the thread pool for notifications if dispatch_method is not None: self.__notification_pool.enqueue(dispatch_method, method, params) else: self.__notification_pool.enqueue(self._dispatch, method, params, config) # Return immediately return None else: # Synchronous call try: # Call the method if dispatch_method is not None: response = dispatch_method(method, params) else: response = self._dispatch(method, params, config) except Exception as ex: # Return a fault fault = Fault(-32603, '{0}:{1}'.format(type(ex).__name__, ex), config=config) _logger.error("Error calling method %s: %s", method, fault) return fault.dump() if is_notification: # It's a notification, no result needed # Do not use 'not id' as it might be the integer 0 return None # Prepare a JSON-RPC dictionary try: return jsonrpclib.dump(response, rpcid=request['id'], is_response=True, config=config) except Exception as ex: # JSON conversion exception fault = Fault(-32603, '{0}:{1}'.format(type(ex).__name__, ex), config=config) _logger.error("Error preparing JSON-RPC result: %s", fault) return fault.dump()
return response def _marshaled_single_dispatch(self, request): # TODO - Use the multiprocessing and skip the response if # it is a notification # Put in support for custom dispatcher here # (See SimpleXMLRPCServer._marshaled_dispatch) method = request.get('method') params = request.get('params') try: response = self._dispatch(method, params) except Fault, fault: return fault.response() except: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response() if 'id' not in request.keys() or request['id'] == None: # It's a notification return None try: response = jsonrpclib.dumps(response, methodresponse=True, rpcid=request['id'] ) return response except: exc_type, exc_value, exc_tb = sys.exc_info() fault = Fault(-32603, '%s:%s' % (exc_type, exc_value)) return fault.response()