import logging import re import os import subprocess import sys from uuid import UUID from distutils.spawn import find_executable PY3 = sys.version_info[0] == 3 levelNames = { logging.ERROR : 'ERROR', logging.WARNING : 'WARN', logging.INFO : 'INFO' } if PY3: string_types = str else: string_types = basestring # keep consistent with DurationUtil.scala duration_pattern = re.compile("^([1-9][0-9]*)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|ms|cs|ds)$") class MADRESTException(Exception): def to_json(self): return {"message": [{"type": self.level, "text": self.message}]} def __init__(self, message, level, status_code=500): super(MADRESTException, self).__init__(message) self.message = message self.status_code = status_code self.level = levelNames[level] def get_field(dict, field_name, default=None, is_optional=False): """ Get a field from a dictionary return the value from `dict`, None if it does not exist and `is_optional` is set to true, otherwise raise exception """ try: return dict[field_name] except KeyError: if default is None: if is_optional: return None else: raise MADRESTException("Required parameter '%s' must be given" % field_name, logging.ERROR, status_code=400) else: return default def check_allowed_params(args_dict, arg_names): extraneous_args = list(set(args_dict).difference(arg_names)) if len(extraneous_args) > 0: raise MADRESTException("Unexpected parameter '%s'" % extraneous_args[0], logging.ERROR, status_code=400) def check_arrays(args_dict, arg_names): for arg in arg_names: if not isinstance(args_dict[arg], list): raise MADRESTException("Parameter '%s' must be an array" % arg, logging.ERROR, status_code=400) def parse_bool_str(value): if value == "0" or value.lower() == "false": return False elif value == "1" or value.lower() == "true": return True else: raise ValueError("unable to parse value %s" % value) def check_int(what, value): if type(value) == int: return value else: try: if isinstance(value, string_types): return int(value) else: raise ValueError("%s value %s can not be converted to integer" % (what, value)) except ValueError: raise MADRESTException(what + " is not an integer, %s" % type(value), logging.ERROR, status_code=400) def check_float(what, value): if type(value) == float: return value else: try: if isinstance(value, (string_types, int)) and type(value) != bool: return float(value) else: raise ValueError("%s value %s can not be converted to integer" % (what, value)) except ValueError: raise MADRESTException(what + " is not a floating point number", logging.ERROR, status_code=400) def check_flag(what, flag): if type(flag) == bool: return flag else: try: if isinstance(flag, string_types): return parse_bool_str(flag) if isinstance(flag, int) and (flag == 0 or flag == 1): return bool(flag) else: raise ValueError("unable to parse value %s" % flag) except Exception: raise MADRESTException("unsupported '%s' parameter: %s" % (what, flag), logging.ERROR, status_code=400) def check_valid_uuid(uuid_str): try: UUID(uuid_str) return uuid_str except: raise MADRESTException("%s is not a valid uuid" % uuid_str, logging.ERROR, status_code=400) def check_duration(what, value): if isinstance(value, string_types): matches = duration_pattern.findall(value) if len(matches) == 0: raise MADRESTException("%s: %s is not a valid duration" % (what, value), logging.ERROR, status_code=400) else: return value else: raise MADRESTException(what + ": " + value + " is not a duration value", logging.ERROR, status_code=400) def update_or_keep(updated, original, limits): if updated is None: return original else: return original.update(updated, limits) def discover_jvm(): java_home_env = os.getenv("JAVA_HOME") # distutil.spawn.findexecutable() auto add .exe for win32 java_cmd = "java" found_jvm = {} result = {} jvm_details = get_jvm_details(java_cmd, True) if jvm_details: result["active"] = "PATH" result["activeRunnable"] = jvm_details["status"]["runnable"] found_jvm["PATH"] = jvm_details if java_home_env is not None: java_cmd = os.path.join(java_home_env, "bin", java_cmd) jvm_details = get_jvm_details(java_cmd) if jvm_details: if "active" not in result or jvm_details["status"]["runnable"]: result["active"] = "JAVA_HOME" result["activeRunnable"] = jvm_details["status"]["runnable"] found_jvm["JAVA_HOME"] = jvm_details if len(found_jvm) > 0: result["availableJVMs"] = found_jvm return result else: result["ERROR"] = "No JVM Found" raise MADRESTException("No JVM Found", logging.ERROR, 404) def get_jvm_details(java_cmd, is_discovered=False): jvm_details = {"path": java_cmd, "status": {"supported": False}} fpath, fname = os.path.split(java_cmd) try: if fpath: found_exec = find_executable(fname, fpath) else: found_exec = find_executable(fname) if found_exec: jvm_details["path"] = found_exec jvm_details["status"]["runnable"] = True if sys.platform == 'win32' and ' ' in found_exec: found_exec = r'"{}"'.format(found_exec) if PY3: ver_output = subprocess.getoutput(' '.join([found_exec, "-version"])) else: ver_output = subprocess.check_output([found_exec, "-version"], stderr=subprocess.STDOUT) jvm_ver_search = re.search(".*?version\\s+[\"]?((\\d+)([.](\\d+))?).*", ver_output) if jvm_ver_search: matched_groups = [g for g in jvm_ver_search.groups() if g] jvm_ver_str = matched_groups[0] jvm_details["version"] = jvm_ver_str version_error_msg = "Unsupported JVM version: %s, require JVM 1.8+" % jvm_ver_str if len(matched_groups) == 4: # Version string has minor number major_ver = int(matched_groups[1]) minor_ver = int(matched_groups[3]) if major_ver >= 9 or (major_ver == 1 and minor_ver == 8): jvm_details["status"]["supported"] = True else: jvm_details["status"]["ERROR"] = version_error_msg elif len(matched_groups) == 2: # Version string only has major number, e.g. the openjdk downloaded from java.net major_ver = int(matched_groups[1]) if major_ver >= 8 and major_ver <= 20: jvm_details["status"]["supported"] = True else: jvm_details["status"]["ERROR"] = version_error_msg else: jvm_details["status"]["ERROR"] = "Unable to parse version number [%s] of the JVM" % jvm_ver_str else: jvm_details["status"]["ERROR"] = "Unable to parse JVM version output:\n %s" % ver_output else: jvm_details["status"]["runnable"] = False if is_discovered: return None else: jvm_details["status"]["ERROR"] = "Can't find java executable in the given path" except OSError as ose: jvm_details["status"]["runnable"] = False jvm_details["status"]["ERROR"] = str(ose.strerror) except Exception: jvm_details["status"]["runnable"] = False jvm_details["status"]["ERROR"] = "unable to validate java from %s" % java_cmd return jvm_details