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.

760 lines
36 KiB

import json
import uuid
import time
import math
import logging
import re
from .mad_util import get_field, MADRESTException, check_flag, check_allowed_params, check_valid_uuid
from .mad_util import check_int, check_duration, check_float, update_or_keep
AVAILABLE_ALGORITHMS = ["trending", "cohesive"]
class MADContext(object):
def __init__(self, name, search, output_dest, managed_saved_search, alert_url, metric_limit_url, disabled):
self.name = MADContext.check_name(name)
self.search = search
self.output_dest = output_dest
self.managed_saved_search = check_flag("managed_saved_search", managed_saved_search)
self.alert_url = alert_url
self.metric_limit_url = metric_limit_url
self.disabled = check_flag("disabled", disabled)
@staticmethod
def check_name(name):
if re.match("^[a-zA-Z0-9][a-zA-Z_0-9]*$", name):
return name
else:
raise MADRESTException("'%s' is not a valid context name" % name, logging.ERROR, status_code=400)
@staticmethod
def from_json(context_json):
try:
return MADContext(
context_json["name"],
context_json["search"],
context_json["output_dest"],
context_json["managed_saved_search"],
context_json.get("alert_url"),
context_json.get("metric_limit_url"),
context_json["disabled"]
)
except KeyError as e:
raise MADRESTException("%s field missing in context" % e, logging.ERROR, status_code=400)
@staticmethod
def from_kv_json(context_kv_json):
context_kv_json["name"] = context_kv_json["_key"]
context_kv_json.pop("_key", None)
return MADContext.from_json(context_kv_json)
@staticmethod
def from_args(args):
check_allowed_params(args, ['name', 'search', 'output_dest', 'managed_saved_search', 'alert_url', 'metric_limit_url'])
name = get_field(args, "name")
search = get_field(args, "search")
output_dest = get_field(args, "output_dest")
managed_saved_search = get_field(args, "managed_saved_search", is_optional=True, default=True)
alert_url = get_field(args, "alert_url", is_optional=True)
metric_limit_url = get_field(args, "metric_limit_url", is_optional=True)
return MADContext(
name = name,
search = search,
output_dest = output_dest,
managed_saved_search = managed_saved_search,
alert_url = alert_url,
metric_limit_url = metric_limit_url,
disabled = True
)
def update(self, args):
check_allowed_params(args, ['search', 'output_dest', 'disabled', 'alert_url', 'metric_limit_url'])
return MADContext(
name = self.name,
search = get_field(args, "search", is_optional=True, default=self.search),
output_dest = get_field(args, "output_dest", is_optional=True, default=self.output_dest),
managed_saved_search = self.managed_saved_search,
alert_url = get_field(args, "alert_url", is_optional=True, default=self.alert_url),
metric_limit_url = get_field(args, "metric_limit_url", is_optional=True, default=self.metric_limit_url),
disabled = get_field(args, "disabled", is_optional=True, default=self.disabled)
)
def to_json(self):
context_json = {
"name": self.name,
"output_dest": self.output_dest,
"search": self.search,
"disabled": self.disabled,
"managed_saved_search": self.managed_saved_search
}
if self.alert_url is not None:
context_json["alert_url"] = self.alert_url
if self.metric_limit_url is not None:
context_json["metric_limit_url"] = self.metric_limit_url
return context_json
def to_kv_json(self):
kv_json = self.to_json()
kv_json["_key"] = kv_json["name"]
kv_json.pop("name", None)
return kv_json
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADInstance(object):
def __init__(self, instance_id, context_name, selector, instance_type, sensitivity, sensitivity_max, config, disabled, resolution, last_modified):
self.instance_id = check_valid_uuid(instance_id)
self.context_name = MADContext.check_name(context_name)
self.instance_type = instance_type
self.sensitivity = check_int("'sensitivity' in instance config", sensitivity)
self.sensitivity_max = check_int("'sensitivity_max' in instance config", sensitivity_max)
self.selector = selector
self.config = config
self.disabled = check_flag("'disabled' in instance config", disabled)
self.resolution = check_duration("'resolution' in instance config", resolution)
self.last_modified = check_int("'lastModified' in instance config", last_modified)
if self.sensitivity > sensitivity_max or self.sensitivity < 0:
raise MADRESTException("sensitivity must be between 0 and %d" % self.sensitivity_max, logging.ERROR, status_code=400)
@staticmethod
def get_Naccum_step(limits):
return (limits.Naccum_max - limits.Naccum_min) / limits.sensitivity_max
@staticmethod
def get_sensitivity(Naccum, limits):
Naccum_step = MADInstance.get_Naccum_step(limits)
return limits.sensitivity_max - int(math.floor((Naccum - limits.Naccum_min) / Naccum_step))
@staticmethod
def update_algo_configs(config, config_str, sensitivity, limits):
# if we have a sensitivity value, then we first update the default config's Naccum
if sensitivity is not None:
sensitivity = check_int("'sensitivity' in instance config", sensitivity)
if sensitivity > limits.sensitivity_max or sensitivity < 0:
raise MADRESTException("sensitivity must be between 0 and %d" % limits.sensitivity_max, logging.ERROR, status_code=400)
Naccum_new = limits.Naccum_min + (limits.sensitivity_max - sensitivity) * MADInstance.get_Naccum_step(limits)
config = config.update({"alertConfig": {"Naccum": Naccum_new}}, limits)
# if we have ANY algorithm config update, then we perform the update here
# NOTE: Naccum calculated from sensitivity is overriden here, if Naccum is specified
if config_str is not None:
try:
config_json = json.loads(config_str)
except:
raise MADRESTException("unable to deserialize algorithm configuration json", logging.ERROR, status_code=400)
config = config.update(config_json, limits)
# we can skip calculating the sensitivity when "sensitivity is set" and "config_str is not set"
if not (sensitivity is not None and config_str is None):
sensitivity = MADInstance.get_sensitivity(config.alertConfig.Naccum, limits)
return config, sensitivity
@staticmethod
def from_json(conf_mgr, instance_json):
try:
algorithm_type = MADInstance.check_algorithm_type(instance_json["type"])
if algorithm_type == "trending":
selector = MADTrendingSelector.from_json(instance_json["selector"])
limits = conf_mgr.get_trending_limits()
config = MADTrendingConfig.from_json(instance_json["config"], limits)
elif algorithm_type == "cohesive":
selector = MADCohesiveSelector.from_json(instance_json["selector"])
limits = conf_mgr.get_cohesive_limits()
config = MADCohesiveConfig.from_json(instance_json["config"], limits)
else:
raise MADRESTException("unknown algorithm type: %s" % algorithm_type, logging.ERROR, status_code=400)
sensitivity = MADInstance.get_sensitivity(config.alertConfig.Naccum, limits)
return MADInstance(
instance_id = instance_json["id"],
context_name = instance_json["contextName"],
selector = selector,
instance_type = algorithm_type,
sensitivity = sensitivity,
sensitivity_max = limits.sensitivity_max,
config = config,
disabled = instance_json["disabled"],
resolution = instance_json["resolution"],
last_modified = instance_json["lastModified"]
)
except KeyError as e:
raise MADRESTException("%s field missing in instance config" % e, logging.ERROR, status_code=400)
@staticmethod
def from_kv_json(conf_mgr, instance_kv_json):
instance_kv_json["id"] = instance_kv_json["_key"]
instance_kv_json.pop("_key", None)
instance_kv_json.pop("_user", None)
return MADInstance.from_json(conf_mgr, instance_kv_json)
@staticmethod
def from_args(conf_mgr, args, context_name):
check_allowed_params(args, ['selector', 'type', 'config', 'sensitivity', 'disabled', 'resolution'])
instance_id = str(uuid.uuid4())
algorithm_type = get_field(args, "type")
selector_str = get_field(args, "selector")
config_str = get_field(args, "config", is_optional=True)
sensitivity = get_field(args, "sensitivity", is_optional=True)
disabled = get_field(args, "disabled", is_optional=True, default=False)
resolution = get_field(args, "resolution")
last_modified = int(time.time())
algorithm_type = MADInstance.check_algorithm_type(algorithm_type)
try:
selector_json = json.loads(selector_str)
except:
raise MADRESTException("unable to deserialize 'selector' json\n%s" % selector_str, logging.ERROR, status_code=400)
if algorithm_type == "trending":
selector = MADTrendingSelector.from_json(selector_json)
default_config, limits = conf_mgr.get_trending_defaults()
elif algorithm_type == "cohesive":
selector = MADCohesiveSelector.from_json(selector_json)
default_config, limits = conf_mgr.get_cohesive_defaults()
else:
raise MADRESTException("unknown algorithm type: %s" % algorithm_type, logging.ERROR, status_code=400)
config = default_config
config, sensitivity = MADInstance.update_algo_configs(config, config_str, sensitivity, limits)
return MADInstance(
instance_id = instance_id,
context_name = context_name,
selector = selector,
instance_type = algorithm_type,
sensitivity = sensitivity,
sensitivity_max = limits.sensitivity_max,
config = config,
disabled = disabled,
resolution = resolution,
last_modified = last_modified
)
@staticmethod
def check_algorithm_type(algorithm_type):
if algorithm_type not in AVAILABLE_ALGORITHMS:
raise MADRESTException("unrecognized algorithm '%s'" % algorithm_type, logging.ERROR, status_code=400)
else:
return algorithm_type
def update(self, args, conf_mgr):
check_allowed_params(args, ['selector', 'type', 'config', 'sensitivity', 'disabled', 'resolution'])
instance_type = get_field(args, "type" , is_optional=True)
selector_str = get_field(args, "selector" , is_optional=True)
sensitivity = get_field(args, "sensitivity" , is_optional=True)
config_str = get_field(args, "config" , is_optional=True)
if instance_type is None or instance_type == self.instance_type:
instance_type = self.instance_type
# instance type has not changed, we're just updating the existing one
selector = self.selector
if selector_str is not None:
try:
selector_json = json.loads(selector_str)
except:
raise MADRESTException("unable to deserialize 'selector' json\n%s" % selector_str, logging.ERROR, status_code=400)
selector = selector.update(selector_json)
config = self.config
if instance_type == "trending":
limits = conf_mgr.get_trending_limits()
elif instance_type == "cohesive":
limits = conf_mgr.get_cohesive_limits()
else:
raise MADRESTException("unknown algorithm type: %s" % instance_type, logging.ERROR, status_code=400)
else:
# we're changing the type of our instance, old algorithm config is useless
try:
selector_json = json.loads(selector_str)
except:
raise MADRESTException("unable to deserialize 'selector' json\n%s" % selector_str, logging.ERROR, status_code=400)
if instance_type == "trending":
selector = MADTrendingSelector.from_json(selector_json)
default_config, limits = conf_mgr.get_trending_defaults()
elif instance_type == "cohesive":
selector = MADCohesiveSelector.from_json(selector_json)
default_config, limits = conf_mgr.get_cohesive_defaults()
else:
raise MADRESTException("unknown algorithm type: %s" % instance_type, logging.ERROR, status_code=400)
config = default_config
config, sensitivity = MADInstance.update_algo_configs(config, config_str, sensitivity, limits)
return MADInstance(
instance_id = self.instance_id,
context_name = self.context_name,
selector = selector,
instance_type = instance_type,
sensitivity = sensitivity,
sensitivity_max = limits.sensitivity_max,
config = config,
disabled = get_field(args, "disabled" , is_optional=True, default=self.disabled),
resolution = get_field(args, "resolution", is_optional=True, default=self.resolution),
last_modified = int(time.time())
)
def to_json(self):
return {
"id" : self.instance_id,
"selector" : self.selector.to_json(),
"type" : self.instance_type,
"config" : self.config.to_json(),
"sensitivity" : self.sensitivity,
"sensitivity_max" : self.sensitivity_max,
"disabled" : self.disabled,
"resolution" : self.resolution,
"contextName" : self.context_name,
"lastModified" : self.last_modified
}
def to_kv_json(self):
kv_json = self.to_json()
kv_json["_key"] = kv_json["id"]
kv_json.pop("id", None)
return kv_json
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADInstanceSelector(object):
def __init__(self, selector_type, filters, spl_filter, value_key):
self.selector_type = selector_type
self.filters = filters
self.spl_filter = spl_filter
self.value_key = value_key
def to_json_common(self):
selector_common_json = {
"type": self.selector_type,
"filters": self.filters,
"value_key": self.value_key
}
if self.spl_filter is not None:
selector_common_json["spl_filter"] = self.spl_filter
return selector_common_json
class MADTrendingSelector(MADInstanceSelector):
def __init__(self, selector_type, filters, spl_filter, value_key):
super(MADTrendingSelector, self).__init__(selector_type, filters, spl_filter, value_key)
@staticmethod
def from_json(selector_json):
try:
return MADTrendingSelector(
selector_type = selector_json["type"],
filters = selector_json["filters"],
spl_filter = selector_json.get("spl_filter"),
value_key = selector_json["value_key"]
)
except KeyError as e:
raise MADRESTException("%s field missing in selector config" % e, logging.ERROR, status_code=400)
def update(self, selector_json):
return MADTrendingSelector(
selector_type = get_field(selector_json, "type", is_optional=True, default=self.selector_type),
filters = get_field(selector_json, "filters", is_optional=True, default=self.filters),
spl_filter = get_field(selector_json, "spl_filter", is_optional=True, default=self.spl_filter),
value_key = get_field(selector_json, "value_key", is_optional=True, default=self.value_key)
)
def to_json(self):
return self.to_json_common()
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADCohesiveSelector(MADInstanceSelector):
def __init__(self, selector_type, filters, spl_filter, value_key, group_by):
super(MADCohesiveSelector, self).__init__(selector_type, filters, spl_filter, value_key)
self.group_by = group_by
@staticmethod
def from_json(selector_json):
try:
return MADCohesiveSelector(
selector_type = selector_json["type"],
filters = selector_json["filters"],
spl_filter = selector_json.get("spl_filter"),
value_key = selector_json["value_key"],
group_by = selector_json["group_by"]
)
except KeyError as e:
raise MADRESTException("%s field missing in selector config" % e, logging.ERROR, status_code=400)
def update(self, selector_json):
return MADCohesiveSelector(
selector_type = get_field(selector_json, "type", is_optional=True, default=self.selector_type),
filters = get_field(selector_json, "filters", is_optional=True, default=self.filters),
spl_filter = get_field(selector_json, "spl_filter", is_optional=True, default=self.spl_filter),
value_key = get_field(selector_json, "value_key", is_optional=True, default=self.value_key),
group_by = get_field(selector_json, "group_by", is_optional=True, default=self.group_by)
)
def to_json(self):
selector_json = self.to_json_common()
selector_json["group_by"] = self.group_by
return selector_json
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADTrendingConfig(object):
def __init__(self, limits, trainingPeriod, maxNAratio, NArm, currentWindowIx, trendingNtrend, periodsConfig, selfComparison, alertConfig, thresholdConfig, windowConfig):
self.trainingPeriod = check_duration("'trainingPeriod' in trending algorithm config", trainingPeriod)
self.maxNAratio = check_float("'maxNAratio' in trending algorithm config", maxNAratio)
self.NArm = check_flag("'NArm' in trending algorithm config", NArm)
self.currentWindowIx = currentWindowIx # string name
self.trendingNtrend = check_int("'trendingNtrend' in trending algorithm config", trendingNtrend)
self.periodsConfig = MADTrendingConfig.check_period_config(periodsConfig)
self.selfComparison = check_flag("'selfComparison' in trending algorithm config", selfComparison)
self.alertConfig = alertConfig
self.thresholdConfig = thresholdConfig
self.windowConfig = windowConfig
@staticmethod
def check_period_config(period_config):
valid_period_config = {}
if isinstance(period_config, dict):
for k, v in period_config.items():
nk = check_duration("'periodConfigs' key %s in trending algorithm config" % k, k)
nv = check_int("'periodConfigs' value %s for key %s in trending algorithm config" % (v, k), v)
valid_period_config[nk] = nv
return valid_period_config
else:
raise MADRESTException("'periodConfigs' in trending algorithm config is not a dictionary", logging.ERROR, status_code=400)
@staticmethod
def from_json(trending_json, limits):
try:
return MADTrendingConfig(
limits = limits,
trainingPeriod = trending_json["trainingPeriod"],
maxNAratio = trending_json["maxNAratio"],
NArm = trending_json["NArm"],
currentWindowIx = trending_json["currentWindowIx"],
trendingNtrend = trending_json["trendingNtrend"],
periodsConfig = trending_json["periodsConfig"],
selfComparison = trending_json["selfComparison"],
alertConfig = MADAlgoAlertConfig.from_json(trending_json["alertConfig"], limits),
thresholdConfig = MADAlgoThresholdConfig.from_json(trending_json["thresholdConfig"], limits),
windowConfig = MADAlgoWindowConfig.from_json(trending_json["windowConfig"], limits)
)
except KeyError as e:
raise MADRESTException("%s field missing in trending algorithm config" % e, logging.ERROR, status_code=400)
def update(self, trending_config_json, limits):
return MADTrendingConfig(
limits = limits,
trainingPeriod = get_field(trending_config_json, "trainingPeriod", is_optional=True, default=self.trainingPeriod),
maxNAratio = get_field(trending_config_json, "maxNAratio", is_optional=True, default=self.maxNAratio),
NArm = get_field(trending_config_json, "NArm", is_optional=True, default=self.NArm),
currentWindowIx = get_field(trending_config_json, "currentWindowIx", is_optional=True, default=self.currentWindowIx),
trendingNtrend = get_field(trending_config_json, "trendingNtrend", is_optional=True, default=self.trendingNtrend),
periodsConfig = get_field(trending_config_json, "periodsConfig", is_optional=True, default=self.periodsConfig),
selfComparison = get_field(trending_config_json, "selfComparison", is_optional=True, default=self.selfComparison),
alertConfig = update_or_keep(get_field(trending_config_json, "alertConfig", is_optional=True), self.alertConfig, limits),
thresholdConfig = update_or_keep(get_field(trending_config_json, "thresholdConfig", is_optional=True), self.thresholdConfig, limits),
windowConfig = update_or_keep(get_field(trending_config_json, "windowConfig", is_optional=True), self.windowConfig, limits)
)
def to_json(self):
return {
"trainingPeriod" : self.trainingPeriod,
"maxNAratio" : self.maxNAratio,
"NArm" : self.NArm,
"currentWindowIx" : self.currentWindowIx,
"trendingNtrend" : self.trendingNtrend,
"periodsConfig" : self.periodsConfig,
"selfComparison" : self.selfComparison,
"alertConfig" : self.alertConfig.to_json(),
"thresholdConfig" : self.thresholdConfig.to_json(),
"windowConfig" : self.windowConfig.to_json()
}
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADCohesiveConfig(object):
def __init__(self, limits, trainingPeriod, maxNAratio, NArm, maxNumberOfMetrics, alertConfig, thresholdConfig, windowConfig, normalizationConfig):
self.trainingPeriod = check_duration("'trainingPeriod' in cohesive algorithm config", trainingPeriod)
self.maxNAratio = check_float("'maxNAratio' in cohesive algorithm config", maxNAratio)
self.NArm = check_flag("'NArm' in cohesive algorithm config", NArm)
self.maxNumberOfMetrics = check_int("'maximumNumberOfMetrics' in cohesive algorithm config", maxNumberOfMetrics)
self.alertConfig = alertConfig
self.thresholdConfig = thresholdConfig
self.windowConfig = windowConfig
self.normalizationConfig = normalizationConfig
@staticmethod
def from_json(cohesive_json, limits):
try:
return MADCohesiveConfig(
limits = limits,
trainingPeriod = cohesive_json["trainingPeriod"],
maxNAratio = cohesive_json["maxNAratio"],
NArm = cohesive_json["NArm"],
maxNumberOfMetrics = cohesive_json["maximumNumberOfMetrics"],
alertConfig = MADAlgoAlertConfig.from_json(cohesive_json["alertConfig"], limits),
thresholdConfig = MADAlgoThresholdConfig.from_json(cohesive_json["thresholdConfig"], limits),
windowConfig = MADAlgoWindowConfig.from_json(cohesive_json["windowConfig"], limits),
normalizationConfig = MADAlgoNormalizationConfig.from_json(cohesive_json["normalizationConfig"], limits)
)
except KeyError as e:
raise MADRESTException("%s field missing in cohesive algorithm config" % e, logging.ERROR, status_code=400)
def update(self, cohesive_config_json, limits):
return MADCohesiveConfig(
limits = limits,
trainingPeriod = get_field(cohesive_config_json, "trainingPeriod", is_optional=True, default=self.trainingPeriod),
maxNAratio = get_field(cohesive_config_json, "maxNAratio", is_optional=True, default=self.maxNAratio),
NArm = get_field(cohesive_config_json, "NArm", is_optional=True, default=self.NArm),
maxNumberOfMetrics = get_field(cohesive_config_json, "maximumNumberOfMetrics", is_optional=True, default=self.maxNumberOfMetrics),
alertConfig = update_or_keep(get_field(cohesive_config_json, "alertConfig", is_optional=True), self.alertConfig, limits),
thresholdConfig = update_or_keep(get_field(cohesive_config_json, "thresholdConfig", is_optional=True), self.thresholdConfig, limits),
windowConfig = update_or_keep(get_field(cohesive_config_json, "windowConfig", is_optional=True), self.windowConfig, limits),
normalizationConfig = update_or_keep(get_field(cohesive_config_json, "normalizationConfig", is_optional=True), self.normalizationConfig, limits)
)
def to_json(self):
return {
"trainingPeriod" : self.trainingPeriod,
"maxNAratio" : self.maxNAratio,
"NArm" : self.NArm,
"maximumNumberOfMetrics" : self.maxNumberOfMetrics,
"alertConfig" : self.alertConfig.to_json(),
"thresholdConfig" : self.thresholdConfig.to_json(),
"windowConfig" : self.windowConfig.to_json(),
"normalizationConfig" : self.normalizationConfig.to_json()
}
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADAlgoWindowConfig(object):
def __init__(self, limits, step_size, window_size):
self.step_size = check_int("'stepSize' in 'windowConfig'", step_size)
self.window_size = check_int("'windowSize' in 'windowConfig", window_size)
@staticmethod
def from_json(window_cfg_json, limits):
try:
return MADAlgoWindowConfig(
limits = limits,
step_size = window_cfg_json["stepSize"],
window_size = window_cfg_json["windowSize"]
)
except KeyError as e:
raise MADRESTException("%s field missing in window config" % e, logging.ERROR, status_code=400)
def update(self, window_cfg_json, limits):
return MADAlgoWindowConfig(
limits = limits,
step_size = get_field(window_cfg_json, "stepSize", is_optional=True, default=self.step_size),
window_size = get_field(window_cfg_json, "windowSize", is_optional=True, default=self.window_size))
def to_json(self):
return {
"stepSize" : self.step_size,
"windowSize" : self.window_size
}
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADAlgoThresholdConfig(object):
def __init__(self, limits, pct_outlier, slope_threshold, diff_sigma, Nkeep):
self.pct_outlier = check_float("'pctOutlier' in 'thresholdConfig'", pct_outlier)
self.slope_threshold = check_float("'slopeThreshold' in 'thresholdConfig'", slope_threshold)
self.diff_sigma = check_float("'diffSigma' in 'thresholdConfig'", diff_sigma)
self.Nkeep = check_duration("'Nkeep' in 'thresholdConfig'", Nkeep)
@staticmethod
def from_json(threshold_cfg_json, limits):
try:
return MADAlgoThresholdConfig(
limits = limits,
pct_outlier = threshold_cfg_json["pctOutlier"],
slope_threshold = threshold_cfg_json["slopeThreshold"],
diff_sigma = threshold_cfg_json["diffSigma"],
Nkeep = threshold_cfg_json["Nkeep"]
)
except KeyError as e:
raise MADRESTException("%s field missing in threshold config" % e, logging.ERROR, status_code=400)
def update(self, threshold_cfg_json, limits):
return MADAlgoThresholdConfig(
limits = limits,
pct_outlier = get_field(threshold_cfg_json, "pctOutlier", is_optional=True, default=self.pct_outlier),
slope_threshold = get_field(threshold_cfg_json, "slopeThreshold", is_optional=True, default=self.slope_threshold),
diff_sigma = get_field(threshold_cfg_json, "diffSigma", is_optional=True, default=self.diff_sigma),
Nkeep = get_field(threshold_cfg_json, "Nkeep", is_optional=True, default=self.Nkeep)
)
def to_json(self):
return {
"pctOutlier" : self.pct_outlier,
"slopeThreshold" : self.slope_threshold,
"diffSigma" : self.diff_sigma,
"Nkeep" : self.Nkeep
}
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADAlgoAlertConfig(object):
def __init__(self, limits, Naccum):
self.Naccum = check_float("'Naccum' in 'alertConfig'", Naccum)
if self.Naccum < limits.Naccum_min or self.Naccum > limits.Naccum_max:
raise MADRESTException(
"'Naccum' in 'alertConfig' must be a number between %d - %d" % (limits.Naccum_min, limits.Naccum_max),
level=logging.ERROR,
status_code=400
)
@staticmethod
def from_json(alert_cfg_json, limits):
try:
return MADAlgoAlertConfig(
limits=limits,
Naccum=alert_cfg_json["Naccum"]
)
except KeyError as e:
raise MADRESTException("%s field missing in alert config" % e, logging.ERROR, status_code=400)
def update(self, alert_cfg_json, limits):
return MADAlgoAlertConfig(limits, get_field(alert_cfg_json, "Naccum", is_optional=True, default=self.Naccum))
def to_json(self):
return {"Naccum": self.Naccum}
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MADAlgoNormalizationConfig(object):
def __init__(self, limits, batch, Ninit, Nshift, Nwindow, MAratio, Ntrend, NArm, maxNAratio, trendOnly):
self.batch = check_flag("'batch' in 'normalizationConfig'", batch)
self.Ninit = check_int("'Ninit' in 'normalizationConfig'", Ninit)
self.Nshift = check_int("'Nshift' in 'normalizationConfig'", Nshift)
self.Nwindow = check_int("'Nwindow', in 'normalizationConfig'", Nwindow)
self.MAratio = check_float("'MAratio' in 'normalizationConfig'", MAratio)
self.Ntrend = check_int("'Ntrend' in 'normalizationConfig'", Ntrend)
self.NArm = check_flag("'NArm' in 'normalizationConfig'", NArm)
self.maxNAratio = check_float("'maxNAratio' in 'normalizationConfig'", maxNAratio)
self.trendOnly = check_flag("'trendOnly' in 'normalizationConfig'", trendOnly)
@staticmethod
def from_json(norm_cfg_json, limits):
try:
return MADAlgoNormalizationConfig(
limits = limits,
batch = norm_cfg_json["batch"],
Ninit = norm_cfg_json["Ninit"],
Nshift = norm_cfg_json["Nshift"],
Nwindow = norm_cfg_json["Nwindow"],
MAratio = norm_cfg_json["MAratio"],
Ntrend = norm_cfg_json["Ntrend"],
NArm = norm_cfg_json["NArm"],
maxNAratio = norm_cfg_json["maxNAratio"],
trendOnly = norm_cfg_json["trendOnly"]
)
except KeyError as e:
raise MADRESTException("%s field missing in normalization config" % e, logging.ERROR, status_code=400)
def update(self, norm_cfg_json, limits):
return MADAlgoNormalizationConfig(
limits = limits,
batch = get_field(norm_cfg_json, "batch", is_optional=True, default=self.batch),
Ninit = get_field(norm_cfg_json, "Ninit", is_optional=True, default=self.Ninit),
Nshift = get_field(norm_cfg_json, "Nshift", is_optional=True, default=self.Nshift),
Nwindow = get_field(norm_cfg_json, "Nwindow", is_optional=True, default=self.Nwindow),
MAratio = get_field(norm_cfg_json, "MAratio", is_optional=True, default=self.MAratio),
Ntrend = get_field(norm_cfg_json, "Ntrend", is_optional=True, default=self.Ntrend),
NArm = get_field(norm_cfg_json, "NArm", is_optional=True, default=self.NArm),
maxNAratio = get_field(norm_cfg_json, "maxNAratio", is_optional=True, default=self.maxNAratio),
trendOnly = get_field(norm_cfg_json, "trendOnly", is_optional=True, default=self.trendOnly)
)
def to_json(self):
return {
"batch" : self.batch,
"Ninit" : self.Ninit,
"Nshift" : self.Nshift,
"Nwindow" : self.Nwindow,
"MAratio" : self.MAratio,
"Ntrend" : self.Ntrend,
"NArm" : self.NArm,
"maxNAratio" : self.maxNAratio,
"trendOnly" : self.trendOnly
}
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return NotImplemented