def conf_schema_active(): return { "description": "whether the check shall be executed", "type": "boolean", "default": True, } def conf_schema_threshold(): return { "description": "how often a condition has to occur in order to be reported", "type": "integer", "minimum": 1, "default": 3, } def conf_schema_annoy(): return { "description": "whether notifications shall be kept sending after the threshold has been surpassed", "type": "boolean", "default": False, } def conf_schema_interval(default): return { "anyOf": [ { "description": "in seconds", "type": "integer", "exclusiveMinimum": 0, }, { "description": "as text", "type": "string", "enum": [ "minute", "hour", "day", "week", ] }, ], "default": default, } def conf_schema_schedule(): return { "type": "object", "additionalProperties": False, "properties": { "regular_interval": conf_schema_interval(60 * 60), "attentive_interval": conf_schema_interval(60 * 2), }, "required": [ ], } def conf_schema_notifications(notification_channel_implementations): return { "type": "array", "items": { "anyOf": list( map( lambda pair: { "title": ("check kind '%s'" % pair[0]), "type": "object", "unevaluatedProperties": False, "properties": { "kind": { "type": "string", "enum": [pair[0]] }, "parameters": pair[1].parameters_schema(), }, "required": [ "kind", "parameters" ], }, notification_channel_implementations.items() ) ) }, "default": [ { "kind": "console", "parameters": { } }, ], } def conf_schema_root(check_kind_implementations, notification_channel_implementations): return { "type": "object", "additionalProperties": False, "properties": { "defaults": { "description": "default values for checks", "type": "object", "additionalProperties": False, "properties": { "active": conf_schema_active(), "threshold": conf_schema_threshold(), "annoy": conf_schema_annoy(), "schedule": conf_schema_schedule(), "notifications": conf_schema_notifications(notification_channel_implementations), }, "required": [ ], }, "checks": { "type": "array", "items": { "allOf": [ { "description": "should represent a specific check", "type": "object", "unevaluatedProperties": False, "properties": { "name": { "type": "string" }, "title": { "type": "string" }, "active": conf_schema_active(), "threshold": conf_schema_threshold(), "annoy": conf_schema_annoy(), "schedule": conf_schema_schedule(), "notifications": conf_schema_notifications(notification_channel_implementations), }, "required": [ "name", ], }, { "anyOf": list( map( lambda pair: { "title": ("notification channel '%s'" % pair[0]), "type": "object", "unevaluatedProperties": False, "properties": { "kind": { "type": "string", "enum": [pair[0]] }, "parameters": pair[1].parameters_schema(), }, "required": [ "kind", "parameters", ] }, check_kind_implementations.items() ) ), }, ] } } }, "required": [ "defaults", "checks", ] } def conf_normalize_interval(interval_raw): if (type(interval_raw) == int): return interval_raw elif (type(interval_raw) == str): map_ = { "minute": (60), "hour": (60 * 60), "day": (60 * 60 * 24), "week": (60 * 60 * 24 * 7), } if (interval_raw not in map_): raise ValueError("invalid string interval value: %s" % interval_raw) else: return map_[interval_raw] else: raise ValueError("invalid type for interval value") def conf_normalize_schedule(node): node_ = dict_merge( { "regular_interval": (60 * 60), "attentive_interval": (60 * 2), }, node ) return { "regular_interval": conf_normalize_interval(node["regular_interval"]), "attentive_interval": conf_normalize_interval(node["attentive_interval"]), } def conf_normalize_notification(notification_channel_implementations, node): if (node["kind"] not in notification_channel_implementations): raise ValueError("invalid notification kind: %s" % notification["kind"]) else: return { "kind": node["kind"], "parameters": notification_channel_implementations[node["kind"]].normalize_conf_node(node["parameters"]), } def conf_normalize_defaults(notification_channel_implementations, node): node_ = dict_merge( { "active": True, "threshold": 3, "annoy": False, "schedule": { "regular_interval": (60 * 60), "attentive_interval": (60 * 2), }, "notifications": [ ], }, node ) return { "active": node_["active"], "threshold": node_["threshold"], "annoy": node_["annoy"], "schedule": node_["schedule"], "notifications": list( map( lambda x: conf_normalize_notification(notification_channel_implementations, x), node_["notifications"] ) ), } def conf_normalize_check(check_kind_implementations, notification_channel_implementations, defaults, node): if ("name" not in node): raise ValueError("missing mandatory field in 'check' node: 'name'") else: if ("kind" not in node): raise ValueError("missing mandatory field in 'check' node: 'kind'") else: if (node["kind"] not in check_kind_implementations): raise ValueError("invalid check kind: '%s'" % node["kind"]) else: node_ = dict_merge( { "title": node["name"], "active": defaults["active"], "threshold": defaults["threshold"], "annoy": defaults["annoy"], "schedule": defaults["schedule"], "notifications": defaults["notifications"], "parameters": {}, }, node ) return { "name": node_["name"], "title": node_["title"], "active": node_["active"], "threshold": node_["threshold"], "annoy": node_["annoy"], "schedule": conf_normalize_schedule(node_["schedule"]), "notifications": list( map( lambda x: conf_normalize_notification(notification_channel_implementations, x), node_["notifications"] ) ), "kind": node_["kind"], "parameters": check_kind_implementations[node_["kind"]].normalize_conf_node(node_["parameters"]), } def conf_normalize_root(check_kind_implementations, notification_channel_implementations, node): counts = {} for node_ in node["checks"]: if (node_["name"] not in counts): counts[node_["name"]] = 0 counts[node_["name"]] += 1 fails = list(filter(lambda pair: (pair[1] > 1), counts.items())) if (len(fails) > 0): raise ValueError( string_coin( "ambiguous check names: {{names}}", { "names": ",".join(counts.keys()), } ) ) else: defaults = conf_normalize_defaults(notification_channel_implementations, node["defaults"]) return { "defaults": defaults, "checks": list( map( lambda node_: conf_normalize_check( check_kind_implementations, notification_channel_implementations, defaults, node_ ), node["checks"] ) ) }