def rpyc_send(self, data): """ Sends data to app via stdin :param data: Data to send over stdin. Could be an item like str, bytearray or list/tuple of items :return: True if data were sent successfully, otherwise - False """ start_time = time.time() if (self._mock is None or data == 'exit') and self._connection is None: return proto_failure("No connection. Ensure start is complete") try: if not isinstance(data, (list, tuple)): data = data, for m in data: if self._mock is None or data == 'exit': # TODO: why data=='exit' overrides self._mock? self._connection.root.send(m) else: try: if self._mock_eval: r = evaluate(m) else: r = m except Exception as e: r = None self._mock.append(str(r)) if self._mock is None or data == 'exit': # TODO: why data=='exit' overrides self._mock? self._connection.root.flush() except Exception as e: eprint("Platform {}: exception occurred during send: {}".format( self.name, e)) exprint() return proto_failure( "Failed to send due to exception {}".format(e), -2) tprint("rpyc_send elapsed {}".format(time.time() - start_time)) return proto_success(None)
def _start(self, reply_contexts): """ 1. Update's host from parent platform if necessary 2. Connects to a Caller :return: True if started successfully, otherwise - False """ if self._port is None and self._service is None: eprint( "Platform {} failed to start - port or service should be specified" .format(self.name)) return proto_failure( "Platform {} failed to start - port or service should be specified" ) if self._port is not None and self._service is not None: eprint( "Platform {} failed to start - specify only port or service, but not both" .format(self.name)) return proto_failure( "Platform {} failed to start - port or service should be specified" ) if self._host is None and self.parent is not None: c = self.request( new_message("platformix", "get", "host"), self._generic_request_handler, [], { "on_success": lambda d: setattr(self, "_host", d["host"]), "on_failure": lambda d: eprint("Failed to get host due to {}:{}".format( d["errcode"], d["state"])) }) if self._host is None: eprint("Platform {} failed to start - can't get host".format( self.name)) return proto_failure("Failed to start - can't get host") try: self._connection = rpyc.connect(self._host, self._port) # TODO: use service instead of port if it's specified if self._args is not None: self._connection.root.set_args(copy.copy(self._args)) result = self._connection.root.run() except Exception as e: self._connection = None eprint("Platform {} failed to start due to exception {}".format( self.name, e)) exprint() return proto_failure("Failed to start due to exception {}", -2) if result is False: self._connection = None eprint("Platform {} failed to start: rpyc server returned False". format(self.name)) return proto_failure("RPYC server returned False") if result and not self._mock: if self._start_sequence is not None and len( self._start_sequence) > 0: self.rpyc_send(self._start_sequence) return super(SoftwareRunner, self)._start(reply_contexts)
def calculate(self, expression): """ :param expression: string with expression to send to Calc App :return: ProtocolReply """ start_time = time.time() if self._mock: try: result = evaluate(expression) except ZeroDivisionError: result = None except Exception as e: result = "Platform {}: exception occurred on calculate: {}".format( self.name, e) exprint() tprint("calculate (with fail) elapsed {}".format(time.time() - start_time)) return proto_failure(result, -2) tprint("calculate elapsed {}".format(time.time() - start_time)) return proto_success(result) # NOTE: mock code just ended here. To avoid nesting there is no else, just flat code # TODO: optimize code - now it's way to hard (just send/receive and so much code!!!) c = self.request(new_message(self._io_interface, "send", expression), None, [], {}, timeout=2.0) # TODO: decrease timeout c_state = self._pop_request_state(c) if not self._request_state_is_success(c_state): tprint( "calculate (with fail result) elapsed {}".format(time.time() - start_time)) return proto_failure("IO failed to send data") c = self.request(new_message(self._io_interface, "receive"), None, [], {}, timeout=2.0) # TODO: decrease timeout c_state = self._pop_request_state(c) if not self._request_state_is_success(c_state): tprint( "calculate (with fail result) elapsed {}".format(time.time() - start_time)) return proto_failure("IO failed to receive response") # TODO: convert from string to number tprint("calculate elapsed {}".format(time.time() - start_time)) result = PM.parse(c_state["__message__"]).reply_data["value"] if isinstance( result, (list, tuple) ): # NOTE: softwarerunner returns list but stream_io returns single item result = result[0] if result.strip() == 'None': result = None else: try: result = int(result) except ValueError: result = float(result) return proto_success(result)
def rpyc_log(self): if self._connection is None: return proto_failure("No connection. Ensure start is complete") try: data = self._connection.root.log except Exception as e: eprint( "Platform {}: exception occurred while getting log: {}".format( self.name, e)) exprint() return proto_failure( "Failed to get log due to exception {}".format(e), -2) return proto_success(data)
def _sequencer_run(self, context, fake_reply, runs=None): if not self._ensure_running(context, fake_reply): return if self._worker.remaining > 0: self._reply(context, proto_failure("Already running"), fake_reply) self._notify(context, "Calling run...") self._reply(context, self._worker.run(context, runs), fake_reply)
def rpyc_receive(self, count=1, timeout=1.0): """ Get's data that were sent by app via stdout :param count: Amount of lines to receive. set count to 0 to receive as much as possible, and at least one message set count to -1 to receive as much as possible, but nothing is acceptable too :param timeout: Time in seconds to wait for data :return: True if successfully received Data, otherwise - False. Data itself is contained in a reply to channel and Data is list of strings """ start_time = time.time() if not isinstance(count, int) or count < -1: raise ValueError( "Count should be an integer in a range from -1 and up to +infinity" ) if self._mock is None and self._connection is None: return proto_failure("No connection. Ensure start is complete") try: data = [] if self._mock is not None: data = self._mock[:count] self._mock = self._mock[count:] else: while len(data) < count or count == 0 or count == -1: received = self._connection.root.receive(timeout) if received is None or len(received) == 0: break if isinstance(received, (list, tuple)): data += received else: data.append(received) except Exception as e: eprint("Platform {}: exception occurred during receive: {}".format( self.name, e)) exprint() return proto_failure( "Failed to receive due to exception {}".format(e), -2) tprint("rpyc_receive elapsed {}".format(time.time() - start_time)) if 0 < count != len(data): return proto_failure("Not all requested data were received") # TODO: need a way to return partially received data elif count == 0 and len(data) == 0: return proto_failure("No data were received") else: return proto_success(data)
def _ensure_connected(self, context, fake_reply): if self._worker.connection is None or self._worker.connection is False: self._reply( context, proto_failure( "No connection. Ensure start is complete successfully"), fake_reply) return False else: return True
def _platformix_call(self, context, fake_reply, method, *args, **kwargs): """ Calls host's method Call result is returned in a success message as value item :param context: messaging context :param method: method symbolic name :param args: args to method call :param kwargs: kwargs to method call :return: None """ if hasattr(self.host, method): if not callable(getattr(self.host, method)): self._reply( context, proto_failure("Attribute {} of {} is a property".format( property, self.host.name)), fake_reply) return try: result = getattr(self.host, method)(*args, **kwargs) except Exception as e: eprint( "Platformix protocol: failed to call method {} of {} with args {}, kwargs {} " "due to exception {}".format(method, self.host.name, args, kwargs, e)) exprint() self._reply( context, proto_failure( "Failed to call method {} of {} with args {}, kwargs {} " "due to exception {}".format(method, self.host.name, args, kwargs, e)), fake_reply) return self._reply(context, proto_success(result), fake_reply) else: self._reply( context, proto_failure("Method {} not found on {}".format( property, self.host.name)), fake_reply)
def _start(self, reply_contexts): """ 1. Update's host from parent platform if necessary 2. Connects to a Caller :return: True if started successfully, otherwise - False """ if self._port is None: eprint("Platform {} failed to start - port should be specified".format(self.name)) return proto_failure("Platform {} failed to start - port or service should be specified") if self._host is None and self.parent is not None: c = self.request(new_message("platformix", "get", "host"), self._generic_request_handler, [], {"on_success": lambda d: setattr(self, "_host", d["host"]), "on_failure": lambda d: eprint("Failed to get host due to {}:{}".format( d["errcode"], d["state"])) }) if self._host is None: eprint("Platform {} failed to start - can't get host".format(self.name)) return proto_failure("Failed to start - can't get host") try: timeout = time.time() + self._connect_timeout while True: try: if self._mock is None: self._sock = sock = socket.socket() sock.connect((self._host, self._port)) sock.settimeout(self._timeout) break except ConnectionRefusedError as e: if time.time() > timeout: raise e except Exception as e: self._sock = None eprint("Platform {} failed to start due to exception {}".format(self.name, e)) exprint() return proto_failure("Failed to start due to exception {}", -2) return super(TcpIO, self)._start(reply_contexts)
def _platformix_set(self, context, fake_reply, prop, value): """ Set host's property to a value :param context: messaging context :param prop: property symbolic name :param value: value to set :return: None """ if hasattr(self.host, prop): if not callable(getattr(self.host, prop)): try: setattr(self.host, prop, value) except Exception as e: eprint( "Platformix protocol: failed to set attribute {} of {} to value {} " "due to exception {}".format(prop, self.host.name, value, e)) exprint() self._reply( context, proto_failure( "Failed to set attribute {} of {} to value {} " "due to exception {}".format( prop, self.host.name, value, e)), fake_reply) return self._reply(context, proto_success(getattr(self.host, prop), prop), fake_reply) else: self._reply( context, proto_failure("Attribute {} of {} is a method".format( prop, self.host.name)), fake_reply) else: self._reply( context, proto_failure("Property {} not found on {}".format( prop, self.host.name)), fake_reply)
def tcp_send(self, data): """ Sends data to app via stdin :param data: Data to send over stdin. Could be an item like str, bytearray or list/tuple of items :return: True if data were sent successfully, otherwise - False """ start_time = time.time() if self._mock is None and self._sock is None: return proto_failure("No connection. Ensure start is complete") try: if not isinstance(data, (list, tuple)): data = data, for m in data: if self._mock is None: if not isinstance(m, bytearray): if isinstance(m, str): m = m.encode('UTF-8') else: return proto_failure("Send data is expected to be a string, bytearray or " "list/tuple of strings and bytearrays") self._sock.sendall(m) else: try: if self._mock_eval and isinstance(m, str): r = evaluate(m) else: r = m except Exception as e: r = None self._mock.append(str(r)) except Exception as e: eprint("Platform {} failed to send due to exception {}".format(self.name, e)) exprint() return proto_failure("Failed to send due to exception {}".format(e), -2) tprint("tcp_send elapsed {}".format(time.time() - start_time)) return proto_success(None)
def _platformix_report(self, context, fake_reply, kind): """ Reports about platform's state Currently only running and is_running supported * running - replies with retval True if platform is running, and False otherwise * is_running - successful reply if platform is running, and failure otherwise :param context: :param kind: :return: """ if kind == "running": self._reply(context, proto_success(["False", "True"][self._worker.running]), fake_reply) return elif kind == "is_running": if self._worker.running: self._reply(context, proto_success("True", "state"), fake_reply) else: self._reply(context, proto_failure("False"), fake_reply) else: self._reply(context, proto_failure("Unknown report request"), fake_reply)
def _platformix_get(self, context, fake_reply, prop): """ Get host's property. Value is returned in a success message as item with index same as property name :param context: messaging context :param prop: property symbolic name :return: None """ if hasattr(self.host, prop): self._reply(context, proto_success(getattr(self.host, prop), prop), fake_reply) else: self._reply( context, proto_failure("Property {} not found on {}".format( prop, self.host.name)), fake_reply)
def _start(self, reply_contexts): """ Makes sure that incoming/outgoing streams are synchronised :return: True if all necessary actions complete successfully, otherwise - False """ # Flush any junk that could occur on app start (a common case example) # NOTE: Or you could wait in a loop for a specific message depending on app used if not self._mock: c = self.request(new_message(self._io_interface, "receive", -1), None, [], {}, timeout=2.0) # TODO: cleaning required for softwarerunner only # TODO: reduce receive timeout instead of increasing request timeout c_state = self._pop_request_state(c) if not self._request_state_is_success(c_state): return proto_failure("Failed to flush i/o on start") return super(Calc, self)._start(reply_contexts)
def _platformix_start( self, context, fake_reply): # TODO: Recursive option to start nested platforms """ Starts platform instance (actually checks that necessary conditions are met and calls startup worker method) If parent platform or platforms in wait list are not running yet - will wait for them If start were deferred due to waiting of other platforms then start method should be called again later Usually it happens automatically after other platforms are replying on their's startup end :param context: messaging context :return: None """ assert fake_reply is None, "platformix_start replies shouldn't be faked!" if self._worker.running: # If already running - just do nothing self._reply(context, proto_success("already running", "state"), None) return if self._worker.stopping: # If in the middle of stop - do nothing self._reply(context, proto_failure("stop is in progress"), None) return new_thread = False if self._worker.starting: # If were already starting - update reply list if context not in self._context["reply_to"]: new_thread = True self._context["reply_to"].append(context) self._notify(context, "waiting") else: new_thread = True self._worker.starting = True self._context = { "action": "start", "reply_to": [context], "waiting_for": [], "wait_ignore": [] } self._notify(context, "received start signal") # TODO: do recursive start? parent->childs? and call only root platforms to get up and running? # TODO: lock as validation can intersect with stop action since stop can be called from different threads if not self._validate_context({"action": "start"}): return if not self._worker.starting: # NOTE: in case if starting were interrupted by stop - just return return # Update waiting list self._context["waiting_for"] = [ w for w in self._worker.wait if self._worker.farm.is_running(w) is False ] # If there is some platforms to wait - notify about this if self.waiting_count > 0 and new_thread: self._worker.register_reply_handler( context, self._platformix_start_reply_handler, [], {}, timeout=self._worker.start_max_wait, force=True) self._notify(context, "waiting") # If no one left to wait for - do stop at last elif not self._worker.start_in_progress and self.waiting_count == 0: for c in self._context["reply_to"]: try: self._worker.unregister_reply_handler(c, True, {}, dont_check=True) except AssertionError: pass self._worker.start_in_progress = True self._notify_all(self._context["reply_to"], "launching") if self._validate_context({"action": "start"}): result = self._worker.start(self._context["reply_to"]) result_error = not isinstance(result, ProtocolReply) else: result = None result_error = False if self._validate_context({"action": "start"}): reply_to = self._context["reply_to"] self._context = None else: return # TODO: probably need to fid a way to reply failure in that case assert result_error is False, "Worker should return result as ProtocolReply instance" if result is not None: if result.success: self._reply_all(reply_to, result, None) else: self._reply_all(reply_to, result, None)
def _stop(self, reply_contexts): """ 1. Sends exit sequence to app via stdin 2. Receives exit log if necessary 3. Closes connection to a Caller :return: """ stop_failed = None try: if self._connection is not None: # Check before proceeding as it can be emergency stop if self._exit_sequence is not None and len( self._exit_sequence) > 0: self.rpyc_send(self._exit_sequence) exit_time = time.time() + self._stop_timeout forced_stop = False else: self._connection.root.stop( "Stopped by intent of SoftwareRunner (_stop)", force=True) exit_time = time.time() + self._stop_timeout forced_stop = True while self._connection.root.running and ( not forced_stop or time.time() < exit_time): if not self._connection.root.running: break if time.time() >= exit_time and (not forced_stop): self._connection.root.stop( "Forced to stop by intent of SoftwareRunner (_stop)", force=True) exit_time = time.time() + self._stop_timeout forced_stop = True stop_failed = "Not stopped by stop sequence" if self._connection.root.running: if stop_failed is not None: stop_failed += ", " else: stop_failed = "" stop_failed += "App not stopped" # TODO: check whether isntance is running and call stop explicitly if self._display_log_on_stop: data = list(self._connection.root.log) vprint("Platform {}. Exit log: {}".format( self.name, '\n'.join(data))) self._connection.close() except Exception as e: eprint("Platform {} experienced exception on stop: {}".format( self.name, e)) exprint() self._reply_all( reply_contexts, PM.notify( "abnormal_stop: experienced exception on stop: {}".format( e))) if stop_failed is not None: stop_failed += ", " else: stop_failed = "" stop_failed += "Experienced exception: {}".format(e) if stop_failed is not None: eprint("Software runner {} failed to stop app properly: {}".format( self.name, stop_failed)) self._reply_all( reply_contexts, PM.notify("Software runner {} failed to stop app properly: {}". format(self.name, stop_failed))) self._connection = None super_result = super(SoftwareRunner, self)._stop(reply_contexts) if stop_failed: return proto_failure(stop_failed) else: return super_result
def _platformix_smoke_test(self, tests_list=None): env = self._env # Accepts following values in tests_list: # "success, fake, report" # "no fail check" # TODO: check if there is required amount of responses # Start # NOTE: startup is made by testenv itself now # assert env.transaction("#platforms", new_message("platformix", "start")) is True, "Failed to start platforms" # Test errors control if tests_list is not None: tests_list = list(tests_list) if tests_list is None or "[sanity check]" in tests_list: if tests_list is not None: del tests_list[tests_list.index("[sanity check]")] if env.transaction( "@platform_b", fake_op_message("platformix", proto_failure("Failed by intent (fake_next_op, " "Single unit error in [all success] condition)"), on_channel="#platforms", on_message=new_message("platformix", "report", "is_running") ), er.all_success + er.none_fail) is False: eprint("Failed to set fake_next_op on [sanity check]") return False # NOTE: both all and none in condition above are for testing purpose # No fail since it's another channel if env.transaction("@platform_b", new_message("platformix", "report", "is_running"), er.none_fail) is False: # NOTE: none in condition above is testing purpose eprint("Failed to pass [report is_running] or avoid faking on [sanity check]") return False # No fail since it's another command if env.transaction("#platforms", new_message("platformix", "report", "running")) is False: eprint("Failed to pass [report running] or avoid faking on [sanity check]") return False # Fail as we told platform_b to do so if env.transaction("#platforms", new_message("platformix", "report", "is_running")) is True: eprint("Failed to pass faking or [all success] fail check on [sanity check]") return False if tests_list is None or "[single fail] success check" in tests_list: if tests_list is not None: del tests_list[tests_list.index("[single fail] success check")] if env.transaction( "@platform_b", fake_op_message("platformix", proto_failure("Failed by intent (fake_next_op, Single unit error " "in [single fail] condition)"), on_channel="#platforms", execute=True )) is False: eprint("Failed to set fake_next_op on [single fail] success check") return False # No fail as we expecting that platform_b about to fail (NOTE: - no message filtering here) if env.transaction("#platforms", new_message("platformix", "report", "running"), er.fail("platform_b") + er.others_success) is False: eprint("Failed to pass [single fail] success check") return False if tests_list is None or "[any success] fail check" in tests_list: if tests_list is not None: del tests_list[tests_list.index("[any success] fail check")] if env.transaction( "#platforms", fake_op_message("platformix", proto_failure("Failed by intent (fake_next_op, All fail " "on [any success] condition)"), on_channel="#platforms" )) is False: eprint("Failed to set fake_next_op on [any success] fail check") return False # Should fail if env.transaction("#platforms", new_message("platformix", "report", "running"), er.any_success) is True: eprint("Failed to pass [any success] fail check") return False if tests_list is None or "[not(any success)] fail check" in tests_list: if tests_list is not None: del tests_list[tests_list.index("[not(any success)] fail check")] # Should pass as condition is negated if env.transaction("#platforms", new_message("platformix", "report", "running"), [("any", "success", True)]) is True: eprint("Failed to pass [not(any success)] fail check") return False if tests_list is None or "[any fail] success check" in tests_list: if tests_list is not None: del tests_list[tests_list.index("[any fail] success check")] if env.transaction( "@platform_b", fake_op_message("platformix", proto_failure("Failed by intent (fake_next_op, Should pass " "as condition is negated)"), on_channel="#platforms" )) is False: eprint("Failed to set fake_next_op on [any fail] success check") return False # Should pass as one item failed, as expected if env.transaction("#platforms", new_message("platformix", "report", "running"), er.any_fail) is False: eprint("Failed to pass [any fail] success check") return False # Check request without response (should fail) if tests_list is None or "[no response] fail check" in tests_list: if tests_list is not None: del tests_list[tests_list.index("[no response] fail check")] if env.transaction("__void__", new_message(None, "platformix", "report", "running"), er.all_fail) is True: eprint("Failed to pass [no response] fail check") return False # Stop # NOTE: stop is made by testenv itself now # assert env.transaction("#platforms", new_message("platformix", "stop")) is True, "Failed to stop platforms" if tests_list is not None and len(tests_list) > 0: eprint("There is unknown tests left in a list: {}".format(tests_list)) return False return True
def _platformix_stop(self, context, fake_reply): # TODO: Force parameter """ Stops platform instance Breaks startup process if the platform is in middle of startup Waits before nested platforms are stopped before actually do stop # TODO: also should wait platforms which are waiting for this one on start If parent platform or platforms in wait list are not running yet - will wait for them If stop were deferred due to waiting of other platforms then stop method should be called again later Usually it happens automatically after other platforms are replying on their's stopping end :param context: messaging context :return: None """ assert fake_reply is None, "platformix_stop replies shouldn't be faked!" stopping = self._worker.stopping # Store current stopping state need_stop = self._worker.stopping = self._worker.running or self._worker.starting self._worker.stopping = True # Set _stopping right in the beginning new_thread = False if not stopping and self._context is not None: # Break startup process if necessary self._reply_all(self._context["reply_to"], proto_failure("interrupted by stop"), None) if self._worker.starting: self._worker.starting = False self._worker.start_in_progress = False self._context = None if not stopping and not need_stop: # If not running and not starting - do nothing more self._worker.stopping = False self._reply(context, proto_success("already stopped", "state"), None) return if stopping: # If were already stopping - update reply list if context not in self._context["reply_to"]: new_thread = True self._context["reply_to"].append(context) else: # Otherwise initiate context new_thread = True self._context = { "action": "stop", "reply_to": [context], "waiting_for": [], "wait_ignore": [] } self._notify(context, "received stop signal") # TODO: do recursive stop? parent->childs? and call only root platforms stop? assert self._worker.stopping, "At this point stopping should be True" # Update waiting list # TODO: also wait those that are depends on this one self._context["waiting_for"] = [ w.name for w in self.host.subplatforms + self.host.depended if w.running is True or w.stopping is True and w.name not in self._context["wait_ignore"] ] # If there is some platforms to wait - notify about this if self.waiting_count > 0 and new_thread: self._worker.register_reply_handler( context, self._platformix_stop_reply_handler, [], {}, timeout=self._worker.stop_max_wait, force=True) self._notify_all(self._context["reply_to"], "waiting") # If no one left to wait for - do stop at last elif not self._worker.stop_in_progress and self.waiting_count == 0: for c in self._context["reply_to"]: self._worker.unregister_reply_handler(c, True, {}, dont_check=True) self._worker.running = False self._worker.stop_in_progress = True self._notify_all(self._context["reply_to"], "stopping") result = self._worker.stop(self._context["reply_to"]) reply_to = self._context["reply_to"] self._context = None assert isinstance( result, ProtocolReply ), "Worker should return result as ProtocolReply instance" if result.success: self._reply_all(reply_to, proto_success(None), None) else: self._reply_all(reply_to, result, None)
def tcp_receive(self, count=0, timeout=None, decode='UTF-8'): """ Get's data that were sent by app via stdout :param count: Amount of bytes to receive. set count to 0 to receive as much as possible, at least something set count to -1 to receive as much as possible, but nothing is acceptable too :param timeout: Time in seconds to wait for data. If None then TCP socket timeout is used :param deoode: If not None then received data is decoded into string using specified decoder. Default: 'UTF-8' :return: True if successfully received Data, otherwise - False. Data itself is contained in a reply to channel and Data is list of strings """ start_time = time.time() if not isinstance(count, int) or count < -1: raise ValueError("Count should be an integer in a range from -1 and up to +infinity") if self._mock is None and self._sock is None: return proto_failure("No connection. Ensure start is complete") try: if self._mock is not None: data = self._mock.pop(0) else: data = bytearray() if count < 1: recv_size = 1024 else: recv_size = count if timeout is not None: timeout = time.time() + timeout while len(data) < count or count == 0 or count == -1: try: received = self._sock.recv(recv_size) except TimeoutError: received = None except socket.timeout: received = None if received is None: if count < 0: break if count == 0 and len(data) > 0: break else: data += received if timeout is not None and time.time() > timeout: break if count > 0: recv_size = count - len(data) if decode is not None: data = data.decode(decode) except Exception as e: eprint("Platform {} failed to receive due to exception {}".format(self.name, e)) exprint() return proto_failure("Failed to receive due to exception {}".format(e), -2) tprint("rpyc_receive elapsed {}".format(time.time() - start_time)) if 0 < count != len(data): return proto_failure("Not all requested data were received") # TODO: need a way to return partially received data elif count == 0 and len(data) == 0: return proto_failure("No data were received") else: return proto_success(data)
def _platformix_start_reply_handler(self, context, message, dry_run, timeouted): """ Handles replies for start operation (required when platform is waiting another one) :param context: messaging context :param message: message content :param dry_run: True if it's only required to check whether this message would be processed :param timeouted: True if handler were called due to timeout. In this case message and context are invalid :return: True if message were processed (can be processed) otherwise False """ if not timeouted: # Ignore messages other than success or failure if not message.is_failure and not message.is_success: return False if not self._validate_context({"action": "start"}): return False if timeouted or message.sender in self._context["waiting_for"]: if dry_run: if timeouted: return False else: return True if timeouted or message.is_failure: self._worker.starting = False self._worker.start_in_progress = False self._context["action"] = "start_failed" for c in self._context["reply_to"]: try: status = {} if timeouted: status["__timeouted__"] = True if message.is_failure: status[ "dependency failed to start"] = message.sender self._worker.unregister_reply_handler(c, False, status, dont_check=True) except AssertionError: pass if not timeouted: self._reply_all( self._context["reply_to"], proto_failure( "Aborting start due to platform {} failed to start" .format(message.sender)), None) else: self._reply_all( self._context["reply_to"], proto_failure( "Aborting start due to wait timeout. Platforms {} have not started within " "timeout {}".format(self._context["waiting_for"], self._worker.start_max_wait)), None) self._context = None if timeouted: return False else: return True else: # Success self._platformix_start(context, None) return True return False