def stop_service_logic(self, params, s_space, s_node, s_launcher): from rcmp_robotics_data import execute_int_robotics_data_query # TODO this operation must be atomic: in case of failure all has to come back as before ss_name = execute_int_robotics_data_query(self.get_ss_name_from_conn, params, "Unable to get ss_name from the connection '%s' - '%s'" % (params[self.PI_R_NAME_KEY], params[self.PI_S_NAME_KEY])) self._logger.debug("-- TMP -- Stopping service logic using params: %s and ss_name: %s" % (params, ss_name)) if ss_name: sl_items = execute_int_robotics_data_query(self.get_sl_sli, params, "Unable to get the service logic items from " "service logic of '%s'" % params[self.PI_R_NAME_KEY]) from rcmp_command import DELETE_SERVICE_SPACE, KILL_SERVICE_NODE, KILL_SERVICE_LAUNCHER, RCMPCommandHandler from rcmp_service_command import ServiceSpace, ServiceNode, ServiceLauncher if sl_items: for sli in sl_items: if sli[3]: # sli[0] is the name of the package for that item, sli[1] is the tag of the item # representing the node type in case of node or the launcher name in case of launcher # sli[2] is service_item type (node or launcher) and is used to know what type of # service we have to kill # sli[3] is service_logic_item si_pi_type_target (meaning where the item was launched # and can be S or R) cmd = {ServiceSpace.I_NAME_KEY: params[self.PI_S_NAME_KEY] if sli[3] == RCMPlatformNode.S_TYPE else params[self.PI_R_NAME_KEY], ServiceSpace.SS_NAME_KEY: ss_name} if sli[2] == ServiceSpace.SLI_N_TYPE: # node cmd[RCMPCommandHandler.COMMAND_KEY] = KILL_SERVICE_NODE # sli[4] is a name for the service item if sli[4]: cmd[ServiceNode.SN_NAME_KEY] = sli[4] else: cmd[ServiceNode.SN_PACKAGE_KEY] = sli[0] cmd[ServiceNode.SN_TYPE_KEY] = sli[1] s_node.stop(cmd) elif sli[2] == ServiceSpace.SLI_L_TYPE: # launcher cmd[RCMPCommandHandler.COMMAND_KEY] = KILL_SERVICE_LAUNCHER # sli[4] is a name for the service item if sli[4]: cmd[ServiceLauncher.SL_NAME_KEY] = sli[4] else: cmd[ServiceLauncher.SL_PACKAGE_KEY] = sli[0] cmd[ServiceLauncher.SL_F_LAUNCHER_KEY] = sli[1] s_launcher.stop(cmd) self._logger.debug("-- TMP -- %s stopped" % cmd) # once stopped the service logic items (nodes and launchers) we have to delete the service space ss_pi_type_target = execute_int_robotics_data_query(self.get_sl_ss_pi_type_target, params, "Unable to get the service space target from " "service logic of '%s'" % params[self.PI_R_NAME_KEY]) if ss_pi_type_target: self._logger.debug("Rollback update has to delete the service space on %s" % ss_pi_type_target) result = self.delete_all_of_ss(ss_name, s_space) if not result.is_ok(): self._logger.debug("-- TMP -- Forcing service space deletion") # if the cleaning in the right way failed we force the robotics data cleaning # and aspect that the unreachable platform node would clean himself execute_int_robotics_data_query(self.delete_ss, ss_name, "Unable to delete the service space '%s'" % ss_name)
def execute_update(self, params): from rcmp_robotics_data import execute_int_robotics_data_query if self.start_service_logic(params): # if the service logic launch went well we have to save this binding execute_int_robotics_data_query(self.create_conn, params, "Unable to update the connection between %s and %s" % (params[self.PI_S_NAME_KEY], params[self.PI_R_NAME_KEY])) # now the master know about the connection between the two ends of the pair and has to throw # a paired event to both self.throw_paired_event(params)
def rollback_update_conn(self, params, s_space, s_node, s_launcher): from rcmp_robotics_data import execute_int_robotics_data_query self.stop_service_logic(params, s_space, s_node, s_launcher) self._logger.debug("-- TMP -- Service logic stopped") # in every case we have to cancel the connection and the peer that results unreachable execute_int_robotics_data_query(self.delete_conn, params, "Unable to delete the connection between %s and %s" % (params[self.PI_S_NAME_KEY], params[self.PI_R_NAME_KEY])) self._logger.debug("-- TMP -- Connection deleted") # the following must be done to clean up all there is relative to the platform node instance # that results disconnected (and is not strictly related with the service logic) self.clean_up(params, s_space) self._logger.debug("-- TMP -- After clean_up")
def get_service_spaces(self, params): # Take PI_NAME_KEY from params and get all the service spaces running on that RCM # platform node instance. Add I_ADDRESS_KEY in params with the ip address of this # instance from rcmp_robotics_data import execute_int_robotics_data_query if self.PI_NAME_KEY not in params or (self.PI_NAME_KEY in params and not params[self.PI_NAME_KEY]): raise SyntaxError("%s is missing in the request" % self.PI_NAME_KEY) return execute_int_robotics_data_query(self.get_ss, params, "Unable to provide the list of service spaces " "for the %s named '%s'" % (self._who, params[self.PI_NAME_KEY]))
def clean_up_all(self, params, s_space): # this is to clean up all there is on the master that is not in the connection context from rcmp_robotics_data import execute_int_robotics_data_query self._logger.debug("-- TMP -- Clean_up_all with params: %s" % params) # we take all the service spaces rows = execute_int_robotics_data_query(self.get_ss_list_all, err_reason="Unable to get the remaining service spaces") if rows: for row in rows: if row: self.delete_all_of_ss(row[0], s_space) # at the end we do the rollback provisioning for all the platform node instances except the master rows = execute_int_robotics_data_query(self.get_pi_list_all, err_reason="Unable to get the remaining platform instances") if rows: for row in rows: if row: # self.roll_pi_back(row[0], row[1]) # for robots we roll back half while for servers we roll back fully self.roll_pi_back(row[0])
def delete_all_of_ss(self, ss_name, s_space): from rcmp_robotics_data import execute_int_robotics_data_query ss_info = execute_int_robotics_data_query(self.get_ss_info, ss_name, "Unable to get the service spaces info of %s" % ss_name) if ss_info: from rcmp_command import DELETE_L_SERVICE_SPACE, DELETE_SERVICE_SPACE, RCMPCommandHandler from rcmp_service_command import ServiceSpace cmd = {RCMPCommandHandler.COMMAND_KEY: DELETE_L_SERVICE_SPACE, PNodeInstance.I_ADDRESS_KEY: self.pni[RCMPlatformNode.PNI_ADDRESS], ServiceSpace.SS_ADDRESS_KEY: ss_info[0], ServiceSpace.SS_PORT_KEY: str(ss_info[1])} result = s_space.delete_local(cmd) else: result = RCMPMessage() result.create_error_response("Not enough info to delete services of '%s'" % ss_name) return result
def clean_up(self, params, s_space): # we cannot reach params[self.PI_NAME_KEY] because is the platform node that results # unreachable so we cannot do the right delete and we have to locally delete the remaining # services associated with that node from rcmp_robotics_data import execute_int_robotics_data_query self._logger.debug("-- TMP -- Clean_up with params: %s" % params) rows = execute_int_robotics_data_query(self.get_ss_list, params, "Unable to get the service spaces of %s" % params[self.PI_S_NAME_KEY] if params[self.PI_REACHABLE_KEY] == RCMPlatformNode.R_TYPE else params[self.PI_R_NAME_KEY]) if rows: self._logger.debug("-- TMP -- Service space missed in the rollback service logic") for row in rows: if row: self.delete_all_of_ss(row[0], s_space) # at the end we do the rollback provisioning for the disconnected platform node instance self.roll_pi_back(params[self.PI_S_NAME_KEY] if params[self.PI_REACHABLE_KEY] == RCMPlatformNode.R_TYPE else params[self.PI_R_NAME_KEY])
def execute_rollback_update(self, params): from rcmp_robotics_data import execute_int_robotics_data_query self._logger.debug("Executing rollback update with params: %s" % params) conns = execute_int_robotics_data_query(self.get_conn, params, "Unable to get the connections from '%s'" % params[self.PI_R_NAME_KEY] if self.PI_R_NAME_KEY in params else params[self.PI_S_NAME_KEY]) self._logger.debug("-- TMP -- conns: %s" % conns) if conns: from rcmp_service_command import ServiceSpace, ServiceNode, ServiceLauncher s_space = ServiceSpace(self.pni, self.msm) s_node = ServiceNode(self.pni, self.msm) s_launcher = ServiceLauncher(self.pni, self.msm) if isinstance(conns, list): # starting from a server we have a list of tuples for conn in conns: params[self.PI_R_NAME_KEY] = conn[0] self.rollback_update_conn(params, s_space, s_node, s_launcher) else: # starting from a robot we have only one tuple params[self.PI_S_NAME_KEY] = conns[0] self.rollback_update_conn(params, s_space, s_node, s_launcher)
def start_service_logic(self, params): # TODO this operation must be atomic: check if the solution is ok from rcmp_robotics_data import execute_int_robotics_data_query result = True ss_pi_type_target = execute_int_robotics_data_query(self.get_sl_ss_pi_type_target, params, "Unable to get the service space target from " "service logic of '%s'" % params[self.PI_R_NAME_KEY]) if ss_pi_type_target: ssl_flow = [] self._logger.debug("-- TMP -- Update has to create the service space on %s" % ss_pi_type_target) from rcmp_command import CREATE_SERVICE_SPACE, DELETE_SERVICE_SPACE, \ START_SERVICE_NODE, KILL_SERVICE_NODE, START_SERVICE_LAUNCHER, KILL_SERVICE_LAUNCHER, \ RCMPCommandHandler from rcmp_service_command import ServiceSpace, ServiceNode, ServiceLauncher s_space = ServiceSpace(self.pni, self.msm) s_node = ServiceNode(self.pni, self.msm) s_launcher = ServiceLauncher(self.pni, self.msm) # the update starts the service logic associated with the platform # node instance of type R at manual provisioning time cmd = {RCMPCommandHandler.COMMAND_KEY: CREATE_SERVICE_SPACE, ServiceSpace.I_NAME_KEY: params[self.PI_S_NAME_KEY] if ss_pi_type_target == RCMPlatformNode.S_TYPE else params[self.PI_R_NAME_KEY], ServiceSpace.SS_NAME_KEY: params[self.PI_R_NAME_KEY]} res = s_space.create(cmd) result = res.is_ok() if result: # the service space creation went well so we can add it in ssl_flow ssl_flow.append(cmd) # once started the service space associated with the service logic # we have to start all the remaining items of the service logic (nodes # and launchers) sl_items = execute_int_robotics_data_query(self.get_sl_sli, params, "Unable to get the service logic items from " "service logic of '%s'" % params[self.PI_R_NAME_KEY]) if sl_items: for sli in sl_items: if sli[0] and sli[1] and sli[2] and sli[3]: # all this parameters are mandatory # sli[0] is the name of the package for that item, sli[1] is the tag of the item # representing the node type in case of node or the launcher name in case of launcher # sli[2] is service_item type (node or launcher) and is used to know what type of # service we have to launch # sli[3] is service_logic_item si_pi_type_target (meaning where to launch # the item and can be S or R) cmd = {ServiceSpace.I_NAME_KEY: params[self.PI_S_NAME_KEY] if sli[3] == RCMPlatformNode.S_TYPE else params[self.PI_R_NAME_KEY], ServiceSpace.SS_NAME_KEY: params[self.PI_R_NAME_KEY]} if sli[2] == ServiceSpace.SLI_N_TYPE: # node cmd[RCMPCommandHandler.COMMAND_KEY] = START_SERVICE_NODE cmd[ServiceNode.SN_PACKAGE_KEY] = sli[0] cmd[ServiceNode.SN_TYPE_KEY] = sli[1] # the following are optional parameters: sli[4] is a name for the service item # and sli[5] is a list of params to pass to the service if sli[4]: cmd[ServiceNode.SN_NAME_KEY] = sli[4] if sli[5]: cmd[ServiceNode.SN_PARAMS_KEY] = sli[5] if sli[0] == ServiceNode.FIROS_PACKAGE and sli[1] == ServiceNode.FIROS_TYPE: firos_connected_cb = False try: while not firos_connected_cb: ss_ext_port = execute_int_robotics_data_query(self.get_ss_ext_port, cmd, "Unable to get an inbound " "free port for '%s' '%s' " "from service logic of '%s'" % (sli[0], sli[1], params[self.PI_R_NAME_KEY])) if ss_ext_port: res = s_node.start(cmd) if not res.is_ok() and \ ServiceNode.FIROS_FAILURE in res.get_response_reason(): # only in case of FIROS_FAILURE we retry to launch firos firos_connected_cb = False else: firos_connected_cb = True # result is needed for appending the result in the rollback trace result = res.is_ok() else: # no port found but not firos error so no more firos retry firos_connected_cb = True result = False except Exception as e: self._logger.error(e) result = False else: # the custom case of rcm rcmdriver.py is different because need to be managed on the # machine where we launch that node because has to check for a local free port if sli[0] == "webrtc" and sli[1] == "webRTC_master.py": cmd[ServiceNode.SN_PARAMS_KEY] = "%s r_name=%s" % \ (cmd[ServiceNode.SN_PARAMS_KEY], params[self.PI_R_NAME_KEY]) \ if cmd[ServiceNode.SN_PARAMS_KEY] else "r_name=%s" % \ params[self.PI_R_NAME_KEY] res = s_node.start(cmd) result = res.is_ok() elif sli[2] == ServiceSpace.SLI_L_TYPE: # launcher cmd[RCMPCommandHandler.COMMAND_KEY] = START_SERVICE_LAUNCHER cmd[ServiceLauncher.SL_PACKAGE_KEY] = sli[0] cmd[ServiceLauncher.SL_F_LAUNCHER_KEY] = sli[1] # the following are optional parameters: sli[4] is a name for the service item # and sli[5] is a list of params to pass to the service if sli[4]: cmd[ServiceLauncher.SL_NAME_KEY] = sli[4] if sli[5]: cmd[ServiceLauncher.SL_PARAMS_KEY] = sli[5] res = s_launcher.start(cmd) result = res.is_ok() if result: # the command went well so we add it in ssl_flow ssl_flow.append(cmd) else: # we found and error so we exit from the loop without adding ssl_flow # element break else: # some mandatory parameter missing in the service logic result = False if not result: # something went wrong so we have to rollback the service space creation while len(ssl_flow) > 0: # we pop (return the entry and delete from the list) from the last entry # in the list that hold all the cmd went well before the error cmd = ssl_flow.pop(-1) if cmd[RCMPCommandHandler.COMMAND_KEY] == CREATE_SERVICE_SPACE: cmd[RCMPCommandHandler.COMMAND_KEY] = DELETE_SERVICE_SPACE s_space.delete(cmd) elif cmd[RCMPCommandHandler.COMMAND_KEY] == START_SERVICE_NODE: cmd[RCMPCommandHandler.COMMAND_KEY] = KILL_SERVICE_NODE s_node.stop(cmd) elif cmd[RCMPCommandHandler.COMMAND_KEY] == START_SERVICE_LAUNCHER: cmd[RCMPCommandHandler.COMMAND_KEY] = KILL_SERVICE_LAUNCHER s_launcher.stop(cmd) self._logger.debug("-- TMP -- result: %s" % result) return result