timeboard.py (12138B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\datadog\dogshell\timeboard.py 2 from __future__ import print_function 3 import os.path 4 import platform 5 import sys 6 import webbrowser 7 import argparse 8 from datadog import api 9 from datadog.dogshell.common import report_errors, report_warnings, print_err 10 from datadog.util.compat import json 11 from datadog.util.format import pretty_json 12 from datetime import datetime 13 14 class TimeboardClient(object): 15 16 @classmethod 17 def setup_parser(cls, subparsers): 18 parser = subparsers.add_parser('timeboard', help='Create, edit, and delete timeboards') 19 parser.add_argument('--string_ids', action='store_true', dest='string_ids', help='Represent timeboard IDs as strings instead of ints in JSON') 20 verb_parsers = parser.add_subparsers(title='Verbs', dest='verb') 21 verb_parsers.required = True 22 post_parser = verb_parsers.add_parser('post', help='Create timeboards') 23 post_parser.add_argument('title', help='title for the new timeboard') 24 post_parser.add_argument('description', help='short description of the timeboard') 25 post_parser.add_argument('graphs', help='graph definitions as a JSON string. if unset, reads from stdin.', nargs='?') 26 post_parser.add_argument('--template_variables', type=_template_variables, default=[], help="a json list of template variable dicts, e.g. [{'name': 'host', 'prefix': 'host', 'default': 'host:my-host'}]'") 27 post_parser.set_defaults(func=cls._post) 28 update_parser = verb_parsers.add_parser('update', help='Update existing timeboards') 29 update_parser.add_argument('timeboard_id', help='timeboard to replace with the new definition') 30 update_parser.add_argument('title', help='new title for the timeboard') 31 update_parser.add_argument('description', help='short description of the timeboard') 32 update_parser.add_argument('graphs', help='graph definitions as a JSON string. if unset, reads from stdin', nargs='?') 33 update_parser.add_argument('--template_variables', type=_template_variables, default=[], help="a json list of template variable dicts, e.g. [{'name': 'host', 'prefix': 'host', 'default': 'host:my-host'}]'") 34 update_parser.set_defaults(func=cls._update) 35 show_parser = verb_parsers.add_parser('show', help='Show a timeboard definition') 36 show_parser.add_argument('timeboard_id', help='timeboard to show') 37 show_parser.set_defaults(func=cls._show) 38 show_all_parser = verb_parsers.add_parser('show_all', help='Show a list of all timeboards') 39 show_all_parser.set_defaults(func=cls._show_all) 40 pull_parser = verb_parsers.add_parser('pull', help='Pull a timeboard on the server into a local file') 41 pull_parser.add_argument('timeboard_id', help='ID of timeboard to pull') 42 pull_parser.add_argument('filename', help='file to pull timeboard into') 43 pull_parser.set_defaults(func=cls._pull) 44 pull_all_parser = verb_parsers.add_parser('pull_all', help='Pull all timeboards into files in a directory') 45 pull_all_parser.add_argument('pull_dir', help='directory to pull timeboards into') 46 pull_all_parser.set_defaults(func=cls._pull_all) 47 push_parser = verb_parsers.add_parser('push', help='Push updates to timeboards from local files to the server') 48 push_parser.add_argument('--append_auto_text', action='store_true', dest='append_auto_text', help='When pushing to the server, appends filename and timestamp to the end of the timeboard description') 49 push_parser.add_argument('file', help='timeboard files to push to the server', nargs='+', type=argparse.FileType('r')) 50 push_parser.set_defaults(func=cls._push) 51 new_file_parser = verb_parsers.add_parser('new_file', help='Create a new timeboard and put its contents in a file') 52 new_file_parser.add_argument('filename', help='name of file to create with empty timeboard') 53 new_file_parser.add_argument('graphs', help='graph definitions as a JSON string. if unset, reads from stdin.', nargs='?') 54 new_file_parser.set_defaults(func=cls._new_file) 55 web_view_parser = verb_parsers.add_parser('web_view', help='View the timeboard in a web browser') 56 web_view_parser.add_argument('file', help='timeboard file', type=argparse.FileType('r')) 57 web_view_parser.set_defaults(func=cls._web_view) 58 delete_parser = verb_parsers.add_parser('delete', help='Delete timeboards') 59 delete_parser.add_argument('timeboard_id', help='timeboard to delete') 60 delete_parser.set_defaults(func=cls._delete) 61 62 @classmethod 63 def _pull(cls, args): 64 cls._write_dash_to_file(args.timeboard_id, args.filename, args.timeout, args.format, args.string_ids) 65 66 @classmethod 67 def _pull_all(cls, args): 68 api._timeout = args.timeout 69 70 def _title_to_filename(title): 71 no_punct = ''.join([ c for c in title.lower() if c.isalnum() or c in (' ', '_', '-') ]) 72 return no_punct.replace(' ', '_').replace('-', '_').strip('_') 73 74 format = args.format 75 res = api.Timeboard.get_all() 76 report_warnings(res) 77 report_errors(res) 78 if not os.path.exists(args.pull_dir): 79 os.mkdir(args.pull_dir, 493) 80 used_filenames = set() 81 for dash_summary in res['dashes']: 82 filename = _title_to_filename(dash_summary['title']) 83 if filename in used_filenames: 84 filename = filename + '-' + dash_summary['id'] 85 used_filenames.add(filename) 86 cls._write_dash_to_file(dash_summary['id'], os.path.join(args.pull_dir, filename + '.json'), args.timeout, format, args.string_ids) 87 88 if format == 'pretty': 89 print('\n### Total: {0} dashboards to {1} ###'.format(len(used_filenames), os.path.realpath(args.pull_dir))) 90 91 @classmethod 92 def _new_file(cls, args): 93 api._timeout = args.timeout 94 format = args.format 95 graphs = args.graphs 96 if args.graphs is None: 97 graphs = sys.stdin.read() 98 try: 99 graphs = json.loads(graphs) 100 except: 101 raise Exception('bad json parameter') 102 103 res = api.Timeboard.create(title=args.filename, description='Description for {0}'.format(args.filename), graphs=[graphs]) 104 report_warnings(res) 105 report_errors(res) 106 cls._write_dash_to_file(res['dash']['id'], args.filename, args.timeout, format, args.string_ids) 107 if format == 'pretty': 108 print(pretty_json(res)) 109 else: 110 print(json.dumps(res)) 111 112 @classmethod 113 def _write_dash_to_file(cls, dash_id, filename, timeout, format = 'raw', string_ids = False): 114 with open(filename, 'w') as f: 115 res = api.Timeboard.get(dash_id) 116 report_warnings(res) 117 report_errors(res) 118 dash_obj = res['dash'] 119 if 'resource' in dash_obj: 120 del dash_obj['resource'] 121 if 'url' in dash_obj: 122 del dash_obj['url'] 123 if string_ids: 124 dash_obj['id'] = str(dash_obj['id']) 125 json.dump(dash_obj, f, indent=2) 126 if format == 'pretty': 127 print(u'Downloaded dashboard {0} to file {1}'.format(dash_id, filename)) 128 else: 129 print(u'{0} {1}'.format(dash_id, filename)) 130 131 @classmethod 132 def _push(cls, args): 133 api._timeout = args.timeout 134 for f in args.file: 135 try: 136 dash_obj = json.load(f) 137 except Exception as err: 138 raise Exception('Could not parse {0}: {1}'.format(f.name, err)) 139 140 if args.append_auto_text: 141 datetime_str = datetime.now().strftime('%x %X') 142 auto_text = '<br/>\nUpdated at {0} from {1} ({2}) on {3}'.format(datetime_str, f.name, dash_obj['id'], platform.node()) 143 dash_obj['description'] += auto_text 144 tpl_vars = dash_obj.get('template_variables', []) 145 if 'id' in dash_obj: 146 dash_obj['id'] = int(dash_obj['id']) 147 res = api.Timeboard.update(dash_obj['id'], title=dash_obj['title'], description=dash_obj['description'], graphs=dash_obj['graphs'], template_variables=tpl_vars) 148 else: 149 res = api.Timeboard.create(title=dash_obj['title'], description=dash_obj['description'], graphs=dash_obj['graphs'], template_variables=tpl_vars) 150 if 'errors' in res: 151 print_err('Upload of dashboard {0} from file {1} failed.'.format(dash_obj['id'], f.name)) 152 report_warnings(res) 153 report_errors(res) 154 if format == 'pretty': 155 print(pretty_json(res)) 156 else: 157 print(json.dumps(res)) 158 if args.format == 'pretty': 159 print('Uploaded file {0} (dashboard {1})'.format(f.name, dash_obj['id'])) 160 161 @classmethod 162 def _post(cls, args): 163 api._timeout = args.timeout 164 format = args.format 165 graphs = args.graphs 166 if args.graphs is None: 167 graphs = sys.stdin.read() 168 try: 169 graphs = json.loads(graphs) 170 except: 171 raise Exception('bad json parameter') 172 173 res = api.Timeboard.create(title=args.title, description=args.description, graphs=[graphs], template_variables=args.template_variables) 174 report_warnings(res) 175 report_errors(res) 176 if format == 'pretty': 177 print(pretty_json(res)) 178 else: 179 print(json.dumps(res)) 180 181 @classmethod 182 def _update(cls, args): 183 api._timeout = args.timeout 184 format = args.format 185 graphs = args.graphs 186 if args.graphs is None: 187 graphs = sys.stdin.read() 188 try: 189 graphs = json.loads(graphs) 190 except: 191 raise Exception('bad json parameter') 192 193 res = api.Timeboard.update(args.timeboard_id, title=args.title, description=args.description, graphs=graphs, template_variables=args.template_variables) 194 report_warnings(res) 195 report_errors(res) 196 if format == 'pretty': 197 print(pretty_json(res)) 198 else: 199 print(json.dumps(res)) 200 201 @classmethod 202 def _show(cls, args): 203 api._timeout = args.timeout 204 format = args.format 205 res = api.Timeboard.get(args.timeboard_id) 206 report_warnings(res) 207 report_errors(res) 208 if args.string_ids: 209 res['dash']['id'] = str(res['dash']['id']) 210 if format == 'pretty': 211 print(pretty_json(res)) 212 else: 213 print(json.dumps(res)) 214 215 @classmethod 216 def _show_all(cls, args): 217 api._timeout = args.timeout 218 format = args.format 219 res = api.Timeboard.get_all() 220 report_warnings(res) 221 report_errors(res) 222 if args.string_ids: 223 for d in res['dashes']: 224 d['id'] = str(d['id']) 225 226 if format == 'pretty': 227 print(pretty_json(res)) 228 elif format == 'raw': 229 print(json.dumps(res)) 230 else: 231 for d in res['dashes']: 232 print('\t'.join([d['id'], 233 d['resource'], 234 d['title'], 235 cls._escape(d['description'])])) 236 237 @classmethod 238 def _delete(cls, args): 239 api._timeout = args.timeout 240 res = api.Timeboard.delete(args.timeboard_id) 241 if res is not None: 242 report_warnings(res) 243 report_errors(res) 244 245 @classmethod 246 def _web_view(cls, args): 247 dash_id = json.load(args.file)['id'] 248 url = api._api_host + '/dash/dash/{0}'.format(dash_id) 249 webbrowser.open(url) 250 251 @classmethod 252 def _escape(cls, s): 253 return s.replace('\r', '\\r').replace('\n', '\\n').replace('\t', '\\t') 254 255 256 def _template_variables(tpl_var_input): 257 if '[' not in tpl_var_input: 258 return [ v.strip() for v in tpl_var_input.split(',') ] 259 try: 260 return json.loads(tpl_var_input) 261 except Exception: 262 raise argparse.ArgumentTypeError('bad template_variable json parameter')