def get(self): try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Get Server Status".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value if not request_headers.get('tokenurl', None): request_headers[ 'tokenurl'] = "https://unity-jsc.fz-juelich.de/jupyter-oauth2/token" if not request_headers.get('authorizeurl', None): request_headers[ 'authorizeurl'] = "https://unity-jsc.fz-juelich.de/jupyter-oauth2-as/oauth2-authz" app.log.debug( "uuidcode={} - Start Thread to communicate with j4j_unicore". format(uuidcode)) t = Thread(target=jobs_threads.get, args=(app.log, uuidcode, request_headers, app.urls, app.database)) t.start() except: app.log.exception("Jobs.get failed. Bugfix required") return '', 202
def post(self): try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Revoke Token".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) app.log.trace("uuidcode={} - Json: {}".format( uuidcode, request.json)) validate_auth(app.log, uuidcode, request.headers.get('intern-authorization', None)) request_json = {} for key, value in request.json.items(): request_json[key] = value request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value t = Thread(target=self.post_thread, args=(app.log, uuidcode, request_headers, request_json, app.urls, app.database)) t.start() return '', 202 except: app.log.exception("Revoke.post failed. Bugfix required") return '', 500
def get(self): try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Get UNICOREX User".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value t = Thread(target=self.get_thread, args=(app.log, uuidcode, request_headers, app.urls, app.database)) t.start() except: app.log.exception("UNICORE/X get failed. Bugfix required") return '', 500 return '', 204
def get(self): """ Headers Intern-Authorization uuidcode email """ try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Get Deletion information".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format(uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth(app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value config = utils_file_loads.get_general_config() email = request_headers.get('email', '<no_email_submitted>') email = email.replace("@", "_at_") basefolder = config.get('basefolder', '<no basefolder defined>') userfolder = os.path.join(basefolder, email) workfolder = os.path.join(userfolder, "work") projectfolder = os.path.join(userfolder, "Projects", "MyProjects") totalfiles = 0 totalsize = 0 for root, dirs, files in os.walk(workfolder): # @UnusedVariable if root.endswith(".hpc_mount") or \ root.endswith(".hpc_mount/.upload") or \ root.endswith(".davfs2") or \ root.endswith(".davfs2/cache") or \ root.endswith(".davfs2/cache/b2drop.eudat.eu-remote.php-webdav+home-jovyan-B2DROP+jovyan") or \ root.endswith(".davfs2/certs") or \ root.endswith(".davfs2/certs/private") or \ root.endswith(".ipynb_checkpoints"): continue else: totalfiles += len(files) for file in files: filepath = os.path.join(root, file) totalsize += os.path.getsize(filepath) for root, dirs, files in os.walk(projectfolder): # @UnusedVariable totalfiles += len(files) for file in files: filepath = os.path.join(root, file) totalsize += os.path.getsize(filepath) totalsize = sizeof_fmt(totalsize) except: app.log.exception("Deletion.get failed. Bugfix required") return "", 500 return '{}:{}'.format(totalfiles, totalsize), 200
def post(self): try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Spawn Server".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) app.log.trace("uuidcode={} - Json: {}".format( uuidcode, request.json)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) app.log.debug( "uuidcode={} - Start Thread to communicate with j4j_unicore". format(uuidcode)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value if not request_headers.get('tokenurl', None): request_headers[ 'tokenurl'] = "https://unity-jsc.fz-juelich.de/jupyter-oauth2/token" if not request_headers.get('authorizeurl', None): request_headers[ 'authorizeurl'] = "https://unity-jsc.fz-juelich.de/jupyter-oauth2-as/oauth2-authz" request_json = {} for key, value in request.json.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_json[key] = value if 'port' not in request_json.keys(): # Find a random port, that's not already used by Jupyter@JSC request_json['port'] = jobs_threads_unicore.random_port( app.log, uuidcode, app.database, app.database_tunnel) if request_json['port'] == 0: return '{}'.format(request_json['port']), 539 app.log.trace("uuidcode={} - New Headers: {}".format( uuidcode, request_headers)) app.log.trace("uuidcode={} - New Json: {}".format( uuidcode, request_json)) t = Thread(target=jobs_threads.post, args=(app.log, uuidcode, request_headers, request_json, app.urls, app.database)) t.start() except: app.log.exception("Jobs.post failed. Bugfix required") return '{}'.format(request_json['port']), 202
def delete(self): """ Headers: Intern-Authorization: spawner_token uuidcode containername: uuidcode from spawn """ try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Delete JupyterLab".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format(uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth(app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value containername = request_headers.get('containername') cmd1 = ["docker", "container", "exec", containername, "/bin/umount", "/home/jovyan/B2DROP"] cmd2 = ["docker", "container", "exec", containername, "/bin/fusermount", "-u", "/home/jovyan/HPCMOUNT"] cmd3 = ["docker", "container", "rm", "--force", containername] try: app.log.trace("uuidcode={} - Cmd: {}".format(uuidcode, cmd1)) ret = subprocess.check_output(cmd1, stderr=subprocess.STDOUT, timeout=5) ret = ret.strip().decode("utf-8") app.log.trace("uuidcode={} - Output: {}".format(uuidcode, ret)) except: app.log.warning("uuidcode={} - Could not unmount B2DROP".format(uuidcode)) try: app.log.trace("uuidcode={} - Cmd: {}".format(uuidcode, cmd2)) ret = subprocess.check_output(cmd2, stderr=subprocess.STDOUT, timeout=5) ret = ret.strip().decode("utf-8") app.log.trace("uuidcode={} - Output: {}".format(uuidcode, ret)) except: app.log.warning("uuidcode={} - Could not unmount HPCMOUNT".format(uuidcode)) try: app.log.trace("uuidcode={} - Cmd: {}".format(uuidcode, cmd3)) ret = subprocess.check_output(cmd3, stderr=subprocess.STDOUT, timeout=5) ret = ret.strip().decode("utf-8") app.log.trace("uuidcode={} - Output: {}".format(uuidcode, ret)) except: app.log.exception("uuidcode={} - Could not stop container".format(uuidcode)) except: app.log.exception("JLabs.delete failed. Bugfix required") return '', 500 return '', 202
def delete(self): # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Delete Database entry".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) if request.headers.get('servername'): servername = request.headers.get('servername') infos = utils_db.get_entry_infos(app.log, uuidcode, servername, app.database) if len(infos) == 0: return '', 204 utils_db.remove_entrys(app.log, uuidcode, servername, app.database) return '', 200 return '', 422
def post(self): try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Set skip".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) app.log.trace("uuidcode={} - Json: {}".format( uuidcode, json.dumps(request.json))) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) if request.json.get('value') and request.json.get('servername'): utils_db.set_skip(app.log, uuidcode, request.json.get('servername'), app.database, request.json.get('value')) return '', 202 return '', 422 except: app.log.exception("SkipHandler.post failed. Bugfix required") return '', 500
def get(self): try: """ Headers: Intern-Authorization: spawner_token uuidcode Containername: uuidcode_from_spawn """ # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info( "uuidcode={} - Get JupyterLab Status".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value containername = request_headers.get("containername") cmd1 = [ "docker", "ps", "-q", "-f", "name={}".format(containername) ] try: app.log.trace("uuidcode={} - Cmd: {}".format(uuidcode, cmd1)) ret = subprocess.check_output(cmd1, stderr=subprocess.STDOUT, timeout=5) ret = ret.strip().decode("utf-8") app.log.trace("uuidcode={} - Output: {}".format(uuidcode, ret)) except: app.log.exception( "uuidcode={} - Could not check docker status. Return True". format(uuidcode)) return "True", 200 if ret == "": return "False", 200 else: cmd2 = [ "docker", "ps", "-aq", "-f", "status=exited", "-f", "name={}".format(containername) ] try: app.log.trace("uuidcode={} - Cmd: {}".format( uuidcode, cmd2)) ret = subprocess.check_output(cmd2, stderr=subprocess.STDOUT, timeout=5) ret = ret.strip().decode("utf-8") app.log.trace("uuidcode={} - Output: {}".format( uuidcode, ret)) except: app.log.exception( "uuidcode={} - Could not check docker status. Return True" .format(uuidcode)) return "True", 200 if ret == "": # it's running return "True", 200 else: # cleanup. Container status=exited cmd3 = ["docker", "rm", containername] try: app.log.trace("uuidcode={} - Cmd: {}".format( uuidcode, cmd3)) ret = subprocess.check_output(cmd3, stderr=subprocess.STDOUT, timeout=5) ret = ret.strip().decode("utf-8") app.log.trace("uuidcode={} - Output: {}".format( uuidcode, ret)) except: app.log.exception( "uuidcode={} - Could not cleanup non running container. Return False" .format(uuidcode)) return "False", 200 return "False", 200 except: app.log.exception("JLab.get failed. Bugfix required") return '', 500
def post(self): try: """ Headers: Intern-Authorization: spawner_token uuidcode Body: email environments image port servername jupyterhub_api_url Config: basefolder # /etc/j4j/j4j_hdfcloud network cap-add memory memory-swap device storage-opt """ # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Start JupyterLab".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) app.log.trace("uuidcode={} - Json: {}".format( uuidcode, request.json)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value request_json = {} for key, value in request.json.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_json[key.lower()] = value app.log.trace("uuidcode={} - New Headers: {}".format( uuidcode, request_headers)) app.log.trace("uuidcode={} - New Json: {}".format( uuidcode, request_json)) config = utils_file_loads.get_general_config() basefolder = config.get('basefolder', '<no basefolder defined>') userfolder = os.path.join( basefolder, request_json.get('email').replace("@", "_at_")) serverfolder = Path( os.path.join(userfolder, '.{}'.format(uuidcode))) mounts = jlab_utils.get_mounts(app.log, uuidcode, serverfolder, userfolder) cmd = ["docker", "run"] cmd.append("--network") cmd.append(config.get("network")) cmd.append("--cap-add") cmd.append(config.get("cap-add")) cmd.append("--memory") cmd.append(config.get("memory")) cmd.append("--memory-swap") cmd.append(config.get("memory-swap")) cmd.append("--device") cmd.append(config.get("device")) cmd.append("--restart") cmd.append(config.get("restart")) #cmd.append("--storage-opt") #cmd.append(config.get("storage-opt")) cmd.append("--name") cmd.append(uuidcode) cmd.append("-e") cmd.append("{}={}".format( "HPCACCOUNTS", request_json.get("environments", {}).get("HPCACCOUNTS", ""))) cmd.append("-e") cmd.append("{}={}".format( "JUPYTERHUB_API_URL", request_json.get("environments", {}).get("JUPYTERHUB_API_URL", ""))) cmd.append("-e") cmd.append("{}={}".format( "JUPYTERHUB_CLIENT_ID", request_json.get("environments", {}).get("JUPYTERHUB_CLIENT_ID", ""))) cmd.append("-e") cmd.append("{}={}".format( "JUPYTERHUB_API_TOKEN", request_json.get("environments", {}).get("JUPYTERHUB_API_TOKEN", ""))) cmd.append("-e") cmd.append("{}={}".format( "JUPYTERHUB_USER", request_json.get("environments", {}).get("JUPYTERHUB_USER", ""))) cmd.append("-e") cmd.append("{}={}".format( "JUPYTERHUB_SERVICE_PREFIX", request_json.get("environments", {}).get("JUPYTERHUB_SERVICE_PREFIX", ""))) cmd.append("-e") cmd.append("{}={}".format( "JUPYTERHUB_BASE_URL", request_json.get("environments", {}).get("JUPYTERHUB_BASE_URL", ""))) cmd.extend(mounts) cmd.append(request_json.get("image")) cmd.append("/home/jovyan/.start.sh") cmd.append(str(request_json.get("port"))) cmd.append(request_json.get("servername")) cmd.append(request_json.get("jupyterhub_api_url")) cmd.append("&") app.log.debug("uuidcode={} - Run Command: {}".format( uuidcode, cmd)) subprocess.Popen(cmd) except: app.log.exception("JLab.post failed. Bugfix required") return "", 500 return "", 202
def delete(self): """ Headers Intern-Authorization uuidcode email """ try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Delete Account".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format(uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth(app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value config = utils_file_loads.get_general_config() email = request_headers.get('email', '<no_email_submitted>') email = email.replace("@", "_at_") basefolder = config.get('basefolder', '<no basefolder defined>') userfolder = os.path.join(basefolder, email) user_id = utils_db.get_user_id(app.log, uuidcode, app.database, email) servernames = utils_db.get_all_user_container_names(app.log, uuidcode, app.database, user_id) for servername in servernames: results = jlab_utils.get_slave_infos(app.log, uuidcode, app.database, servername, email) if len(results) > 0: user_id, slave_id, slave_hostname, containername, running_no = results else: app.log.warning("uuidcode={} - {} not in database".format(uuidcode, servername)) continue; url = app.urls.get('dockerspawner', {}).get('url_jlab_hostname', '<no_url_found>').replace('<hostname>', slave_hostname) headers = {"Intern-Authorization": utils_file_loads.get_j4j_dockerspawner_token(), "uuidcode": uuidcode, "containername": containername} try: with closing(requests.delete(url, headers=headers, verify=False)) as r: if r.status_code != 202: app.log.error("uuidcode={} - DockerSpawner delete failed: {} {}".format(uuidcode, r.text, r.status_code)) except: app.log.exception("uuidcode={} - Could not call DockerSpawner {}".format(uuidcode, slave_hostname)) serverfolder = Path(os.path.join(userfolder, '.{}'.format(containername))) utils_db.decrease_slave_running(app.log, uuidcode, app.database, slave_id) utils_db.remove_container(app.log, uuidcode, app.database, user_id, servername) log_dir = Path(os.path.join(config.get('jobs_path', '<no_jobs_path>'), "{}-{}".format(email, containername))) try: os.makedirs(log_dir, exist_ok=True) shutil.copy2(os.path.join(serverfolder, ".jupyterlabhub.log"), os.path.join(log_dir, "jupyterlabhub.log")) except: app.log.exception("uuidcode={} - Could not copy log".format(uuidcode)) jlab_output = "{};{};{}".format(userfolder, containername, running_no == 1) jlab_delete_path = config.get('jlab_delete', '<no_jlab_delete_defined>') app.log.debug("uuidcode={} - Write {} to {}".format(uuidcode, jlab_output, os.path.join(jlab_delete_path, uuidcode))) with open(os.path.join(jlab_delete_path, uuidcode), 'w') as f: f.write(jlab_output) # end for loop all containers are stopped utils_db.delete_account(app.log, uuidcode, app.database, user_id) try: #shutil.rmtree(userfolder) app.log.error("uuidcode={} - Account deletion. Please remove {} from the disk.".format(uuidcode, userfolder)) # remove user quota from xfs_quota jlab_output = "{}".format(uuidcode) jlab_delete_path = config.get('jlab_delete', '<no_jlab_delete_defined>') app.log.debug("uuidcode={} - Write {} to {}.deletion".format(uuidcode, jlab_output, os.path.join(jlab_delete_path, email))) with open('{}.deletion'.format(os.path.join(jlab_delete_path, email)), 'w') as f: f.write(jlab_output) except: app.log.exception("uuidcode={} - Could not delete the users directories".format(uuidcode)) except: app.log.exception("Deletion.delete failed. Bugfix required") return "", 500 return '', 204
def get(self): """ Headers: intern-authorization uuidcode email servername """ try: # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info( "uuidcode={} - Get JupyterLab Status".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) request_headers = {} for key, value in request.headers.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_headers[key.lower()] = value email = request_headers.get('email', '<no_email_submitted>') email = email.replace("@", "_at_") app.log.trace("uuidcode={} - Get User ID for email: {}".format( uuidcode, email)) servername = request_headers.get('servername', '<no_servername_submitted>6') user_id = utils_db.get_user_id(app.log, uuidcode, app.database, email) app.log.trace("uuidcode={} - User_id: {}".format( uuidcode, user_id)) results = utils_db.get_container_info(app.log, uuidcode, app.database, user_id, servername) if len(results) > 0: slave_id, containername = results else: app.log.error( "uuidcode={} - Containerinfo is empty for: user_id: {} , servername={}" .format(uuidcode, user_id, servername)) return "unknown", 200 if slave_id == 0: app.log.error( "uuidcode={} - Could not check if container {} is running". format(uuidcode, containername)) return "unknown", 200 slave_hostname = utils_db.get_slave_hostname( app.log, uuidcode, app.database, slave_id) url = app.urls.get('dockerspawner', {}).get( 'url_jlab_hostname', '<no_url_found>').replace('<hostname>', slave_hostname) header = { "Intern-Authorization": utils_file_loads.get_j4j_dockerspawner_token(), "uuidcode": uuidcode, "containername": containername } with closing(requests.get(url, headers=header, verify=False)) as r: if r.status_code == 200: app.log.trace( "uuidcode={} - Answer from DockerSpawner {}: {}". format(uuidcode, slave_hostname, r.text.strip())) return r.text.strip(), 200 else: app.log.error( "uuidcode={} - Could not check if container {} is running. DockerSpawner answered with: {} {}" .format(uuidcode, containername, r.text, r.status_code)) return "unknown", 200 except: app.log.exception("JLab.get failed. Bugfix required") return '', 202
def post(self): try: """ Headers: intern-authorization uuidcode Body: servername service dashboard email environments image port jupyterhub_api_url """ # Track actions through different webservices. uuidcode = request.headers.get('uuidcode', '<no uuidcode>') app.log.info("uuidcode={} - Start JupyterLab".format(uuidcode)) app.log.trace("uuidcode={} - Headers: {}".format( uuidcode, request.headers)) app.log.trace("uuidcode={} - Json: {}".format( uuidcode, request.json)) # Check for the J4J intern token utils_common.validate_auth( app.log, uuidcode, request.headers.get('intern-authorization', None)) request_json = {} for key, value in request.json.items(): if 'Token' in key: # refresh, jhub, access key = key.replace('-', '_') request_json[key.lower()] = value app.log.trace("uuidcode={} - New Json: {}".format( uuidcode, request_json)) servername = request_json.get('servername') email = request_json.get('email') email = email.replace("@", "_at_") environments = request_json.get('environments') service = request_json.get('service') dashboard = request_json.get('dashboard') image = request_json.get('image') port = request_json.get('port') config = utils_file_loads.get_general_config() quota_config = utils_file_loads.get_quota_config() jupyterhub_api_url = config.get('jupyterhub_api_url') basefolder = config.get('basefolder', '<no basefolder defined>') userfolder = os.path.join(basefolder, email) serverfolder = Path( os.path.join(userfolder, '.{}'.format(uuidcode))) os.umask(0) user_id, set_user_quota = jlab_utils.create_user( app.log, uuidcode, app.database, quota_config, email, basefolder, userfolder) jlab_utils.create_server_dirs(app.log, uuidcode, app.urls, app.database, service, dashboard, user_id, email, servername, serverfolder, basefolder) #jlab_utils.setup_server_quota(app.log, uuidcode, quota_config, serverfolder) try: start = jlab_utils.call_slave_start( app.log, uuidcode, app.database, app.urls, userfolder, config.get('jlab', '<no_jlab_path_defined>'), quota_config, set_user_quota, user_id, servername, email, environments, image, port, jupyterhub_api_url) except: app.log.exception( "uuidcode={} - Could not start JupyterLab".format( uuidcode)) start = False if start: return 200 else: return 501 except: app.log.exception("JLab.post failed. Bugfix required") return 500