def _make_management_call(self, url, method="get", data=None): """ Makes a call to the Rabbit HTTP management API using the passed in HTTP method. """ log.debug("Calling rabbit API management (%s): %s", method, url) meth = getattr(requests, method) try: from putil.rabbitmq.rabbit_util import RabbitManagementUtil mgmt_cfg = RabbitManagementUtil.get_mgmt_config(CFG) with gevent.timeout.Timeout(10): r = meth(url, auth=(mgmt_cfg["username"], mgmt_cfg["password"]), data=data) r.raise_for_status() if not r.content == "": content = json.loads(r.content) else: content = None except gevent.timeout.Timeout as ex: raise Timeout(str(ex)) except requests.exceptions.Timeout as ex: raise Timeout(str(ex)) except (requests.exceptions.ConnectionError, socket.error) as ex: raise ServiceUnavailable(str(ex)) except requests.exceptions.RequestException as ex: # the generic base exception all requests' exceptions inherit from, raise our # general server error too. raise ServerError(str(ex)) return content
def _make_management_call(self, url, use_ems=True, method="get", data=None): """ Makes a call to the Rabbit HTTP management API using the passed in HTTP method. """ log.debug("Calling rabbit API management (%s): %s", method, url) if use_ems and self._ems_available(): log.debug("Directing call to EMS") content = self._ems_client.call_management( url, method, headers=self._build_security_headers()) else: meth = getattr(requests, method) try: username = CFG.get_safe( "container.exchange.management.username", "guest") password = CFG.get_safe( "container.exchange.management.password", "guest") with gevent.timeout.Timeout(10): r = meth(url, auth=(username, password), data=data) r.raise_for_status() if not r.content == "": content = json.loads(r.content) else: content = None except gevent.timeout.Timeout as ex: raise Timeout(str(ex)) except requests.exceptions.Timeout as ex: raise Timeout(str(ex)) except (requests.exceptions.ConnectionError, socket.error) as ex: raise ServiceUnavailable(str(ex)) except requests.exceptions.RequestException as ex: # the generic base exception all requests' exceptions inherit from, raise our # general server error too. raise ServerError(str(ex)) return content
def forward(self, *args, **kwargs): """ Forward a service method to the terrestrial endpoint through the service interface. """ func_name = kwargs.pop('func_name') try: link = kwargs.pop('link') except KeyError: link = True cid = '' try: remote_timeout = kwargs.pop('remote_timeout') if not isinstance(remote_timeout, int): remote_timeout = 0 elif remote_timeout < 0: remote_timeout = 0 elif remote_timeout == 0: pass else: cid = str(uuid.uuid4()) except KeyError: remote_timeout = 0 cmd = IonObject('RemoteCommand', resource_id=self._resource_id, svc_name=self._svc_name, command=func_name, command_id=cid, args=args, kwargs=kwargs) if remote_timeout == 0: return self._te_client.enqueue_command(cmd, link) else: if self._resource_id: origin = self._resource_id elif self._svc_name: origin = self._svc_name + self._xs_name pending_cmd = cmd async_result_evt = AsyncResult() def result_callback(evt, *args, **kwargs): """ Callback for subscriber retrive blocking results. """ #global async_result_evt if evt.type_ == 'RemoteCommandResult': cmd = evt.command if cmd.command_id == pending_cmd.command_id: async_result_evt.set(cmd) sub = EventSubscriber(event_type='RemoteCommandResult', origin=origin, callback=result_callback) sub.start() #self._pending_cmd = cmd cmd = self._te_client.enqueue_command(cmd, link) try: result = async_result_evt.get(timeout=remote_timeout) #self._pending_cmd = None sub.stop() except gevent.Timeout: #self._pending_cmd = None sub.stop() raise Timeout('Timed out waiting for remote result.') return result
def _control_flow(self): """ Main process thread of execution method. This method is run inside a greenlet and exists for each ION process. Listeners attached to the process, either RPC Servers or Subscribers, synchronize their calls by placing future calls into the queue by calling _routing_call. This is all done automatically for you by the Container's Process Manager. This method blocks until there are calls to be made in the synchronized queue, and then calls from within this greenlet. Any exception raised is caught and re-raised in the greenlet that originally scheduled the call. If successful, the AsyncResult created at scheduling time is set with the result of the call. """ if self.name: svc_name = "unnamed-service" if self.service is not None and hasattr(self.service, 'name'): svc_name = self.service.name threading.current_thread().name = "%s-%s-ctrl" % (svc_name, self.name) self._ready_control.set() for calltuple in self._ctrl_queue: calling_gl, ar, call, callargs, callkwargs, context = calltuple log.debug("control_flow making call: %s %s %s (has context: %s)", call, callargs, callkwargs, context is not None) res = None start_proc_time = int(get_ion_ts()) # check context for expiration if context is not None and 'reply-by' in context: if start_proc_time >= int(context['reply-by']): log.info("control_flow: attempting to process message already exceeding reply-by, ignore") # raise a timeout in the calling thread to allow endpoints to continue processing e = IonTimeout("Reply-by time has already occurred (reply-by: %s, op start time: %s)" % (context['reply-by'], start_proc_time)) calling_gl.kill(exception=e, block=False) continue # also check ar if it is set, if it is, that means it is cancelled if ar.ready(): log.info("control_flow: attempting to process message that has been cancelled, ignore") continue try: with self.service.push_context(context): with self.service.container.context.push_context(context): self._ctrl_current = ar res = call(*callargs, **callkwargs) except OperationInterruptedException: # endpoint layer takes care of response as it's the one that caused this log.debug("Operation interrupted") pass except Exception as e: # raise the exception in the calling greenlet, and don't # wait for it to die - it's likely not going to do so. # try decorating the args of the exception with the true traceback # this should be reported by ThreadManager._child_failed exc = PyonThreadTraceback("IonProcessThread _control_flow caught an exception (call: %s, *args %s, **kwargs %s, context %s)\nTrue traceback captured by IonProcessThread' _control_flow:\n\n%s" % (call, callargs, callkwargs, context, traceback.format_exc())) e.args = e.args + (exc,) # HACK HACK HACK # we know that we only handle TypeError and IonException derived things, so only forward those if appropriate if isinstance(e, (TypeError, IonException)): calling_gl.kill(exception=e, block=False) else: # otherwise, swallow/record/report and hopefully we can continue on our way self._errors.append((call, callargs, callkwargs, context, e, exc)) log.warn(exc) log.warn("Attempting to continue...") # have to raise something friendlier on the client side calling_gl.kill(exception=ContainerError(str(exc)), block=False) finally: proc_time = int(get_ion_ts()) - start_proc_time self._proc_time += proc_time self._ctrl_current = None ar.set(res)
def _control_flow(self): """ Entry point for process control thread of execution. This method is run by the control greenlet for each ION process. Listeners attached to the process, either RPC Servers or Subscribers, synchronize calls to the process by placing call requests into the queue by calling _routing_call. This method blocks until there are calls to be made in the synchronized queue, and then calls from within this greenlet. Any exception raised is caught and re-raised in the greenlet that originally scheduled the call. If successful, the AsyncResult created at scheduling time is set with the result of the call. """ if self.name: svc_name = "unnamed-service" if self.service is not None and hasattr(self.service, 'name'): svc_name = self.service.name threading.current_thread().name = "%s-%s" % (svc_name, self.name) thread_base_name = threading.current_thread().name self._ready_control.set() for calltuple in self._ctrl_queue: calling_gl, ar, call, callargs, callkwargs, context = calltuple request_id = (context or {}).get("request-id", None) if request_id: threading.current_thread().name = thread_base_name + "-" + str(request_id) #log.debug("control_flow making call: %s %s %s (has context: %s)", call, callargs, callkwargs, context is not None) res = None start_proc_time = get_ion_ts_millis() self._record_proc_time(start_proc_time) # check context for expiration if context is not None and 'reply-by' in context: if start_proc_time >= int(context['reply-by']): log.info("control_flow: attempting to process message already exceeding reply-by, ignore") # raise a timeout in the calling thread to allow endpoints to continue processing e = IonTimeout("Reply-by time has already occurred (reply-by: %s, op start time: %s)" % (context['reply-by'], start_proc_time)) calling_gl.kill(exception=e, block=False) continue # If ar is set, means it is cancelled if ar.ready(): log.info("control_flow: attempting to process message that has been cancelled, ignore") continue init_db_stats() try: # ****************************************************************** # ****** THIS IS WHERE THE RPC OPERATION/SERVICE CALL IS MADE ****** with self.service.push_context(context): with self.service.container.context.push_context(context): self._ctrl_current = ar res = call(*callargs, **callkwargs) # ****** END CALL, EXCEPTION HANDLING FOLLOWS ****** # ****************************************************************** except OperationInterruptedException: # endpoint layer takes care of response as it's the one that caused this log.debug("Operation interrupted") pass except Exception as e: if self._log_call_exception: log.exception("PROCESS exception: %s" % e.message) # Raise the exception in the calling greenlet. # Try decorating the args of the exception with the true traceback - # this should be reported by ThreadManager._child_failed exc = PyonThreadTraceback("IonProcessThread _control_flow caught an exception (call: %s, *args %s, **kwargs %s, context %s)\nTrue traceback captured by IonProcessThread' _control_flow:\n\n%s" % ( call, callargs, callkwargs, context, traceback.format_exc())) e.args = e.args + (exc,) # HACK HACK HACK # we know that we only handle TypeError and IonException derived things, so only forward those if appropriate if isinstance(e, (TypeError, IonException)): calling_gl.kill(exception=e, block=False) else: # otherwise, swallow/record/report and hopefully we can continue on our way self._errors.append((call, callargs, callkwargs, context, e, exc)) log.warn(exc) log.warn("Attempting to continue...") # have to raise something friendlier on the client side # calling_gl.kill(exception=ContainerError(str(exc)), block=False) # If exception string representation then calling calling_gl.kill(exception=ContainerError(str(exc)), block=False) # will crush the container. exceptions_str = str(exc) if len(exceptions_str) > 10000: exceptions_str = ( "Exception string representation is to large to put it all here. " "Begin and end of the exception:\n" + exceptions_str[:2000] + "\n...\n" + exceptions_str[-2000:] ) calling_gl.kill(exception=ContainerError(exceptions_str), block=False) finally: try: self._compute_proc_stats(start_proc_time) db_stats = get_db_stats() if db_stats: if self._warn_call_dbstmt_threshold > 0 and db_stats.get("count.all", 0) >= self._warn_call_dbstmt_threshold: stats_str = ", ".join("{}={}".format(k, db_stats[k]) for k in sorted(db_stats.keys())) log.warn("PROC_OP '%s.%s' EXCEEDED DB THRESHOLD. stats=%s", call.__module__, call.__name__, stats_str) elif self._log_call_dbstats: stats_str = ", ".join("{}={}".format(k, db_stats[k]) for k in sorted(db_stats.keys())) log.info("PROC_OP '%s.%s' DB STATS: %s", call.__module__, call.__name__, stats_str) clear_db_stats() except Exception: log.exception("Error computing process call stats") self._ctrl_current = None threading.current_thread().name = thread_base_name ar.set(res)
def _control_flow(self): """ Main process thread of execution method. This method is run inside a greenlet and exists for each ION process. Listeners attached to the process, either RPC Servers or Subscribers, synchronize their calls by placing future calls into the queue by calling _routing_call. This is all done automatically for you by the Container's Process Manager. This method blocks until there are calls to be made in the synchronized queue, and then calls from within this greenlet. Any exception raised is caught and re-raised in the greenlet that originally scheduled the call. If successful, the AsyncResult created at scheduling time is set with the result of the call. """ if self.name: svc_name = "unnamed-service" if self.service is not None and hasattr(self.service, 'name'): svc_name = self.service.name threading.current_thread().name = "%s-%s-ctrl" % (svc_name, self.name) self._ready_control.set() for calltuple in self._ctrl_queue: calling_gl, ar, call, callargs, callkwargs, context = calltuple log.debug("control_flow making call: %s %s %s (has context: %s)", call, callargs, callkwargs, context is not None) res = None start_proc_time = int(get_ion_ts()) # check context for expiration if context is not None and 'reply-by' in context: if start_proc_time >= int(context['reply-by']): log.info( "control_flow: attempting to process message already exceeding reply-by, ignore" ) # raise a timeout in the calling thread to allow endpoints to continue processing e = IonTimeout( "Reply-by time has already occurred (reply-by: %s, op start time: %s)" % (context['reply-by'], start_proc_time)) calling_gl.kill(exception=e, block=False) continue # also check ar if it is set, if it is, that means it is cancelled if ar.ready(): log.info( "control_flow: attempting to process message that has been cancelled, ignore" ) continue try: with self.service.push_context(context): with self.service.container.context.push_context(context): self._ctrl_current = ar res = call(*callargs, **callkwargs) except OperationInterruptedException: # endpoint layer takes care of response as it's the one that caused this log.debug("Operation interrupted") pass except Exception as e: # raise the exception in the calling greenlet, and don't # wait for it to die - it's likely not going to do so. # try decorating the args of the exception with the true traceback # this should be reported by ThreadManager._child_failed exc = PyonThreadTraceback( "IonProcessThread _control_flow caught an exception (call: %s, *args %s, **kwargs %s, context %s)\nTrue traceback captured by IonProcessThread' _control_flow:\n\n%s" % (call, callargs, callkwargs, context, traceback.format_exc())) e.args = e.args + (exc, ) # HACK HACK HACK # we know that we only handle TypeError and IonException derived things, so only forward those if appropriate if isinstance(e, (TypeError, IonException)): calling_gl.kill(exception=e, block=False) else: # otherwise, swallow/record/report and hopefully we can continue on our way self._errors.append( (call, callargs, callkwargs, context, e, exc)) log.warn(exc) log.warn("Attempting to continue...") # have to raise something friendlier on the client side calling_gl.kill(exception=ContainerError(str(exc)), block=False) finally: proc_time = int(get_ion_ts()) - start_proc_time self._proc_time += proc_time self._ctrl_current = None ar.set(res)
def _control_flow(self): """ Entry point for process control thread of execution. This method is run by the control greenlet for each ION process. Listeners attached to the process, either RPC Servers or Subscribers, synchronize calls to the process by placing call requests into the queue by calling _routing_call. This method blocks until there are calls to be made in the synchronized queue, and then calls from within this greenlet. Any exception raised is caught and re-raised in the greenlet that originally scheduled the call. If successful, the AsyncResult created at scheduling time is set with the result of the call. """ svc_name = getattr( self.service, "name", "unnamed-service") if self.service else "unnamed-service" proc_id = getattr(self.service, "id", "unknown-pid") if self.service else "unknown-pid" if self.name: threading.current_thread().name = "%s-%s" % (svc_name, self.name) thread_base_name = threading.current_thread().name self._ready_control.set() for calltuple in self._ctrl_queue: calling_gl, ar, call, callargs, callkwargs, context = calltuple request_id = (context or {}).get("request-id", None) if request_id: threading.current_thread( ).name = thread_base_name + "-" + str(request_id) #log.debug("control_flow making call: %s %s %s (has context: %s)", call, callargs, callkwargs, context is not None) res = None start_proc_time = get_ion_ts_millis() self._record_proc_time(start_proc_time) # check context for expiration if context is not None and 'reply-by' in context: if start_proc_time >= int(context['reply-by']): log.info( "control_flow: attempting to process message already exceeding reply-by, ignore" ) # raise a timeout in the calling thread to allow endpoints to continue processing e = IonTimeout( "Reply-by time has already occurred (reply-by: %s, op start time: %s)" % (context['reply-by'], start_proc_time)) calling_gl.kill(exception=e, block=False) continue # If ar is set, means it is cancelled if ar.ready(): log.info( "control_flow: attempting to process message that has been cancelled, ignore" ) continue init_db_stats() try: # ****************************************************************** # ****** THIS IS WHERE THE RPC OPERATION/SERVICE CALL IS MADE ****** with self.service.push_context(context), \ self.service.container.context.push_context(context): self._ctrl_current = ar res = call(*callargs, **callkwargs) # ****** END CALL, EXCEPTION HANDLING FOLLOWS ****** # ****************************************************************** except OperationInterruptedException: # endpoint layer takes care of response as it's the one that caused this log.debug("Operation interrupted") pass except Exception as e: if self._log_call_exception: log.exception("PROCESS exception: %s" % e.message) # Raise the exception in the calling greenlet. # Try decorating the args of the exception with the true traceback - # this should be reported by ThreadManager._child_failed exc = PyonThreadTraceback( "IonProcessThread _control_flow caught an exception " "(call: %s, *args %s, **kwargs %s, context %s)\n" "True traceback captured by IonProcessThread' _control_flow:\n\n%s" % (call, callargs, callkwargs, context, traceback.format_exc())) e.args = e.args + (exc, ) if isinstance(e, (TypeError, IonException)): # Pass through known process exceptions, in particular IonException calling_gl.kill(exception=e, block=False) else: # Otherwise, wrap unknown, forward and hopefully we can continue on our way self._errors.append( (call, callargs, callkwargs, context, e, exc)) log.warn(exc) log.warn("Attempting to continue...") # Note: Too large exception string will crash the container (when passed on as msg header). exception_str = str(exc) if len(exception_str) > 10000: exception_str = ( "Exception string representation too large. " "Begin and end of the exception:\n" + exception_str[:2000] + "\n...\n" + exception_str[-2000:]) calling_gl.kill(exception=ContainerError(exception_str), block=False) finally: try: # Compute statistics self._compute_proc_stats(start_proc_time) db_stats = get_db_stats() if db_stats: if self._warn_call_dbstmt_threshold > 0 and db_stats.get( "count.all", 0) >= self._warn_call_dbstmt_threshold: stats_str = ", ".join( "{}={}".format(k, db_stats[k]) for k in sorted(db_stats.keys())) log.warn( "PROC_OP '%s.%s' EXCEEDED DB THRESHOLD. stats=%s", svc_name, call.__name__, stats_str) elif self._log_call_dbstats: stats_str = ", ".join( "{}={}".format(k, db_stats[k]) for k in sorted(db_stats.keys())) log.info("PROC_OP '%s.%s' DB STATS: %s", svc_name, call.__name__, stats_str) clear_db_stats() if stats_callback: stats_callback(proc_id=proc_id, proc_name=self.name, svc=svc_name, op=call.__name__, request_id=request_id, context=context, db_stats=db_stats, proc_stats=self.time_stats, result=res, exc=None) except Exception: log.exception("Error computing process call stats") self._ctrl_current = None threading.current_thread().name = thread_base_name # Set response in AsyncEvent of caller (endpoint greenlet) ar.set(res)