diff --git a/brian_polling_manager/error_report/cli.py b/brian_polling_manager/error_report/cli.py index 305da32a8d689bdd277662d1c73f175acdf03230..4d6da0453986e64041f860ee6f4bdcc0b759b77d 100644 --- a/brian_polling_manager/error_report/cli.py +++ b/brian_polling_manager/error_report/cli.py @@ -35,7 +35,17 @@ PROCESSED_ERROR_COUNTERS_SCHEMA = { "definitions": { "error_counters_content": { "type": "object", - "properties": {"additionalProperties": {"type": "integer"}}, + "properties": { + "framing-errors": {"type": "integer"}, + "bit-error-seconds": {"type": "integer"}, + "errored-blocks-seconds": {"type": "integer"}, + "input-crc-errors": {"type": "integer"}, + "input-total-errors": {"type": "integer"}, + "input-discards": {"type": "integer"}, + "input-drops": {"type": "integer"}, + "output-drops": {"type": "integer"}, + }, + "additionalProperties": False, }, "interface_error_counters": { "type": "object", @@ -45,16 +55,14 @@ PROCESSED_ERROR_COUNTERS_SCHEMA = { "description": {"type": "string"}, "error_counters": {"$ref": "#/definitions/error_counters_content"}, "diff": {"$ref": "#/definitions/error_counters_content"}, - "has_new_errors": {"type": "boolean"}, }, "required": [ "router", "interface", "description", "error_counters", - "diff", - "has_new_errors", ], + "additionalProperties": False, }, "excluded_interface_error_counters": { "type": "object", @@ -70,17 +78,18 @@ PROCESSED_ERROR_COUNTERS_SCHEMA = { "description", "error_counters", ], + "additionalProperties": False, }, }, "type": "object", "properties": { "interfaces": { "type": "array", - "item": {"$ref": "#/definitions/interface_error_counters"}, + "items": {"$ref": "#/definitions/interface_error_counters"}, }, "excluded_interfaces": { "type": "array", - "item": {"$ref": "#/definitions/excluded_interface_error_counters"}, + "items": {"$ref": "#/definitions/excluded_interface_error_counters"}, }, }, "required": ["interfaces", "excluded_interfaces"], @@ -283,7 +292,7 @@ def main(): all_error_counters, date=datetime.utcnow().strftime("%a %d %b %H:%M:%S UTC %Y"), ) - print(body) + # TODO: ensure data is from the day that we're interested in (today or yesterday) # TODO: send script failures to admin email diff --git a/brian_polling_manager/error_report/config-example.json b/brian_polling_manager/error_report/config-example.json index e7e23625b7e91eb139aa72f51e7a0672e94137a4..336a28d2b33abfda5a0591715518882fba7cd7a0 100644 --- a/brian_polling_manager/error_report/config-example.json +++ b/brian_polling_manager/error_report/config-example.json @@ -1,8 +1,9 @@ { "email": { "from": "noreply@geant.org", - "reply-to": "noreply@geant.org", + "reply_to": "noreply@geant.org", "to": "some-bogus-email", + "cc": "some-cc", "contact": "someone@geant.org / NE team" }, "inventory": ["blah"], diff --git a/brian_polling_manager/error_report/config.py b/brian_polling_manager/error_report/config.py index 7cad8f1fd25bfe131cdb39eaa3d08fe0a5b8b698..e39177a7fecf2dd2b30fb2c6188b0e0a73aefae7 100644 --- a/brian_polling_manager/error_report/config.py +++ b/brian_polling_manager/error_report/config.py @@ -13,8 +13,9 @@ CONFIG_SCHEMA = { "type": "object", "properties": { "from": {"type": "string"}, - "reply-to": {"type": "string"}, + "reply_to": {"type": "string"}, "to": {"type": "string"}, + "cc": {"type": "string"}, "contact": {"type": "string"}, }, "additionalProperties": False, diff --git a/brian_polling_manager/error_report/error_report.html.jinja2 b/brian_polling_manager/error_report/error_report.html.jinja2 index 808e8ade7eeafe708b4084f9a7186171ba8dd42f..cb7371acbb375a023dc6fdc4262c9e3b6ff6a158 100644 --- a/brian_polling_manager/error_report/error_report.html.jinja2 +++ b/brian_polling_manager/error_report/error_report.html.jinja2 @@ -1,6 +1,9 @@ <html> <body> <pre> +{#- We mix tabs with spaces and have otherwise inconsistent whitespace to keep the + output as close as possible to the output of the original script +#} {%- for ifc in interfaces %} ================================= {{ ifc.router }} diff --git a/test/error_report/test_error_report.py b/test/error_report/test_error_report.py index 853fb291740fb9f3849b52c226b1f2da4635d22b..68196ac4d902e0696824a1e3caa3d4ed92566b27 100644 --- a/test/error_report/test_error_report.py +++ b/test/error_report/test_error_report.py @@ -3,6 +3,7 @@ import json import pathlib from unittest.mock import Mock, patch, call +from brian_polling_manager.error_report.mailer import render_html import jsonschema import pytest from brian_polling_manager.error_report import cli, config @@ -130,7 +131,7 @@ def test_validate_config(tmp_path): content = { "email": { "from": "noreply@geant.org", - "reply-to": "noreply@geant.org", + "reply_to": "noreply@geant.org", "to": "some-bogus-email", "contact": "someone@geant.org / NE team", }, @@ -386,3 +387,85 @@ def test_processes_excluded_interface(create_error_point, get_interface_errors): }, } ] + + +def test_render_html(create_error_point, get_interface_errors): + create_error_point( + "mx1.ams.nl.geant.net", "ae1", "yesterday", input_drops=1, framing_errors=2 + ) + create_error_point( + "mx1.ams.nl.geant.net", "ae1", "today", input_drops=2, framing_errors=4 + ) + + create_error_point("mx1.fra.de.geant.net", "ae10", "today", input_drops=3) + errors = get_interface_errors() + result = render_html(errors=errors, date="<some date>") + # careful, there are tabs mixed with spaces here, but hey, we want to keep the + # output as close to the original script + expected = """\ +<html> +<body> +<pre> +================================= +mx1.ams.nl.geant.net +================================= + ae1 PHY blah blah + framing-errors 4 Diff: 2 + input-drops 2 Diff: 1 + +================================= +mx1.fra.de.geant.net +================================= + ae10 PHY blah blah foo + input-drops 3 + + + + +Generated <some date> +</pre> +</body> +</html>""" + assert result == expected.replace("\n", "\r\n") + + +def test_render_html_with_exclusions(create_error_point, get_interface_errors): + create_error_point("mx1.ams.nl.geant.net", "ae1", "today", input_drops=2) + + create_error_point( + "mx1.fra.de.geant.net", + "ae10", + "today", + # mess up order of kwargs to test re-ordering + bit_error_seconds=2, + framing_errors=1, + input_crc_errors=4, + errored_blocks_seconds=3, + input_discards=6, + input_total_errors=5, + output_drops=8, + input_drops=7, + ) + errors = get_interface_errors(exclusions=["foo"]) + result = render_html(errors=errors, date="<some date>") + # careful, mixing tabs with spaces here + expected = """\ +<html> +<body> +<pre> +================================= +mx1.ams.nl.geant.net +================================= + ae1 PHY blah blah + input-drops 2 + +ROUTER,INTERFACE,FRAMING ERRORS,BIT ERROR SECONDS,ERRORED BLOCKS SECONDS,CRC ERRORS,TOTAL ERRORS,INPUT DISCARDS,INPUT DROPS,OUTPUT DROPS +mx1.fra.de.geant.net,ae10,1,2,3,4,5,6,7,8,PHY blah blah foo + + + +Generated <some date> +</pre> +</body> +</html>""" + assert result == expected.replace("\n", "\r\n")