diff --git a/grafana_backup/archive.py b/grafana_backup/archive.py index 5c179198f1ace49463a932f4e9712ab85a5be403..af1729e264a4d3786ac0935c4cb17bac0a5a140f 100755 --- a/grafana_backup/archive.py +++ b/grafana_backup/archive.py @@ -9,7 +9,7 @@ def main(args, settings): archive_file = '{0}/{1}.tar.gz'.format(backup_dir, timestamp) backup_files = list() - for folder_name in ['folders', 'datasources', 'dashboards', 'alert_channels', 'organizations', 'users', 'snapshots', 'versions']: + for folder_name in ['folders', 'datasources', 'dashboards', 'alert_channels', 'organizations', 'users', 'snapshots', 'versions', 'annotations']: backup_path = '{0}/{1}/{2}'.format(backup_dir, folder_name, timestamp) for file_path in glob(backup_path): diff --git a/grafana_backup/cli.py b/grafana_backup/cli.py index 0a983f31c8eb54cdfa4974425fe2efe63c26f80f..0c910a82e4eee0a7237bb3ab3afb1f49b1a2319e 100755 --- a/grafana_backup/cli.py +++ b/grafana_backup/cli.py @@ -10,8 +10,8 @@ docstring = """ {0} {1} Usage: - grafana-backup save [--config=<filename>] [--components=<folders,dashboards,datasources,alert-channels,organizations,users,snapshots,versions>] [--no-archive] - grafana-backup restore <archive_file> [--config=<filename>] [--components=<folders,dashboards,datasources,alert-channels,organizations,users,snapshots>] + grafana-backup save [--config=<filename>] [--components=<folders,dashboards,datasources,alert-channels,organizations,users,snapshots,versions,annotations>] [--no-archive] + grafana-backup restore <archive_file> [--config=<filename>] [--components=<folders,dashboards,datasources,alert-channels,organizations,users,snapshots,annotations>] grafana-backup [--config=<filename>] grafana-backup -h | --help grafana-backup --version @@ -20,7 +20,7 @@ Options: -h --help Show this help message and exit --version Get version information and exit --config=<filename> Override default configuration path - --components=<folders,dashboards,datasources,alert-channels,organizations,users,snapshots,versions> Comma separated list of individual components to backup + --components=<folders,dashboards,datasources,alert-channels,organizations,users,snapshots,versions,annotations> Comma separated list of individual components to backup rather than backing up all components by default. Versions can only be saved not restored. --no-archive Skip archive creation and do not delete unarchived files (used for troubleshooting purposes) diff --git a/grafana_backup/dashboardApi.py b/grafana_backup/dashboardApi.py index d64c967427ee16fd96a97ba5d2cb6c1d76ac511a..845a52418ce3c48925eb0fe78685bced979190d9 100755 --- a/grafana_backup/dashboardApi.py +++ b/grafana_backup/dashboardApi.py @@ -84,6 +84,13 @@ def get_dashboard(board_uri, grafana_url, http_get_headers, verify_ssl, client_c return (status_code, content) +def search_annotations(grafana_url, ts_from, ts_to, http_get_headers, verify_ssl, client_cert, debug): + url = '{0}/api/annotations?limit=5000&from={1}&to={2}'.format(grafana_url, ts_from, ts_to) + print("query annotation uri: {0}".format(url)) + (status_code, content) = send_grafana_get(url, http_get_headers, verify_ssl, client_cert, debug) + return (status_code, content) + + def search_alert_channels(grafana_url, http_get_headers, verify_ssl, client_cert, debug): url = '{0}/api/alert-notifications'.format(grafana_url) print("search alert channels in grafana: {0}".format(url)) diff --git a/grafana_backup/save.py b/grafana_backup/save.py index f3edc145b36fd9d43581ed61196707871f722f24..cefcc8638b6fe9f67642e6f031a960efee5b7dbc 100755 --- a/grafana_backup/save.py +++ b/grafana_backup/save.py @@ -5,6 +5,7 @@ from grafana_backup.save_folders import main as save_folders from grafana_backup.save_alert_channels import main as save_alert_channels from grafana_backup.save_snapshots import main as save_snapshots from grafana_backup.save_versions import main as save_versions +from grafana_backup.save_annotations import main as save_annotations from grafana_backup.archive import main as archive from grafana_backup.s3_upload import main as s3_upload from grafana_backup.save_orgs import main as save_orgs @@ -25,7 +26,8 @@ def main(args, settings): 'organizations': save_orgs, 'users': save_users, 'snapshots': save_snapshots, - 'versions': save_versions} + 'versions': save_versions, + 'annotations': save_annotations} (status, json_resp, uid_support, paging_support) = api_checks(settings) diff --git a/grafana_backup/save_annotations.py b/grafana_backup/save_annotations.py new file mode 100644 index 0000000000000000000000000000000000000000..289473160c237d06cfa59ee1795b07eadfdd15c9 --- /dev/null +++ b/grafana_backup/save_annotations.py @@ -0,0 +1,53 @@ +import os +import time +from grafana_backup.dashboardApi import search_annotations +from grafana_backup.commons import print_horizontal_line, save_json + + +def main(args, settings): + backup_dir = settings.get('BACKUP_DIR') + timestamp = settings.get('TIMESTAMP') + grafana_url = settings.get('GRAFANA_URL') + http_get_headers = settings.get('HTTP_GET_HEADERS') + verify_ssl = settings.get('VERIFY_SSL') + client_cert = settings.get('CLIENT_CERT') + debug = settings.get('DEBUG') + pretty_print = settings.get('PRETTY_PRINT') + + folder_path = '{0}/annotations/{1}'.format(backup_dir, timestamp) + 'annotations_{0}.txt'.format(timestamp) + + if not os.path.exists(folder_path): + os.makedirs(folder_path) + + get_all_annotations_and_save(folder_path, grafana_url, http_get_headers, verify_ssl, client_cert, debug, pretty_print) + print_horizontal_line() + + +def save_annotation(file_name, annotation_setting, folder_path, pretty_print): + file_path = save_json(file_name, annotation_setting, folder_path, 'annotation', pretty_print) + # print("annotation: {0} is saved to {1}".format(file_name, file_path)) + + +def get_all_annotations_and_save(folder_path, grafana_url, http_get_headers, verify_ssl, client_cert, debug, pretty_print): + now = int(round(time.time() * 1000)) + one_day_in_ms = 24 * 60 * 60 * 1000 + + ts_to = now + ts_from = now - one_day_in_ms + thirteen_months_retention = (now - (13 * 31 * one_day_in_ms)) + + while ts_from > thirteen_months_retention: + status_code_and_content = search_annotations(grafana_url, ts_from, ts_to, http_get_headers, verify_ssl, client_cert, debug) + if status_code_and_content[0] == 200: + annotations_batch = status_code_and_content[1] + print("There are {0} annotations:".format(len(annotations_batch))) + for annotation in annotations_batch: + # print(annotation) + save_annotation(str(annotation['id']), annotation, folder_path, pretty_print) + else: + print("query annotation failed, status: {0}, msg: {1}".format(status_code_and_content[0], + status_code_and_content[1])) + + ts_to = ts_from + ts_from = ts_from - one_day_in_ms