]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/ci/check_grafana_uids.py
f82ddbbc8bab7355bce536bc02713969645066a8
[ceph.git] / ceph / src / pybind / mgr / dashboard / ci / check_grafana_uids.py
1 # -*- coding: utf-8 -*-
2 # pylint: disable=F0401
3 """
4 This script does:
5 * Scan through Angular html templates and extract <cd-grafana> tags
6 * Check if every tag has a corresponding Grafana dashboard by `uid`
7
8 Usage:
9 python <script> <angular_app_dir> <grafana_dashboard_dir>
10
11 e.g.
12 cd /ceph/src/pybind/mgr/dashboard
13 python ci/<script> frontend/src/app /ceph/monitoring/grafana/dashboards
14 """
15 import argparse
16 import codecs
17 import copy
18 import json
19 import os
20 from html.parser import HTMLParser
21
22
23 class TemplateParser(HTMLParser):
24
25 def __init__(self, _file, search_tag):
26 super().__init__()
27 self.search_tag = search_tag
28 self.file = _file
29 self.parsed_data = []
30
31 def parse(self):
32 with codecs.open(self.file, encoding='UTF-8') as f:
33 self.feed(f.read())
34
35 def handle_starttag(self, tag, attrs):
36 if tag != self.search_tag:
37 return
38 tag_data = {
39 'file': self.file,
40 'attrs': dict(attrs),
41 'line': self.getpos()[0]
42 }
43 self.parsed_data.append(tag_data)
44
45 def error(self, message):
46 error_msg = 'fail to parse file {} (@{}): {}'.\
47 format(self.file, self.getpos(), message)
48 exit(error_msg)
49
50
51 def get_files(base_dir, file_ext):
52 result = []
53 for root, _, files in os.walk(base_dir):
54 for _file in files:
55 if _file.endswith('.{}'.format(file_ext)):
56 result.append(os.path.join(root, _file))
57 return result
58
59
60 def get_tags(base_dir, tag='cd-grafana'):
61 templates = get_files(base_dir, 'html')
62 tags = []
63 for templ in templates:
64 parser = TemplateParser(templ, tag)
65 parser.parse()
66 if parser.parsed_data:
67 tags.extend(parser.parsed_data)
68 return tags
69
70
71 def get_grafana_dashboards(base_dir):
72 json_files = get_files(base_dir, 'json')
73 dashboards = {}
74 for json_file in json_files:
75 with open(json_file) as f:
76 dashboard_config = json.load(f)
77 uid = dashboard_config.get('uid')
78
79 # Grafana dashboard checks
80 title = dashboard_config['title']
81 assert len(title) > 0, \
82 "Title not found in '{}'".format(json_file)
83 assert len(dashboard_config.get('links', [])) == 0, \
84 "Links found in '{}'".format(json_file)
85 if not uid:
86 continue
87 if uid in dashboards:
88 # duplicated uids
89 error_msg = 'Duplicated UID {} found, already defined in {}'.\
90 format(uid, dashboards[uid]['file'])
91 exit(error_msg)
92
93 dashboards[uid] = {
94 'file': json_file,
95 'title': title
96 }
97 return dashboards
98
99
100 def parse_args():
101 long_desc = ('Check every <cd-grafana> component in Angular template has a'
102 ' mapped Grafana dashboard.')
103 parser = argparse.ArgumentParser(description=long_desc)
104 parser.add_argument('angular_app_dir', type=str,
105 help='Angular app base directory')
106 parser.add_argument('grafana_dash_dir', type=str,
107 help='Directory contains Grafana dashboard JSON files')
108 parser.add_argument('--verbose', action='store_true',
109 help='Display verbose mapping information.')
110 return parser.parse_args()
111
112
113 def main():
114 args = parse_args()
115 tags = get_tags(args.angular_app_dir)
116 grafana_dashboards = get_grafana_dashboards(args.grafana_dash_dir)
117 verbose = args.verbose
118
119 if not tags:
120 error_msg = 'Can not find any cd-grafana component under {}'.\
121 format(args.angular_app_dir)
122 exit(error_msg)
123
124 if verbose:
125 print('Found mappings:')
126 no_dashboard_tags = []
127 for tag in tags:
128 uid = tag['attrs']['uid']
129 if uid not in grafana_dashboards:
130 no_dashboard_tags.append(copy.copy(tag))
131 continue
132 if verbose:
133 msg = '{} ({}:{}) \n\t-> {} ({})'.\
134 format(uid, tag['file'], tag['line'],
135 grafana_dashboards[uid]['title'],
136 grafana_dashboards[uid]['file'])
137 print(msg)
138
139 if no_dashboard_tags:
140 title = ('Checking Grafana dashboards UIDs: ERROR\n'
141 'Components that have no mapped Grafana dashboards:\n')
142 lines = ('{} ({}:{})'.format(tag['attrs']['uid'],
143 tag['file'],
144 tag['line'])
145 for tag in no_dashboard_tags)
146 error_msg = title + '\n'.join(lines)
147 exit(error_msg)
148 else:
149 print('Checking Grafana dashboards UIDs: OK')
150
151
152 if __name__ == '__main__':
153 main()