Commit 8a77dd16 authored by Michael R's avatar Michael R Committed by Andrey Vertiprahov
Browse files

Switch service grafanads from tornado to FastAPI

parent 5be44435
#!./bin/python
# ---------------------------------------------------------------------
# annotations handler
# ---------------------------------------------------------------------
# Copyright (C) 2007-2020 The NOC Project
# See LICENSE for details
# ---------------------------------------------------------------------
# Python modules
import operator
from time import mktime
# Third-party modules
import tornado.web
import orjson
import dateutil.parser
from dateutil import tz
# NOC modules
from noc.sa.models.managedobject import ManagedObject
from noc.fm.models.activealarm import ActiveAlarm
from noc.fm.models.archivedalarm import ArchivedAlarm
from noc.fm.models.alarmclass import AlarmClass
class AnnotationsHandler(tornado.web.RequestHandler):
def initialize(self, service=None):
self.service = service
async def post(self, *args, **kwargs):
try:
req = orjson.loads(self.request.body)
except ValueError:
raise tornado.web.HTTPError(400, "Bad request")
#
f = dateutil.parser.parse(req["range"]["from"], ignoretz=False)
t = dateutil.parser.parse(req["range"]["to"], ignoretz=False)
if f > t:
t, f = f, t
# Convert from UTC
t = t.astimezone(tz.tzlocal())
f = f.astimezone(tz.tzlocal())
t = t.replace(tzinfo=None)
f = f.replace(tzinfo=None)
# Annotation to return in reply
ra = req.get("annotation")
#
result = await self.service.run_in_executor("db", self.get_annotations, f, t, ra)
self.write(result)
def get_annotations(self, f, t, annotation):
# @todo: Check object is exists
# @todo: Check access
mo = ManagedObject.get_by_id(int(annotation["query"]))
r = []
# Get alarms
r += self.get_alarms(mo, f, t, annotation)
r = sorted(r, key=operator.itemgetter("time"))
return orjson.dumps(r)
def get_alarms(self, mo, f, t, annotation):
r = []
for ac in (ActiveAlarm, ArchivedAlarm):
q = {"managed_object": mo.id}
if ac.status == "A":
q["timestamp"] = {"$gte": f, "$lte": t}
else:
q["$or"] = [
{"timestamp": {"$gte": f, "$lte": t}},
{"clear_timestamp": {"$gte": f, "$lte": t}},
]
c = ac._get_collection()
for d in c.find(
q,
{
"_id": 1,
"managed_object": 1,
"alarm_class": 1,
"timestamp": 1,
"clear_timestamp": 1,
},
):
if f <= d["timestamp"] <= t:
r += [
{
"annotation": annotation,
"time": mktime(d["timestamp"].timetuple()) * 1000
+ d["timestamp"].microsecond / 1000,
"title": AlarmClass.get_by_id(d["alarm_class"]).name
# "tags": X,
# "text": X
}
]
if "clear_timestamp" in d and f <= d["clear_timestamp"] <= t:
r += [
{
"annotation": annotation,
"time": mktime(d["timestamp"].timetuple()) * 1000
+ d["timestamp"].microsecond / 1000,
"title": "[CLEAR] %s" % AlarmClass.get_by_id(d["alarm_class"]).name
# "tags": X,
# "text": X
}
]
return r
# ---------------------------------------------------------------------
# check handler
# ---------------------------------------------------------------------
# Copyright (C) 2007-2016 The NOC Project
# See LICENSE for details
# ---------------------------------------------------------------------
# Third-party modules
import tornado.web
class CheckHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write("OK")
# ----------------------------------------------------------------------
# Pydentic models for grafanads service
# ----------------------------------------------------------------------
# Copyright (C) 2007-2020 The NOC Project
# See LICENSE for details
# ----------------------------------------------------------------------
# Third-party modules
from pydantic import BaseModel, Field
class RangeSingle(BaseModel):
from_: str = Field(..., alias='from')
to: str
class RangeSection(BaseModel):
from_: str = Field(..., alias='from')
to: str
raw: RangeSingle
class AnnotationSection(BaseModel):
name: str
datasource: str
enable: bool
icon_color: str = Field(..., alias='iconColor')
query: str
class Annotation(BaseModel):
range: RangeSection
annotation: AnnotationSection
range_raw: RangeSingle = Field(..., alias='rangeRaw')
# ----------------------------------------------------------------------
# grafanads API endpoint
# ----------------------------------------------------------------------
# Copyright (C) 2007-2020 The NOC Project
# See LICENSE for details
# ----------------------------------------------------------------------
# Python modules
import operator
from time import mktime
# Third-party modules
import dateutil.parser
from dateutil import tz
from fastapi import APIRouter
# NOC modules
from noc.core.service.loader import get_service
from noc.sa.models.managedobject import ManagedObject
from noc.fm.models.activealarm import ActiveAlarm
from noc.fm.models.archivedalarm import ArchivedAlarm
from noc.fm.models.alarmclass import AlarmClass
from noc.services.grafanads.models.grafanads import Annotation
router = APIRouter()
@router.post("/api/grafanads/annotations/")
@router.post("/api/grafanads/annotations")
async def api_grafanads_annotations(req: Annotation):
service = get_service()
f = dateutil.parser.parse(req.range.from_, ignoretz=False)
t = dateutil.parser.parse(req.range.to, ignoretz=False)
if f > t:
t, f = f, t
# Convert from UTC
t = t.astimezone(tz.tzlocal())
f = f.astimezone(tz.tzlocal())
t = t.replace(tzinfo=None)
f = f.replace(tzinfo=None)
# Annotation to return in reply
ra = req.annotation
#
result = await service.run_in_executor("db", get_annotations, f, t, ra)
return result
@router.get("/api/grafanads/")
def api_grafanads():
return "OK"
def get_annotations(f, t, annotation):
# @todo: Check object is exists
# @todo: Check access
mo = ManagedObject.get_by_id(int(annotation.query))
r = []
# Get alarms
r += get_alarms(mo, f, t, annotation)
r = sorted(r, key=operator.itemgetter("time"))
return r
def get_alarms(mo, f, t, annotation):
r = []
for ac in (ActiveAlarm, ArchivedAlarm):
q = {"managed_object": mo.id}
if ac.status == "A":
q["timestamp"] = {"$gte": f, "$lte": t}
else:
q["$or"] = [
{"timestamp": {"$gte": f, "$lte": t}},
{"clear_timestamp": {"$gte": f, "$lte": t}},
]
c = ac._get_collection()
for d in c.find(
q,
{
"_id": 1,
"managed_object": 1,
"alarm_class": 1,
"timestamp": 1,
"clear_timestamp": 1,
},
):
if f <= d["timestamp"] <= t:
r += [
{
"annotation": annotation,
"time": mktime(d["timestamp"].timetuple()) * 1000
+ d["timestamp"].microsecond / 1000,
"title": AlarmClass.get_by_id(d["alarm_class"]).name
# "tags": X,
# "text": X
}
]
if "clear_timestamp" in d and f <= d["clear_timestamp"] <= t:
r += [
{
"annotation": annotation,
"time": mktime(d["timestamp"].timetuple()) * 1000
+ d["timestamp"].microsecond / 1000,
"title": "[CLEAR] %s" % AlarmClass.get_by_id(d["alarm_class"]).name
# "tags": X,
# "text": X
}
]
return r
......@@ -7,21 +7,13 @@
# ---------------------------------------------------------------------
# NOC modules
from noc.core.service.ui import UIService
from noc.services.grafanads.check import CheckHandler
from noc.services.grafanads.annotations import AnnotationsHandler
from noc.core.service.fastapi import FastAPIService
class GrafanaDSService(UIService):
class GrafanaDSService(FastAPIService):
name = "grafanads"
use_mongo = True
def get_handlers(self):
return super().get_handlers() + [
("^/api/grafanads/annotations", AnnotationsHandler, {"service": self}),
("^/api/grafanads/", CheckHandler),
]
if __name__ == "__main__":
GrafanaDSService().start()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment