Merge lp:~jacekn/charms/trusty/grafana/layer-grafana into lp:~canonical-is-sa/charms/trusty/grafana/layer-grafana

Proposed by Jacek Nykis on 2016-07-14
Status: Merged
Merged at revision: 40
Proposed branch: lp:~jacekn/charms/trusty/grafana/layer-grafana
Merge into: lp:~canonical-is-sa/charms/trusty/grafana/layer-grafana
Diff against target: 180 lines (+128/-1)
4 files modified
config.yaml (+13/-1)
files/dashboards_backup (+41/-0)
reactive/grafana.py (+69/-0)
templates/juju-dashboards-backup.j2 (+5/-0)
To merge this branch: bzr merge lp:~jacekn/charms/trusty/grafana/layer-grafana
Reviewer Review Type Date Requested Status
Canonical IS SAs 2016-07-14 Pending
Review via email: mp+300088@code.launchpad.net

Description of the change

Added dashboards backup support

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2016-05-06 20:42:51 +0000
3+++ config.yaml 2016-07-14 15:09:02 +0000
4@@ -78,4 +78,16 @@
5 default: ""
6 type: string
7 description: |
8- Grafana admin password. Default, pwgen(16) random password
9+ Grafana admin password. Default, pwgen(16) random password.
10+ See also README.md for instructions how to retrieve automatically
11+ generated password
12+ dashboards_backup_schedule:
13+ default: ""
14+ type: string
15+ description: |
16+ Cron schedule for dashboards backups
17+ dashboards_backup_dir:
18+ default: "/srv/backups"
19+ type: string
20+ description: |
21+ Location where to put dashboard dumps
22
23=== added directory 'files'
24=== added file 'files/dashboards_backup'
25--- files/dashboards_backup 1970-01-01 00:00:00 +0000
26+++ files/dashboards_backup 2016-07-14 15:09:02 +0000
27@@ -0,0 +1,41 @@
28+#!/usr/bin/env python
29+
30+import os
31+import argparse
32+import requests
33+import string
34+import datetime
35+
36+
37+def get_backup_filename(directory, org_name, uri):
38+ valid_chars = '-_(){}{}'.format(string.ascii_letters, string.digits)
39+ dashboard = uri.split('/')[1]
40+ filename = ''.join(c if c in valid_chars else '_' for c in dashboard)
41+ filename += '.json.' + datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
42+ org_name = ''.join(c if c in valid_chars else '_' for c in org_name)
43+ return os.path.join(directory, org_name, filename)
44+
45+
46+def main(args):
47+ base_url = 'http://localhost:3000/api/'
48+ for key in args.api_keys:
49+ headers = {'Authorization': 'Bearer {}'.format(key)}
50+ org_name = requests.get(base_url+'org', headers=headers).json()['name']
51+ r = requests.get(base_url+'search', headers=headers).json()
52+ uris = [i['uri'] for i in r]
53+ for d in uris:
54+ backup_file = get_backup_filename(args.directory, org_name, d)
55+ if not os.path.isdir(os.path.dirname(backup_file)):
56+ os.mkdir(os.path.dirname(backup_file))
57+ dashboard = requests.get(base_url+'dashboards/'+d, headers=headers).json()['dashboard']
58+ with open(backup_file, 'w') as f:
59+ f.write(str(dashboard))
60+
61+
62+if __name__ == '__main__':
63+ parser = argparse.ArgumentParser(description='Back up grafana dashboards to disk')
64+ parser.add_argument('-d', '--directory', help='Directory where to store backups',
65+ default='/srv/backups')
66+ parser.add_argument('api_keys', help='List of API keys to use for backups', nargs='+')
67+ args = parser.parse_args()
68+ main(args)
69
70=== modified file 'reactive/grafana.py'
71--- reactive/grafana.py 2016-05-09 16:26:12 +0000
72+++ reactive/grafana.py 2016-07-14 15:09:02 +0000
73@@ -4,7 +4,9 @@
74 import hashlib
75 import datetime
76 import requests
77+import json
78 import subprocess
79+import base64
80 from time import sleep
81 from charmhelpers import fetch
82 from charmhelpers.core import host, hookenv, unitdata
83@@ -33,6 +35,8 @@
84 GRAFANA_INI = '/etc/grafana/grafana.ini'
85 GRAFANA_INI_TMPL = 'grafana.ini.j2'
86 GRAFANA_DEPS = ['libfontconfig1']
87+DASHBOARDS_BACKUP_CRON = '/etc/cron.d/juju-dashboards-backup'
88+DASHBOARDS_BACKUP_CRON_TMPL = 'juju-dashboards-backup.j2'
89
90
91 def install_packages():
92@@ -64,6 +68,57 @@
93 kv.set('grafana.port', new_port)
94
95
96+def select_query(query, params=None):
97+ conn = sqlite3.connect('/var/lib/grafana/grafana.db', timeout=30)
98+ cur = conn.cursor()
99+ if params:
100+ return cur.execute(query, params).fetchall()
101+ else:
102+ return cur.execute(query).fetchall()
103+
104+
105+def insert_query(query, params=None):
106+ conn = sqlite3.connect('/var/lib/grafana/grafana.db', timeout=30)
107+ cur = conn.cursor()
108+ if params:
109+ cur.execute(query, params)
110+ else:
111+ cur.execute(query)
112+ conn.commit()
113+
114+
115+def add_backup_api_keys():
116+ name = 'juju-dashboards-backup'
117+ passwd = host.pwgen(32)
118+ hpasswd = hpwgen(passwd, name)
119+ hookenv.log('Adding backup API keys for all organizations...')
120+ for i in select_query('SELECT id FROM org'):
121+ org_id = i[0]
122+ if select_query('SELECT id FROM api_key WHERE org_id=? AND name=?', [org_id, name]):
123+ hookenv.log('API key {} in org {} already exists, skipping'.format(name, org_id))
124+ continue
125+ j = {'n': name,
126+ 'k': passwd,
127+ 'id': org_id}
128+ encoded = base64.b64encode(json.dumps(j).encode('ascii')).decode('ascii')
129+ stmt = 'INSERT INTO api_key (org_id, name, key, role, created, updated)' + \
130+ ' VALUES (?,?,?,"Viewer",?,?)'
131+ dtime = datetime.datetime.today().strftime("%F %T")
132+ params = [org_id, name, hpasswd, dtime, dtime]
133+ insert_query(stmt, params)
134+
135+ kv = unitdata.kv()
136+ backup_keys = kv.get('grafana.dashboards_backup_keys', False)
137+ if not backup_keys:
138+ backup_keys = [encoded]
139+ else:
140+ backup_keys.append(encoded)
141+ kv.set('grafana.dashboards_backup_keys', backup_keys)
142+
143+ kv = unitdata.kv()
144+ return kv.get('grafana.dashboards_backup_keys')
145+
146+
147 @when_not('grafana.started')
148 def setup_grafana():
149 hookenv.status_set('maintenance', 'Configuring grafana')
150@@ -76,6 +131,20 @@
151 owner='root', group='grafana',
152 perms=0o640,
153 )
154+ if config.get('dashboards_backup_schedule', False):
155+ hookenv.log('Setting up dashboards backup job...')
156+ host.rsync('files/dashboards_backup', '/usr/local/bin/dashboards_backup')
157+ host.mkdir(config.get('dashboards_backup_dir'))
158+ settings = {'schedule': config.get('dashboards_backup_schedule'),
159+ 'directory': config.get('dashboards_backup_dir'),
160+ 'backup_keys': ' '.join(add_backup_api_keys())}
161+ render(source=DASHBOARDS_BACKUP_CRON_TMPL,
162+ target=DASHBOARDS_BACKUP_CRON,
163+ context=settings,
164+ owner='root', group='root',
165+ perms=0o640,
166+ )
167+ # copy script, create cronjob, ensure directory exists
168 check_ports(config.get('port'))
169 set_state('grafana.start')
170 hookenv.status_set('active', 'Ready')
171
172=== added file 'templates/juju-dashboards-backup.j2'
173--- templates/juju-dashboards-backup.j2 1970-01-01 00:00:00 +0000
174+++ templates/juju-dashboards-backup.j2 2016-07-14 15:09:02 +0000
175@@ -0,0 +1,5 @@
176+#
177+# Please do not edit. Juju will overwrite it.
178+#
179+{{ schedule }} root /usr/local/bin/dashboards_backup -d {{ directory }} {{ backup_keys }}
180+

Subscribers

People subscribed via source and target branches

to all changes: