Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: json_schema #388

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions plugins/json_schema/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# json_schema
validates alert to a provided jsonschema
Use the alerta plugin pre_recieve hook to run code before alerta accepts alarm for caller.
If the validation fails caller recieves an HTTP error code, and the alarm is not saved.

## build test
Setup with setupptools
increment version number in setup.py
python3 setup.py install will install the module
restart alerta for the module to get loaded into alerta.
Check the logs for any load module errors or errors during alarm submit


## install
refer to the doc directory
76 changes: 76 additions & 0 deletions plugins/json_schema/alerta_json_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import logging
import os
from sqlite3 import InternalError
from xml.dom import NotFoundErr
from alerta.utils.api import process_action
from alerta.app import alarm_model
from alerta.exceptions import AlertaException
from alerta.plugins import PluginBase
from json_schema_validator import validator
from jsonschema import *
import pprint
import json
import jsonschema

LOG = logging.getLogger('alerta')
SCHEMA_VAR = "JSON_SCHEMA"

import pprint
try:
from alerta.plugins import app # alerta >= 5.0
except ImportError:
from alerta.app import app # alerta < 5.0


TIMEOUT = os.environ.get('ALERT_TIMEOUT') or app.config.get('ALERT_TIMEOUT', '0')




def alert_json(alert):
"""
copy all the relevant properties from the incoming alert for processing by json-schema
"""
tocopy = ["timeout","resource","event", "environment", "severity", "correlate", "status", "service", "group", "value", "text", "tags","attributes", "event_type","raw_data","customer"]
alarmobj = {}
for item in tocopy: #remove all empty values to avoid validating non present elements as empty
LOG.debug(item)
if hasattr(alert,item):
if (getattr(alert,item) != None):
alarmobj[item]= getattr(alert,item)
else:
LOG.debug("skip " + item +" hasattr is false")
LOG.debug(pprint.pformat(alarmobj))
return alarmobj

class AlertaJsonSchema(PluginBase):
"""
Validate the incoming alert against a JSON schema file specified in config

"""
def pre_receive(self, alert, **kwargs):
LOG.debug("Json schema plugin: start validation")
jsonfile = app.config.get(SCHEMA_VAR , '0')
if ( jsonfile == '0'):
raise AlertaException("could not fine "+SCHEMA_VAR+" in alerta config file")
try:
validator.validate(jsonfile,alert_json(alert))
except jsonschema.exceptions.ValidationError as e: #unprocessable entity
raise AlertaException(message=e.message + "on " + e.absolute_path + " refer to the json schema", code=422)
return alert


def post_receive(self, alert, **kwargs):
return

def status_change(self, alert, status, text, **kwargs):
return
def take_action(self, alert, action, text, **kwargs):
return

def take_note(self, alert, text, **kwargs):
raise NotImplementedError

def delete(self, alert, **kwargs) -> bool:
raise NotImplementedError

26 changes: 26 additions & 0 deletions plugins/json_schema/doc/Json validation Explanation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# JSON validation explanation

## Description

We  use a json schema valation made in python via the plugin architecture of alerta to execute this.

## working

The plugin must be installed into the python packages that are used by alerta.
At startup alerta will  read the required plugins from confg try to locate the plugins and load them.

Every incoming request will then be validated against the pre_recieve method in the plugin.
this method will read the json recieved and validate it againt the schema present on disk of the alarta server.
That file is located by opening the file that is specified in the alerta config as JSON_SCHEMA.

Successful validations will proceed to alerta, failed validations will be rejected with an HTTP 422 error: unprocessable entity

## installation and configuration

- Install the plugin python3 setup.py install
- Add the plugin to the alerta configuration file
PLUGINS=['json_schema']
- add a line with path to json schema:
JSON_SCHEMA = '/opt/alerta/schema.json'
- put json schema at that path
- restart alerta
Empty file.
11 changes: 11 additions & 0 deletions plugins/json_schema/json_schema_validator/validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import jsonschema
import json
import logging
LOG = logging.getLogger('alerta')
def validate(schema,jsonfile):
LOG.debug("schema is " + schema)
with open(schema, 'r') as schema_file:
loadedschema = json.loads(schema_file.read())
jsonschema.validate(jsonfile,loadedschema)


