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

Grafana #153

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
00147e8
Add postgres database credentials to GFConfig model, add new migratio…
daisycrego Mar 21, 2020
b9a9ada
Fix grafana credentials for tests, resolve merge conflicts, black/fla…
daisycrego Mar 21, 2020
872d0ab
Merge branch 'frontend-team' into grafana to get grafana in sync.
daisycrego Mar 21, 2020
3c3fec2
Add more checks to create_dashboard().
daisycrego Mar 21, 2020
5d2b3a7
Add more to error messaging for Grafana.create_dashboard_update_dict().
daisycrego Mar 21, 2020
92dd953
Improve error handling of Grafana.add_grafana_panel().
daisycrego Mar 21, 2020
670277d
Add more test logging to test_add_grafana_panel.
daisycrego Mar 21, 2020
64b0bd2
Fix naming bug.
daisycrego Mar 21, 2020
0ac881b
Prevent create_dashboard() from the throwing an error if dashboard al…
daisycrego Mar 21, 2020
a02e513
Add minor syntax fix.
daisycrego Mar 21, 2020
d375c55
Disable all grafana tests but 1.
daisycrego Mar 22, 2020
73f1ee6
Disable all grafana tests but 2.
daisycrego Mar 22, 2020
109231e
Disable all grafana tests but 3.
daisycrego Mar 22, 2020
527a0fa
Disable all grafana tests but 4.
daisycrego Mar 22, 2020
7b49f14
Disable all grafana tests but 5.
daisycrego Mar 22, 2020
567f21c
Enable all grafana tests.
daisycrego Mar 22, 2020
66d14ce
Disable multiple grafana panel test.
daisycrego Mar 22, 2020
0f29a06
Narrowing down problem with multiple grafana panels test.
daisycrego Mar 22, 2020
00d5ee0
Change error handling of create datasource method to fail silently if…
daisycrego Mar 22, 2020
6cd4bcc
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
3b09739
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
3a7f910
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
48ad3c2
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
b48fef0
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
8001acc
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
18357d7
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
723d59e
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
faa9c24
Continue narrowing down issue with multiple panels test.
daisycrego Mar 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
151 changes: 120 additions & 31 deletions mercury/grafanaAPI/grafana_api.py
Expand Up @@ -3,29 +3,56 @@
import requests
from mercury.models import GFConfig

TOKEN = "eyJrIjoiRTQ0cmNGcXRybkZlUUNZWmRvdFI0UlMwdFVYVUt3bzgiLCJuIjoia2V5IiwiaWQiOjF9"
HOST = "https://dbc291.grafana.net"
DASHBOARD_UID = "9UF7VluWz"
DB_GRAFANA_NAME = "Heroku PostgreSQL (sextants-telemetry)"
DB_HOSTNAME = "ec2-35-168-54-239.compute-1.amazonaws.com:5432"
DB_NAME = "d76k4515q6qv"
DB_USERNAME = "qvqhuplbiufdyq"
DB_PASSWORD = "f45a1cfe8458ff9236ead8a7943eba31dcef761471e0d6d62b043b4e3d2e10e5"


class Grafana:
def __init__(self, host=None, token=None):
gf_config = GFConfig.objects.filter(gf_current=True).first()
if gf_config:
self.hostname = gf_config.gf_host
self.api_token = gf_config.gf_token
self.uid = gf_config.gf_host
self.database_hostname = gf_config.gf_host
self.database_name = gf_config.gf_db_name
self.database_username = gf_config.gf_db_username
self.database_password = gf_config.gf_db_pw
self.database_grafana_name = gf_config.gf_db_grafana_name
else:
# for test purposes, a test case should init this class with credentials
self.hostname = host
self.api_token = token
self.hostname = host
self.uid = DASHBOARD_UID
self.database_hostname = DB_HOSTNAME
self.database_name = DB_NAME
self.database_username = DB_USERNAME
self.database_password = DB_PASSWORD
self.database_grafana_name = DB_GRAFANA_NAME

# self.uid = "XwC1wLXZz" # needs to come from dashboard
self.uid = "9UF7VluWz"

self.temp_file = "dashboard_output.json"
self.auth_url = "api/auth/keys"
self.dashboard_post_url = "api/dashboards/db"
self.dashboard_uid_url = "api/dashboards/uid/"
self.dashboard_uid_url = "api/dashboards/uid"
self.dashboard_get_url = "api/dashboards"
self.home_dashboard_url = "api/dashboards/home"
self.search_url = "api/search?"
self.datasource_name_url = "api/datasources/name"

