def abortService(ctx, serviceID, sessionToken): """Aborts the currently running service (not implemented, returns false) """ logging.info( "abortService() called with service ID {}".format(serviceID)) # We obtain the authentication-manager endpoint from a class property # and check that the session token is valid # Read wsdl URL from a file waiterdir = os.path.join(WAITER_LOG_FOLDER, serviceID) wsdlfile = os.path.join(waiterdir, 'wsdl.txt') with open(wsdlfile) as f: auth_wsdl = f.read().strip() auth = AuthClient(auth_wsdl) if not auth.validate_session_token(sessionToken): error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # This method offers the option to abort long-running asynchronous # services. In this example, we do not implement this functionality # and thus always return False. # In a more realistic scenario, this method would terminate the # background computation process gracefully. return False
def hpcprepWaiter(sessionToken, extraParameters, seconds_to_wait): """Creates all parameters required as input by the HPC launcher. Most parameters are hard-coded here, but in other scenarios (for example with user-defined input), some more string handling will happen here. """ # Validate session token ep = ExtraParameters(extraParameters) auth = AuthClient(ep.get_auth_WSDL_URL()) if not auth.validate_session_token(sessionToken): logging.error("Token validation failed") error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # Prepare parameters the HPC launcher needs image_name = "waiter_abortable.simg" commandline = "python" parameters = "/app/startup.py {} /app".format(seconds_to_wait) queue = "qexp" numNodes = os.environ["N_NODES"] numCores = os.environ["N_CORES"] maxDurationInMinutes = 5 SingularityVersion = "2.4.2" return (image_name, commandline, parameters, queue, numNodes, numCores, maxDurationInMinutes, SingularityVersion)
def showDialog(serviceID, sessionToken, extraParameters): """ Starts the dialog application. """ logging.info( "startDialog() called with service ID {}".format(serviceID)) # Check that the session token is valid and abort with a SOAP fault if # it's not. To that end, we use the clfpy library both to extract the # authentication endpoint from the extraParameters input argument and # to communicate with that endpoint. ep = ExtraParameters(extraParameters) auth = AuthClient(ep.get_auth_WSDL_URL()) if not auth.validate_session_token(sessionToken): logging.error("Token validation failed") error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # The entire application, which will be visible to the user running the # workflow, is packed in the status report created in this method. # Here, we create a simple HTML page with a button to continue the # workflow. status = base64.b64encode( create_html_dialog(serviceID, sessionToken, ep.get_WFM_endpoint()).encode()).decode() result = "UNSET" return (status, result)
def abortService(ctx, serviceID, sessionToken): """Aborts the currently running service (not implemented, returns false) """ logging.info(f"abortService() called with service ID {serviceID}") # We obtain the authentication-manager endpoint from a class property # and check that the session token is valid # Read wsdl URL from a file logdir = os.path.join(LOG_FOLDER, serviceID) wsdlfile = os.path.join(logdir, 'wsdl.txt') with open(wsdlfile) as f: auth_wsdl = f.read().strip() auth = AuthClient(auth_wsdl) if not auth.validate_session_token(sessionToken): error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) pidfile = os.path.join(logdir, 'pid.txt') with open(pidfile) as f: pid = f.read().strip() try: os.kill(int(pid), signal.SIGKILL) except Exception as ex: print('Exception while aborting consumer') print(str(ex)) return False return True
def getServiceStatus(ctx, serviceID, sessionToken): """Status-query method which is called regularly by WFM. Here, a more realistic service would query the status of a calculation etc. and process its log files to create a status page. Here, the log contains only a single number, which we convert to an html progress bar. """ logging.info( "getServiceStatus() called with service ID {}".format(serviceID)) # We obtain the authentication-manager endpoint from a class property # and check that the session token is valid auth = AuthClient(ctx.descriptor.service_class.auth_wsdl) if not auth.validate_session_token(sessionToken): logging.error("Token validation failed") error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # Create correct file paths from service ID. By using the unique # service ID, we can address the right waiter process in case this # service is called several times in parallel. waiterdir = os.path.join(WAITER_LOG_FOLDER, serviceID) statusfile = os.path.join(waiterdir, 'status.txt') resultfile = os.path.join(waiterdir, 'result.txt') # Read the current status from the waiter logs. Here, that is only a # single number between 0 and 100. with open(statusfile) as f: current_status = f.read().strip() if current_status == "100": logging.info("Waiting completed") status = "COMPLETED" # Read result page from waiter with open(resultfile) as f: result = f.read() return (status, result) # Note that the interface definition of getServiceStatus() specifies # "UNCHANGED" as another option for the return value of # 'status_base64'. In this case, the workflow manager will simply # continue to display the last status page transmitted. This can be # used when the status-page generation in itself is costly. # If not finished, create a status page from the current status # This could include more post-processing etc. in a more realistic # service result = "UNSET" status = base64.b64encode( create_html_progressbar(int(current_status)).encode()).decode() return (status, result)
def parameterDebugger(serviceID, sessionToken, extraParameters, in1="", label1="in1", in2="", label2="in2", in3="", label3="in3", in4="", label4="in4", in5="", label5="in5"): """ Starts the debugger application. """ logging.info( "parameterDebugger() called with service ID {}".format(serviceID)) # Validate token ep = ExtraParameters(extraParameters) auth = AuthClient(ep.get_auth_WSDL_URL()) if not auth.validate_session_token(sessionToken): logging.error("Token validation failed") error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # Create application HTML html = HTML.format( sid=serviceID, stk=sessionToken, wfm_endpoint=ep.get_WFM_endpoint(), eP=extraParameters, in1=in1, in2=in2, in3=in3, in4=in4, in5=in5, label1=label1, label2=label2, label3=label3, label4=label4, label5=label5, result=RES_B64, ) status = base64.b64encode(html.encode()).decode() return status
def hpcPrepWithFile(sessionToken, extraParameters, filepath, textinput): """Creates parameters required as input by the HPC launcher. """ # Validate session token ep = ExtraParameters(extraParameters) auth = AuthClient(ep.get_auth_WSDL_URL()) if not auth.validate_session_token(sessionToken): logging.error("Token validation failed") error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # Prepare parameters the HPC launcher needs commandline = "python" parameters = "/app/startup.py {} {}".format(filepath, textinput) queue = "qexp" numNodes = os.environ["N_NODES"] numCores = os.environ["N_CORES"] return (commandline, parameters, queue, numNodes, numCores)
def abortService(ctx, serviceID, sessionToken): """Aborts the currently running service (not implemented, returns false) """ logging.info( "abortService() called with service ID {}".format(serviceID)) # We obtain the authentication-manager endpoint from a class property # and check that the session token is valid auth = AuthClient(ctx.descriptor.service_class.auth_wsdl) if not auth.validate_session_token(sessionToken): error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # This method offers the option to abort long-running asynchronous # services. In this example, we do not implement this functionality # and thus always return False. # In a more realistic scenario, this method would terminate the # background computation process gracefully. return False
def startWaiter(ctx, serviceID, sessionToken, extraParameters, secondsToWait): """Starts a waiter script as a separate process and returns immediately. In a more realistic scenario, this is where a longer computation etc. would be started as a separate process. Here, we simply start a process which waits for a while while regularly updating a status file. """ logging.info( "startWaiter() called with service ID {}".format(serviceID)) # Check that the session token is valid and abort with a SOAP fault if # it's not. To that end, we use the clfpy library both to extract the # authentication endpoint from the extraParameters input argument and # to communicate with that endpoint. We also save the authentication- # manager endpoint in a class property which we can re-use in methods # that don't have the extraParameters as an argument. ep = ExtraParameters(extraParameters) auth = AuthClient(ep.get_auth_WSDL_URL()) if not auth.validate_session_token(sessionToken): logging.error("Token validation failed") error_msg = "Session-token validation failed" raise TokenValidationFailedFault(faultstring=error_msg) # Add default value for waiting time if secondsToWait is None: logging.info("Setting default value for waiting time") secondsToWait = 60 # Create a temporary folder to store the status files in. # Note that we use the service ID as a unique identifier. Since this # service is stateless, subsequent calls to getServiceStatus() need to # be able to read the correct status files. (The service can be started # several times in parallel.) # In a more realistic setting, one would set up log directories for a # computation here. waiterdir = os.path.join(WAITER_LOG_FOLDER, serviceID) if not os.path.exists(waiterdir): os.mkdir(waiterdir) statusfile = os.path.join(waiterdir, 'status.txt') resultfile = os.path.join(waiterdir, 'result.txt') # Store the auth-manager WSDL URL in a file for later use in # getServiceStatus() wsdlfile = os.path.join(waiterdir, 'wsdl.txt') with open(wsdlfile, 'w') as f: f.write(ep.get_auth_WSDL_URL()) logging.info("Stored auth-manager WSDL URL: {}".format( ep.get_auth_WSDL_URL())) # Spawn new process running the waiter script. # We pass the status and result file to the script to ensure that the # waiter logs to the correct place. logging.info("Starting waiter script") command = [ 'python', 'wait_a_while.py', str(secondsToWait), statusfile, resultfile ] subprocess.Popen(command) # We now create a first status page to be displayed while the # workflow is executed. Since the waiter process was only just started, # we don't have a proper status yet. So we simply start with an empty # progress bar. # The status page needs to be base64 encoded. status = base64.b64encode(create_html_progressbar(0).encode()).decode() result = "UNSET" return (status, result)