157 changes: 157 additions & 0 deletions plugins/json_schema/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1655122948.json",
"title": "Alert",
"type": "object",
"properties": {
"resource": {
"title": "Resource",
"type": "string",
"examples": [
"resource"
],
"pattern": "^.*$"
},
"event": {
"title": "Event",
"type": "string",
"examples": [
"event"
],
"pattern": "^.*$"
},
"tags": {
"title": "Tags",
"type": "array",
"default": [],
"items":{
"$id": "#root/tags/items",
"title": "Items",
"type": "string",
"default": "",
"examples": [
"tag1"
],
"pattern": "^.*$"
}
},
"services": {
"title": "Services",
"type": "array",
"default": [],
"items":{
"title": "Items",
"type": "string",
"default": "",
"examples": [
"service"
],
"pattern": "^.*$"
}
},
"correlate": {
"title": "Correlate",
"type": "array",
"default": [],
"items":{
"$id": "#root/correlate/items",
"title": "Items",
"type": "string",
"default": "",
"examples": [
"correlate"
],
"pattern": "^$"
}
},
"severity": {
"title": "Severity",
"type": "string",
"default": "None",
"examples": [
"High",
"Medium",
"Low",
"None"
],
"pattern": "High|Medium|Low|None"
},
"status": {
"title": "status",
"type": "string",
"default": "None",
"examples": [
"open",
"shelved"
],
"pattern": "open|shelved"
},
"environment": {
"title": "Environment",
"type": "string",
"examples": [
"Production"
],
"pattern": "^Production$"
},
"group": {
"title": "Group",
"type": "string",
"default": "",
"examples": [
"tool"
],
"pattern": "^.*$"
},
"value": {
"title": "Value",
"type": "integer",
"default": "",
"examples": [
"10"
],
"pattern": "^.*$"
},
"text": {
"title": "Text",
"type": "string",
"default": "",
"examples": [
"ticket text"
],
"pattern": "^.*$"
},
"timeout": {
"title": "Timeout",
"type": "integer",
"default": "",
"examples": [
"100"
],
"pattern": "^.*$"
},"attributes": {
"title": "attributes",
"type": "object",
"properties": {
"type": {
"title" : "type",
"type" : "string",
"pattern": "^.*$"
},"assigned_group": {
"title" : "assigned_group",
"type" : "string",
"pattern": "^.*$"
}
},
"required": ["type, assigned_group"]
}
},
"required": [
"resource",
"event",
"severity",
"environment",
"text",
"attributes"
]
}
24 changes: 24 additions & 0 deletions plugins/json_schema/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

from setuptools import setup, find_packages

version = '0.2.0'

setup(
name="alerta-json_schema-plugin",
version=version,
description='Example Alerta plugin for testing',
url='https://github.com/alerta/alerta-contrib',
license='Apache License 2.0',
packages=find_packages(),
py_modules=['alerta_json_schema'],
install_requires=['jsonschema','jsonpickle'],
package_data={},
include_package_data=True,
zip_safe=True,
entry_points={
'alerta.plugins': [
'json_schema = alerta_json_schema:AlertaJsonSchema'
]
}
)

27 changes: 27 additions & 0 deletions plugins/json_schema/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import unittest
import json_schema_validator.validator as validator
import os
import json
import jsonschema.exceptions
TEST_DIR = "test" + os.sep
SCHEMA = "schema.json"




class ValidatorTest(unittest.TestCase):
def testValidJsonFile(self):
test = json.load(open(TEST_DIR + "valid.json"))
validator.validate(SCHEMA,test)

def testMissingResource(self):
"""Test if invalid json fails"""
test = json.load(open(TEST_DIR + "missing.json"));
try:
validator.validate(SCHEMA,test)
except jsonschema.exceptions.ValidationError:
assert True
else:
assert False
if __name__ == "__main__":
unittest.main() # run all tests
4 changes: 4 additions & 0 deletions plugins/json_schema/test/invalid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
derer

}
10 changes: 10 additions & 0 deletions plugins/json_schema/test/missing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"severity": "High",
"environment" : "Production",
"attributes" :{
"test1" : "test1",
"test2" : "test2"

}
,"text": "text"
}
10 changes: 10 additions & 0 deletions plugins/json_schema/test/valid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"resource": "myresourc2",
"status": "open",
"attributes": {
"type": "fqdn",
"assigned_group": "myresource.com"
},
"event": "host down"

}