self.search_endpoint = os.path.join(self.hostname, self.search_url)
self.datasource_name_endpoint = os.path.join(
self.hostname, self.datasource_name_url
)

self.dashboard_uid_endpoint = os.path.join(
self.hostname, self.dashboard_uid_url
)
Expand All @@ -40,15 +67,11 @@ def __init__(self, host=None, token=None):
self.hostname, self.home_dashboard_url
)

self.datasource = "Heroku PostgreSQL (sextants-telemetry)" # needs to come
# from dashboard after configuring postgres

# Default panel sizes
self.base_panel_width = 15
self.base_panel_height = 12

def delete_all_dashboards(self):
print(self.search_endpoint)
tag_search_endpoint = os.path.join(self.search_endpoint)
headers = {"Content-Type": "application/json"}
response = requests.get(
Expand All @@ -68,7 +91,6 @@ def delete_dashboard(self, uid):
)

if "deleted" not in response.json()["message"]:
print(f"Error deleting dashboard with uid: {uid}")
return False
return True

Expand All @@ -85,6 +107,7 @@ def delete_dashboard(self, uid):
# 'url': '/d/GjrBC6uZz/sensors',
# 'version': 1
# }
# Returns true if the dashboard was created, false otherwise
def create_dashboard(self, title="Sensors"):
dashboard_base = {
"dashboard": {
Expand All @@ -108,11 +131,73 @@ def create_dashboard(self, title="Sensors"):

post_output = response.json()

return post_output
try:
post_output["id"]
return post_output
except KeyError:
if "Access denied" in post_output["message"]:
raise ValueError("Access denied - check hostname and API token")
elif (
"A dashboard with the same name in the folder already exists"
in post_output["message"]
):
return None
else:
raise ValueError("Create_dashboard() failed: " + post_output["message"])

# Still working on this
def configure_postgres_db(self):
pass
# Returns true if datasource was deleted, false otherwise
def delete_datasource_by_name(self, name):
headers = {"Content-Type": "application/json"}
endpoint = os.path.join(self.hostname, "api/datasources/name", name)
response = requests.delete(
url=endpoint, headers=headers, auth=("api_key", self.api_token)
)
json = response.json()
try:
if json["message"] == "Data source deleted":
return True
else:
return False
except KeyError:
return False

def create_postgres_datasource(self):
db = {
"id": None,
"orgId": None,
"name": self.database_grafana_name,
"type": "postgres",
"access": "proxy",
"url": self.database_hostname,
"password": self.database_password,
"user": self.database_username,
"database": self.database_name,
"basicAuth": False,
"jsonData": {"postgresVersion": 903, "sslmode": "require"},
}

headers = {"Content-Type": "application/json"}
response = requests.post(
url=os.path.join(self.hostname, "api/datasources"),
headers=headers,
json=db,
auth=("api_key", self.api_token),
)

datasource = response.json()
try:
if datasource["message"] == "Datasource added":
return datasource
elif "Access denied" in datasource["message"]:
raise ValueError("Access denied - check hostname and API token")
elif "Data source with same name already exists" in datasource["message"]:
return None
else:
raise ValueError(
"Create_postgres_datasource() failed: " + datasource["message"]
)
except KeyError:
raise ValueError("Create_postgres_datasource() failed: " + datasource)

def get_dashboard_with_uid(self, uid):
"""
Expand All @@ -123,15 +208,15 @@ def get_dashboard_with_uid(self, uid):
"""
headers = {"Content-Type": "application/json"}
endpoint = os.path.join(self.dashboard_uid_endpoint, uid)
print(endpoint)
response = requests.get(
url=endpoint, headers=headers, auth=("api_key", self.api_token)
)
dashboard_dict = response.json()

print(response.text)

return dashboard_dict
if "dashboard" in response.json():
return response.json()
else:
print(response.json())
return None

def create_panel_dict(self, panel_id, fields, panel_sql_query, title, x, y):
"""
Expand All @@ -155,7 +240,7 @@ def create_panel_dict(self, panel_id, fields, panel_sql_query, title, x, y):
"bars": False,
"dashLength": 10,
"dashes": False,
"datasource": self.datasource,
"datasource": self.database_grafana_name,
"fill": 1,
"fillGradient": 0,
"gridPos": {"h": 9, "w": 12, "x": x, "y": y},
Expand Down Expand Up @@ -247,16 +332,19 @@ def create_dashboard_update_dict(self, dashboard_info, panels, overwrite=True):
:return: dict which can be posted to Create/Update Dashboard API endpoint
"""

# Extract attributes from existing dashboard
id = dashboard_info["dashboard"]["id"]
uid = dashboard_info["dashboard"]["uid"]
title = dashboard_info["dashboard"]["title"]
schema_version = dashboard_info["dashboard"]["schemaVersion"]
# style = dashboard_info["dashboard"]["style"]
tags = dashboard_info["dashboard"]["tags"]
# templating = dashboard_info["dashboard"]["templating"]
version = dashboard_info["meta"]["version"]
folder_id = dashboard_info["meta"]["folderId"]
try:
# Extract attributes from existing dashboard
id = dashboard_info["dashboard"]["id"]
uid = dashboard_info["dashboard"]["uid"]
title = dashboard_info["dashboard"]["title"]
schema_version = dashboard_info["dashboard"]["schemaVersion"]
# style = dashboard_info["dashboard"]["style"]
tags = dashboard_info["dashboard"]["tags"]
# templating = dashboard_info["dashboard"]["templating"]
version = dashboard_info["meta"]["version"]
folder_id = dashboard_info["meta"]["folderId"]
except KeyError:
raise ValueError(f"dashboard_info object is invalid: {dashboard_info}")

# Prepare updated_dashboard object
updated_dashboard = {
Expand Down Expand Up @@ -295,13 +383,12 @@ def delete_grafana_panels(self, uid):
updated_dashboard = self.create_dashboard_update_dict(dashboard_info, panels)

# POST updated dashboard with empty list of panels
authorization = f"Bearer {self.api_token}"
authorization = f"Bearer {self.api_token}"
headers = {"Content-Type": "application/json", "Authorization": authorization}
headers = {"Content-Type": "application/json"}
requests.post(
self.dashboard_post_endpoint,
data=json.dumps(updated_dashboard),
headers=headers,
auth=("api_key", self.api_token),
)

def add_grafana_panel(self, sensor, uid):
Expand All @@ -326,7 +413,9 @@ def add_grafana_panel(self, sensor, uid):

# Retrieve current dashboard structure
dashboard_info = self.get_dashboard_with_uid(uid)
print(dashboard_info)

if dashboard_info is None:
raise ValueError("Dashboard uid not found.")

# Retrieve current panels
try:
Expand Down
45 changes: 45 additions & 0 deletions mercury/migrations/0014_auto_20200321_1103.py
@@ -0,0 +1,45 @@
# Generated by Django 2.2.6 on 2020-03-21 11:03

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('mercury', '0013_auto_20200320_1706'),
]

operations = [
migrations.AddField(
model_name='gfconfig',
name='gf_dashboard_uid',
field=models.CharField(default='9UF7VluWz', max_length=64),
preserve_default=False,
),
migrations.AddField(
model_name='gfconfig',
name='gf_db_host',
field=models.CharField(default='ec2-35-168-54-239.compute-1.amazonaws.com'
':5432', max_length=128),
preserve_default=False,
),
migrations.AddField(
model_name='gfconfig',
name='gf_db_name',
field=models.CharField(default='d76k4515q6qv', max_length=64),
preserve_default=False,
),
migrations.AddField(
model_name='gfconfig',
name='gf_db_pw',
field=models.CharField(default='f45a1cfe8458ff9236ead8a7943eba31dcef761471e'
'0d6d62b043b4e3d2e10e5', max_length=256),
preserve_default=False,
),
migrations.AddField(
model_name='gfconfig',
name='gf_db_username',
field=models.CharField(default='qvqhuplbiufdyq', max_length=64),
preserve_default=False,
),
]
19 changes: 19 additions & 0 deletions mercury/migrations/0015_gfconfig_gf_db_grafana_name.py
@@ -0,0 +1,19 @@
# Generated by Django 2.2.6 on 2020-03-21 11:09

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('mercury', '0014_auto_20200321_1103'),
]

operations = [
migrations.AddField(
model_name='gfconfig',
name='gf_db_grafana_name',
field=models.CharField(default='', max_length=64),
preserve_default=False,
),
]
6 changes: 6 additions & 0 deletions mercury/models.py
Expand Up @@ -47,6 +47,12 @@ class GFConfig(models.Model):
gf_token = models.CharField(
max_length=256
) # token only, without the prefix "Bearer "
gf_dashboard_uid = models.CharField(max_length=64)
gf_db_host = models.CharField(max_length=128)
gf_db_grafana_name = models.CharField(max_length=64)
gf_db_name = models.CharField(max_length=64)
gf_db_username = models.CharField(max_length=64)
gf_db_pw = models.CharField(max_length=256)
gf_current = models.BooleanField(default=False, blank=True)


Expand Down