You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
8.1 KiB
233 lines
8.1 KiB
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
|