##// END OF EJS Templates
general metrics: require tags to be present
ergo -
Show More
@@ -1,743 +1,743 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # AppEnlight Enterprise Edition, including its added features, Support
18 # AppEnlight Enterprise Edition, including its added features, Support
19 # services, and proprietary license terms, please see
19 # services, and proprietary license terms, please see
20 # https://rhodecode.com/licenses/
20 # https://rhodecode.com/licenses/
21
21
22 import datetime
22 import datetime
23
23
24 import colander
24 import colander
25 from colander import null
25 from colander import null
26
26
27 # those keywords are here so we can distingush between searching for tags and
27 # those keywords are here so we can distingush between searching for tags and
28 # normal properties of reports/logs
28 # normal properties of reports/logs
29 accepted_search_params = ['resource',
29 accepted_search_params = ['resource',
30 'request_id',
30 'request_id',
31 'start_date',
31 'start_date',
32 'end_date',
32 'end_date',
33 'page',
33 'page',
34 'min_occurences',
34 'min_occurences',
35 'http_status',
35 'http_status',
36 'priority',
36 'priority',
37 'error',
37 'error',
38 'url_path',
38 'url_path',
39 'url_domain',
39 'url_domain',
40 'report_status',
40 'report_status',
41 'min_duration',
41 'min_duration',
42 'max_duration',
42 'max_duration',
43 'message',
43 'message',
44 'level',
44 'level',
45 'namespace']
45 'namespace']
46
46
47
47
48 @colander.deferred
48 @colander.deferred
49 def deferred_utcnow(node, kw):
49 def deferred_utcnow(node, kw):
50 return kw['utcnow']
50 return kw['utcnow']
51
51
52
52
53 def lowercase_preparer(input_data):
53 def lowercase_preparer(input_data):
54 """
54 """
55 Transforms a list of string entries to lowercase
55 Transforms a list of string entries to lowercase
56 Used in search query validation
56 Used in search query validation
57 """
57 """
58 if not input_data:
58 if not input_data:
59 return input_data
59 return input_data
60 return [x.lower() for x in input_data]
60 return [x.lower() for x in input_data]
61
61
62
62
63 def shortener_factory(cutoff_size=32):
63 def shortener_factory(cutoff_size=32):
64 """
64 """
65 Limits the input data to specific character count
65 Limits the input data to specific character count
66 :arg cutoff_cutoff_size How much characters to store
66 :arg cutoff_cutoff_size How much characters to store
67
67
68 """
68 """
69
69
70 def shortener(input_data):
70 def shortener(input_data):
71 if not input_data:
71 if not input_data:
72 return input_data
72 return input_data
73 else:
73 else:
74 if isinstance(input_data, str):
74 if isinstance(input_data, str):
75 return input_data[:cutoff_size]
75 return input_data[:cutoff_size]
76 else:
76 else:
77 return input_data
77 return input_data
78
78
79 return shortener
79 return shortener
80
80
81
81
82 def cast_to_unicode_or_null(value):
82 def cast_to_unicode_or_null(value):
83 if value is not colander.null:
83 if value is not colander.null:
84 return str(value)
84 return str(value)
85 return None
85 return None
86
86
87
87
88 class NonTZDate(colander.DateTime):
88 class NonTZDate(colander.DateTime):
89 """ Returns null for incorrect date format - also removes tz info"""
89 """ Returns null for incorrect date format - also removes tz info"""
90
90
91 def deserialize(self, node, cstruct):
91 def deserialize(self, node, cstruct):
92 # disabled for now
92 # disabled for now
93 # if cstruct and isinstance(cstruct, str):
93 # if cstruct and isinstance(cstruct, str):
94 # if ':' not in cstruct:
94 # if ':' not in cstruct:
95 # cstruct += ':0.0'
95 # cstruct += ':0.0'
96 # if '.' not in cstruct:
96 # if '.' not in cstruct:
97 # cstruct += '.0'
97 # cstruct += '.0'
98 value = super(NonTZDate, self).deserialize(node, cstruct)
98 value = super(NonTZDate, self).deserialize(node, cstruct)
99 if value:
99 if value:
100 return value.replace(tzinfo=None)
100 return value.replace(tzinfo=None)
101 return value
101 return value
102
102
103
103
104 class UnknownType(object):
104 class UnknownType(object):
105 """
105 """
106 Universal type that will accept a deserialized JSON object and store it unaltered
106 Universal type that will accept a deserialized JSON object and store it unaltered
107 """
107 """
108
108
109 def serialize(self, node, appstruct):
109 def serialize(self, node, appstruct):
110 if appstruct is null:
110 if appstruct is null:
111 return null
111 return null
112 return appstruct
112 return appstruct
113
113
114 def deserialize(self, node, cstruct):
114 def deserialize(self, node, cstruct):
115 if cstruct is null:
115 if cstruct is null:
116 return null
116 return null
117 return cstruct
117 return cstruct
118
118
119 def cstruct_children(self):
119 def cstruct_children(self):
120 return []
120 return []
121
121
122
122
123 # SLOW REPORT SCHEMA
123 # SLOW REPORT SCHEMA
124
124
125 def rewrite_type(input_data):
125 def rewrite_type(input_data):
126 """
126 """
127 Fix for legacy appenlight clients
127 Fix for legacy appenlight clients
128 """
128 """
129 if input_data == 'remote_call':
129 if input_data == 'remote_call':
130 return 'remote'
130 return 'remote'
131 return input_data
131 return input_data
132
132
133
133
134 class ExtraTupleSchema(colander.TupleSchema):
134 class ExtraTupleSchema(colander.TupleSchema):
135 name = colander.SchemaNode(colander.String(),
135 name = colander.SchemaNode(colander.String(),
136 validator=colander.Length(1, 64))
136 validator=colander.Length(1, 64))
137 value = colander.SchemaNode(UnknownType(),
137 value = colander.SchemaNode(UnknownType(),
138 preparer=shortener_factory(512),
138 preparer=shortener_factory(512),
139 missing=None)
139 missing=None)
140
140
141
141
142 class ExtraSchemaList(colander.SequenceSchema):
142 class ExtraSchemaList(colander.SequenceSchema):
143 tag = ExtraTupleSchema()
143 tag = ExtraTupleSchema()
144 missing = None
144 missing = None
145
145
146
146
147 class TagsTupleSchema(colander.TupleSchema):
147 class TagsTupleSchema(colander.TupleSchema):
148 name = colander.SchemaNode(colander.String(),
148 name = colander.SchemaNode(colander.String(),
149 validator=colander.Length(1, 128))
149 validator=colander.Length(1, 128))
150 value = colander.SchemaNode(UnknownType(),
150 value = colander.SchemaNode(UnknownType(),
151 preparer=shortener_factory(128),
151 preparer=shortener_factory(128),
152 missing=None)
152 missing=None)
153
153
154
154
155 class TagSchemaList(colander.SequenceSchema):
155 class TagSchemaList(colander.SequenceSchema):
156 tag = TagsTupleSchema()
156 tag = TagsTupleSchema()
157 missing = None
157 missing = None
158
158
159
159
160 class NumericTagsTupleSchema(colander.TupleSchema):
160 class NumericTagsTupleSchema(colander.TupleSchema):
161 name = colander.SchemaNode(colander.String(),
161 name = colander.SchemaNode(colander.String(),
162 validator=colander.Length(1, 128))
162 validator=colander.Length(1, 128))
163 value = colander.SchemaNode(colander.Float(), missing=0)
163 value = colander.SchemaNode(colander.Float(), missing=0)
164
164
165
165
166 class NumericTagSchemaList(colander.SequenceSchema):
166 class NumericTagSchemaList(colander.SequenceSchema):
167 tag = NumericTagsTupleSchema()
167 tag = NumericTagsTupleSchema()
168 missing = None
168 missing = None
169
169
170
170
171 class SlowCallSchema(colander.MappingSchema):
171 class SlowCallSchema(colander.MappingSchema):
172 """
172 """
173 Validates slow call format in slow call list
173 Validates slow call format in slow call list
174 """
174 """
175 start = colander.SchemaNode(NonTZDate())
175 start = colander.SchemaNode(NonTZDate())
176 end = colander.SchemaNode(NonTZDate())
176 end = colander.SchemaNode(NonTZDate())
177 statement = colander.SchemaNode(colander.String(), missing='')
177 statement = colander.SchemaNode(colander.String(), missing='')
178 parameters = colander.SchemaNode(UnknownType(), missing=None)
178 parameters = colander.SchemaNode(UnknownType(), missing=None)
179 type = colander.SchemaNode(
179 type = colander.SchemaNode(
180 colander.String(),
180 colander.String(),
181 preparer=rewrite_type,
181 preparer=rewrite_type,
182 validator=colander.OneOf(
182 validator=colander.OneOf(
183 ['tmpl', 'sql', 'nosql', 'remote', 'unknown', 'custom']),
183 ['tmpl', 'sql', 'nosql', 'remote', 'unknown', 'custom']),
184 missing='unknown')
184 missing='unknown')
185 subtype = colander.SchemaNode(colander.String(),
185 subtype = colander.SchemaNode(colander.String(),
186 validator=colander.Length(1, 16),
186 validator=colander.Length(1, 16),
187 missing='unknown')
187 missing='unknown')
188 location = colander.SchemaNode(colander.String(),
188 location = colander.SchemaNode(colander.String(),
189 validator=colander.Length(1, 255),
189 validator=colander.Length(1, 255),
190 missing='')
190 missing='')
191
191
192
192
193 def limited_date(node, value):
193 def limited_date(node, value):
194 """ checks to make sure that the value is not older/newer than 2h """
194 """ checks to make sure that the value is not older/newer than 2h """
195 hours = 2
195 hours = 2
196 min_time = datetime.datetime.utcnow() - datetime.timedelta(hours=72)
196 min_time = datetime.datetime.utcnow() - datetime.timedelta(hours=72)
197 max_time = datetime.datetime.utcnow() + datetime.timedelta(hours=2)
197 max_time = datetime.datetime.utcnow() + datetime.timedelta(hours=2)
198 if min_time > value:
198 if min_time > value:
199 msg = '%r is older from current UTC time by ' + str(hours) + ' hours.'
199 msg = '%r is older from current UTC time by ' + str(hours) + ' hours.'
200 msg += ' Ask administrator to enable permanent logging for ' \
200 msg += ' Ask administrator to enable permanent logging for ' \
201 'your application to store logs with dates in past.'
201 'your application to store logs with dates in past.'
202 raise colander.Invalid(node, msg % value)
202 raise colander.Invalid(node, msg % value)
203 if max_time < value:
203 if max_time < value:
204 msg = '%r is newer from current UTC time by ' + str(hours) + ' hours'
204 msg = '%r is newer from current UTC time by ' + str(hours) + ' hours'
205 msg += ' Ask administrator to enable permanent logging for ' \
205 msg += ' Ask administrator to enable permanent logging for ' \
206 'your application to store logs with dates in future.'
206 'your application to store logs with dates in future.'
207 raise colander.Invalid(node, msg % value)
207 raise colander.Invalid(node, msg % value)
208
208
209
209
210 class SlowCallListSchema(colander.SequenceSchema):
210 class SlowCallListSchema(colander.SequenceSchema):
211 """
211 """
212 Validates list of individual slow calls
212 Validates list of individual slow calls
213 """
213 """
214 slow_call = SlowCallSchema()
214 slow_call = SlowCallSchema()
215
215
216
216
217 class RequestStatsSchema(colander.MappingSchema):
217 class RequestStatsSchema(colander.MappingSchema):
218 """
218 """
219 Validates format of requests statistics dictionary
219 Validates format of requests statistics dictionary
220 """
220 """
221 main = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
221 main = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
222 missing=0)
222 missing=0)
223 sql = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
223 sql = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
224 missing=0)
224 missing=0)
225 nosql = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
225 nosql = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
226 missing=0)
226 missing=0)
227 remote = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
227 remote = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
228 missing=0)
228 missing=0)
229 tmpl = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
229 tmpl = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
230 missing=0)
230 missing=0)
231 custom = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
231 custom = colander.SchemaNode(colander.Float(), validator=colander.Range(0),
232 missing=0)
232 missing=0)
233 sql_calls = colander.SchemaNode(colander.Float(),
233 sql_calls = colander.SchemaNode(colander.Float(),
234 validator=colander.Range(0),
234 validator=colander.Range(0),
235 missing=0)
235 missing=0)
236 nosql_calls = colander.SchemaNode(colander.Float(),
236 nosql_calls = colander.SchemaNode(colander.Float(),
237 validator=colander.Range(0),
237 validator=colander.Range(0),
238 missing=0)
238 missing=0)
239 remote_calls = colander.SchemaNode(colander.Float(),
239 remote_calls = colander.SchemaNode(colander.Float(),
240 validator=colander.Range(0),
240 validator=colander.Range(0),
241 missing=0)
241 missing=0)
242 tmpl_calls = colander.SchemaNode(colander.Float(),
242 tmpl_calls = colander.SchemaNode(colander.Float(),
243 validator=colander.Range(0),
243 validator=colander.Range(0),
244 missing=0)
244 missing=0)
245 custom_calls = colander.SchemaNode(colander.Float(),
245 custom_calls = colander.SchemaNode(colander.Float(),
246 validator=colander.Range(0),
246 validator=colander.Range(0),
247 missing=0)
247 missing=0)
248
248
249
249
250 class FrameInfoVarSchema(colander.SequenceSchema):
250 class FrameInfoVarSchema(colander.SequenceSchema):
251 """
251 """
252 Validates format of frame variables of a traceback
252 Validates format of frame variables of a traceback
253 """
253 """
254 vars = colander.SchemaNode(UnknownType(),
254 vars = colander.SchemaNode(UnknownType(),
255 validator=colander.Length(2, 2))
255 validator=colander.Length(2, 2))
256
256
257
257
258 class FrameInfoSchema(colander.MappingSchema):
258 class FrameInfoSchema(colander.MappingSchema):
259 """
259 """
260 Validates format of a traceback line
260 Validates format of a traceback line
261 """
261 """
262 cline = colander.SchemaNode(colander.String(), missing='')
262 cline = colander.SchemaNode(colander.String(), missing='')
263 module = colander.SchemaNode(colander.String(), missing='')
263 module = colander.SchemaNode(colander.String(), missing='')
264 line = colander.SchemaNode(colander.String(), missing='')
264 line = colander.SchemaNode(colander.String(), missing='')
265 file = colander.SchemaNode(colander.String(), missing='')
265 file = colander.SchemaNode(colander.String(), missing='')
266 fn = colander.SchemaNode(colander.String(), missing='')
266 fn = colander.SchemaNode(colander.String(), missing='')
267 vars = FrameInfoVarSchema()
267 vars = FrameInfoVarSchema()
268
268
269
269
270 class FrameInfoListSchema(colander.SequenceSchema):
270 class FrameInfoListSchema(colander.SequenceSchema):
271 """
271 """
272 Validates format of list of traceback lines
272 Validates format of list of traceback lines
273 """
273 """
274 frame = colander.SchemaNode(UnknownType())
274 frame = colander.SchemaNode(UnknownType())
275
275
276
276
277 class ReportDetailBaseSchema(colander.MappingSchema):
277 class ReportDetailBaseSchema(colander.MappingSchema):
278 """
278 """
279 Validates format of report - ie. request parameters and stats for a request in report group
279 Validates format of report - ie. request parameters and stats for a request in report group
280 """
280 """
281 username = colander.SchemaNode(colander.String(),
281 username = colander.SchemaNode(colander.String(),
282 preparer=[shortener_factory(255),
282 preparer=[shortener_factory(255),
283 lambda x: x or ''],
283 lambda x: x or ''],
284 missing='')
284 missing='')
285 request_id = colander.SchemaNode(colander.String(),
285 request_id = colander.SchemaNode(colander.String(),
286 preparer=shortener_factory(40),
286 preparer=shortener_factory(40),
287 missing='')
287 missing='')
288 url = colander.SchemaNode(colander.String(),
288 url = colander.SchemaNode(colander.String(),
289 preparer=shortener_factory(1024), missing='')
289 preparer=shortener_factory(1024), missing='')
290 ip = colander.SchemaNode(colander.String(), preparer=shortener_factory(39),
290 ip = colander.SchemaNode(colander.String(), preparer=shortener_factory(39),
291 missing=None)
291 missing=None)
292 start_time = colander.SchemaNode(NonTZDate(), validator=limited_date,
292 start_time = colander.SchemaNode(NonTZDate(), validator=limited_date,
293 missing=deferred_utcnow)
293 missing=deferred_utcnow)
294 end_time = colander.SchemaNode(NonTZDate(), validator=limited_date,
294 end_time = colander.SchemaNode(NonTZDate(), validator=limited_date,
295 missing=None)
295 missing=None)
296 user_agent = colander.SchemaNode(colander.String(),
296 user_agent = colander.SchemaNode(colander.String(),
297 preparer=[shortener_factory(512),
297 preparer=[shortener_factory(512),
298 lambda x: x or ''],
298 lambda x: x or ''],
299 missing='')
299 missing='')
300 message = colander.SchemaNode(colander.String(),
300 message = colander.SchemaNode(colander.String(),
301 preparer=shortener_factory(2048),
301 preparer=shortener_factory(2048),
302 missing='')
302 missing='')
303 group_string = colander.SchemaNode(colander.String(),
303 group_string = colander.SchemaNode(colander.String(),
304 validator=colander.Length(1, 512),
304 validator=colander.Length(1, 512),
305 missing=None)
305 missing=None)
306 request_stats = RequestStatsSchema(missing=None)
306 request_stats = RequestStatsSchema(missing=None)
307 request = colander.SchemaNode(colander.Mapping(unknown='preserve'),
307 request = colander.SchemaNode(colander.Mapping(unknown='preserve'),
308 missing={})
308 missing={})
309 traceback = FrameInfoListSchema(missing=None)
309 traceback = FrameInfoListSchema(missing=None)
310 slow_calls = SlowCallListSchema(missing=[])
310 slow_calls = SlowCallListSchema(missing=[])
311 extra = ExtraSchemaList()
311 extra = ExtraSchemaList()
312
312
313
313
314 class ReportDetailSchema_0_4(ReportDetailBaseSchema):
314 class ReportDetailSchema_0_4(ReportDetailBaseSchema):
315 frameinfo = FrameInfoListSchema(missing=None)
315 frameinfo = FrameInfoListSchema(missing=None)
316
316
317
317
318 class ReportDetailSchema_0_5(ReportDetailBaseSchema):
318 class ReportDetailSchema_0_5(ReportDetailBaseSchema):
319 pass
319 pass
320
320
321
321
322 class ReportDetailListSchema(colander.SequenceSchema):
322 class ReportDetailListSchema(colander.SequenceSchema):
323 """
323 """
324 Validates format of list of reports
324 Validates format of list of reports
325 """
325 """
326 report_detail = ReportDetailSchema_0_4()
326 report_detail = ReportDetailSchema_0_4()
327 validator = colander.Length(1)
327 validator = colander.Length(1)
328
328
329
329
330 class ReportSchemaBase(colander.MappingSchema):
330 class ReportSchemaBase(colander.MappingSchema):
331 """
331 """
332 Validates format of report group
332 Validates format of report group
333 """
333 """
334 client = colander.SchemaNode(colander.String(),
334 client = colander.SchemaNode(colander.String(),
335 preparer=lambda x: x or 'unknown')
335 preparer=lambda x: x or 'unknown')
336 server = colander.SchemaNode(
336 server = colander.SchemaNode(
337 colander.String(),
337 colander.String(),
338 preparer=[
338 preparer=[
339 lambda x: x.lower() if x else 'unknown', shortener_factory(128)],
339 lambda x: x.lower() if x else 'unknown', shortener_factory(128)],
340 missing='unknown')
340 missing='unknown')
341 priority = colander.SchemaNode(colander.Int(),
341 priority = colander.SchemaNode(colander.Int(),
342 preparer=[lambda x: x or 5],
342 preparer=[lambda x: x or 5],
343 validator=colander.Range(1, 10),
343 validator=colander.Range(1, 10),
344 missing=5)
344 missing=5)
345 language = colander.SchemaNode(colander.String(), missing='unknown')
345 language = colander.SchemaNode(colander.String(), missing='unknown')
346 error = colander.SchemaNode(colander.String(),
346 error = colander.SchemaNode(colander.String(),
347 preparer=shortener_factory(512),
347 preparer=shortener_factory(512),
348 missing='')
348 missing='')
349 view_name = colander.SchemaNode(colander.String(),
349 view_name = colander.SchemaNode(colander.String(),
350 preparer=[shortener_factory(128),
350 preparer=[shortener_factory(128),
351 lambda x: x or ''],
351 lambda x: x or ''],
352 missing='')
352 missing='')
353 http_status = colander.SchemaNode(colander.Int(),
353 http_status = colander.SchemaNode(colander.Int(),
354 preparer=[lambda x: x or 200],
354 preparer=[lambda x: x or 200],
355 validator=colander.Range(1))
355 validator=colander.Range(1))
356
356
357 occurences = colander.SchemaNode(colander.Int(),
357 occurences = colander.SchemaNode(colander.Int(),
358 validator=colander.Range(1, 99999999999),
358 validator=colander.Range(1, 99999999999),
359 missing=1)
359 missing=1)
360 tags = TagSchemaList()
360 tags = TagSchemaList()
361
361
362
362
363 class ReportSchema_0_5(ReportSchemaBase, ReportDetailSchema_0_5):
363 class ReportSchema_0_5(ReportSchemaBase, ReportDetailSchema_0_5):
364 pass
364 pass
365
365
366
366
367 class ReportListSchema_0_5(colander.SequenceSchema):
367 class ReportListSchema_0_5(colander.SequenceSchema):
368 """
368 """
369 Validates format of list of report groups
369 Validates format of list of report groups
370 """
370 """
371 report = ReportSchema_0_5()
371 report = ReportSchema_0_5()
372 validator = colander.Length(1)
372 validator = colander.Length(1)
373
373
374
374
375 class LogSchema(colander.MappingSchema):
375 class LogSchema(colander.MappingSchema):
376 """
376 """
377 Validates format if individual log entry
377 Validates format if individual log entry
378 """
378 """
379 primary_key = colander.SchemaNode(UnknownType(),
379 primary_key = colander.SchemaNode(UnknownType(),
380 preparer=[cast_to_unicode_or_null,
380 preparer=[cast_to_unicode_or_null,
381 shortener_factory(128)],
381 shortener_factory(128)],
382 missing=None)
382 missing=None)
383 log_level = colander.SchemaNode(colander.String(),
383 log_level = colander.SchemaNode(colander.String(),
384 preparer=shortener_factory(10),
384 preparer=shortener_factory(10),
385 missing='UNKNOWN')
385 missing='UNKNOWN')
386 message = colander.SchemaNode(colander.String(),
386 message = colander.SchemaNode(colander.String(),
387 preparer=shortener_factory(4096),
387 preparer=shortener_factory(4096),
388 missing='')
388 missing='')
389 namespace = colander.SchemaNode(colander.String(),
389 namespace = colander.SchemaNode(colander.String(),
390 preparer=shortener_factory(128),
390 preparer=shortener_factory(128),
391 missing='')
391 missing='')
392 request_id = colander.SchemaNode(colander.String(),
392 request_id = colander.SchemaNode(colander.String(),
393 preparer=shortener_factory(40),
393 preparer=shortener_factory(40),
394 missing='')
394 missing='')
395 server = colander.SchemaNode(colander.String(),
395 server = colander.SchemaNode(colander.String(),
396 preparer=shortener_factory(128),
396 preparer=shortener_factory(128),
397 missing='unknown')
397 missing='unknown')
398 date = colander.SchemaNode(NonTZDate(),
398 date = colander.SchemaNode(NonTZDate(),
399 validator=limited_date,
399 validator=limited_date,
400 missing=deferred_utcnow)
400 missing=deferred_utcnow)
401 tags = TagSchemaList()
401 tags = TagSchemaList()
402
402
403
403
404 class LogSchemaPermanent(LogSchema):
404 class LogSchemaPermanent(LogSchema):
405 date = colander.SchemaNode(NonTZDate(),
405 date = colander.SchemaNode(NonTZDate(),
406 missing=deferred_utcnow)
406 missing=deferred_utcnow)
407 permanent = colander.SchemaNode(colander.Boolean(), missing=False)
407 permanent = colander.SchemaNode(colander.Boolean(), missing=False)
408
408
409
409
410 class LogListSchema(colander.SequenceSchema):
410 class LogListSchema(colander.SequenceSchema):
411 """
411 """
412 Validates format of list of log entries
412 Validates format of list of log entries
413 """
413 """
414 log = LogSchema()
414 log = LogSchema()
415 validator = colander.Length(1)
415 validator = colander.Length(1)
416
416
417
417
418 class LogListPermanentSchema(colander.SequenceSchema):
418 class LogListPermanentSchema(colander.SequenceSchema):
419 """
419 """
420 Validates format of list of log entries
420 Validates format of list of log entries
421 """
421 """
422 log = LogSchemaPermanent()
422 log = LogSchemaPermanent()
423 validator = colander.Length(1)
423 validator = colander.Length(1)
424
424
425
425
426 class ViewRequestStatsSchema(RequestStatsSchema):
426 class ViewRequestStatsSchema(RequestStatsSchema):
427 requests = colander.SchemaNode(colander.Integer(),
427 requests = colander.SchemaNode(colander.Integer(),
428 validator=colander.Range(0),
428 validator=colander.Range(0),
429 missing=0)
429 missing=0)
430
430
431
431
432 class ViewMetricTupleSchema(colander.TupleSchema):
432 class ViewMetricTupleSchema(colander.TupleSchema):
433 """
433 """
434 Validates list of views and their corresponding request stats object ie:
434 Validates list of views and their corresponding request stats object ie:
435 ["dir/module:func",{"custom": 0.0..}]
435 ["dir/module:func",{"custom": 0.0..}]
436 """
436 """
437 view_name = colander.SchemaNode(colander.String(),
437 view_name = colander.SchemaNode(colander.String(),
438 preparer=[shortener_factory(128),
438 preparer=[shortener_factory(128),
439 lambda x: x or 'unknown'],
439 lambda x: x or 'unknown'],
440 missing='unknown')
440 missing='unknown')
441 metrics = ViewRequestStatsSchema()
441 metrics = ViewRequestStatsSchema()
442
442
443
443
444 class ViewMetricListSchema(colander.SequenceSchema):
444 class ViewMetricListSchema(colander.SequenceSchema):
445 """
445 """
446 Validates view breakdown stats objects list
446 Validates view breakdown stats objects list
447 {metrics key of server/time object}
447 {metrics key of server/time object}
448 """
448 """
449 view_tuple = ViewMetricTupleSchema()
449 view_tuple = ViewMetricTupleSchema()
450 validator = colander.Length(1)
450 validator = colander.Length(1)
451
451
452
452
453 class ViewMetricSchema(colander.MappingSchema):
453 class ViewMetricSchema(colander.MappingSchema):
454 """
454 """
455 Validates server/timeinterval object, ie:
455 Validates server/timeinterval object, ie:
456 {server/time object}
456 {server/time object}
457
457
458 """
458 """
459 timestamp = colander.SchemaNode(NonTZDate(),
459 timestamp = colander.SchemaNode(NonTZDate(),
460 validator=limited_date,
460 validator=limited_date,
461 missing=None)
461 missing=None)
462 server = colander.SchemaNode(colander.String(),
462 server = colander.SchemaNode(colander.String(),
463 preparer=[shortener_factory(128),
463 preparer=[shortener_factory(128),
464 lambda x: x or 'unknown'],
464 lambda x: x or 'unknown'],
465 missing='unknown')
465 missing='unknown')
466 metrics = ViewMetricListSchema()
466 metrics = ViewMetricListSchema()
467
467
468
468
469 class GeneralMetricSchema(colander.MappingSchema):
469 class GeneralMetricSchema(colander.MappingSchema):
470 """
470 """
471 Validates universal metric schema
471 Validates universal metric schema
472
472
473 """
473 """
474 namespace = colander.SchemaNode(colander.String(), missing='',
474 namespace = colander.SchemaNode(colander.String(), missing='',
475 preparer=shortener_factory(128))
475 preparer=shortener_factory(128))
476
476
477 server_name = colander.SchemaNode(colander.String(),
477 server_name = colander.SchemaNode(colander.String(),
478 preparer=[shortener_factory(128),
478 preparer=[shortener_factory(128),
479 lambda x: x or 'unknown'],
479 lambda x: x or 'unknown'],
480 missing='unknown')
480 missing='unknown')
481 timestamp = colander.SchemaNode(NonTZDate(), validator=limited_date,
481 timestamp = colander.SchemaNode(NonTZDate(), validator=limited_date,
482 missing=deferred_utcnow)
482 missing=deferred_utcnow)
483 tags = TagSchemaList()
483 tags = TagSchemaList(missing=colander.required)
484
484
485
485
486 class GeneralMetricsListSchema(colander.SequenceSchema):
486 class GeneralMetricsListSchema(colander.SequenceSchema):
487 metric = GeneralMetricSchema()
487 metric = GeneralMetricSchema()
488 validator = colander.Length(1)
488 validator = colander.Length(1)
489
489
490
490
491 class MetricsListSchema(colander.SequenceSchema):
491 class MetricsListSchema(colander.SequenceSchema):
492 """
492 """
493 Validates list of metrics objects ie:
493 Validates list of metrics objects ie:
494 [{server/time object}, ] part
494 [{server/time object}, ] part
495
495
496
496
497 """
497 """
498 metric = ViewMetricSchema()
498 metric = ViewMetricSchema()
499 validator = colander.Length(1)
499 validator = colander.Length(1)
500
500
501
501
502 class StringToAppList(object):
502 class StringToAppList(object):
503 """
503 """
504 Returns validated list of application ids from user query and
504 Returns validated list of application ids from user query and
505 set of applications user is allowed to look at
505 set of applications user is allowed to look at
506 transform string to list containing single integer
506 transform string to list containing single integer
507 """
507 """
508
508
509 def serialize(self, node, appstruct):
509 def serialize(self, node, appstruct):
510 if appstruct is null:
510 if appstruct is null:
511 return null
511 return null
512 return appstruct
512 return appstruct
513
513
514 def deserialize(self, node, cstruct):
514 def deserialize(self, node, cstruct):
515 if cstruct is null:
515 if cstruct is null:
516 return null
516 return null
517
517
518 apps = set([int(a) for a in node.bindings['resources']])
518 apps = set([int(a) for a in node.bindings['resources']])
519
519
520 if isinstance(cstruct, str):
520 if isinstance(cstruct, str):
521 cstruct = [cstruct]
521 cstruct = [cstruct]
522
522
523 cstruct = [int(a) for a in cstruct]
523 cstruct = [int(a) for a in cstruct]
524
524
525 valid_apps = list(apps.intersection(set(cstruct)))
525 valid_apps = list(apps.intersection(set(cstruct)))
526 if valid_apps:
526 if valid_apps:
527 return valid_apps
527 return valid_apps
528 return null
528 return null
529
529
530 def cstruct_children(self):
530 def cstruct_children(self):
531 return []
531 return []
532
532
533
533
534 @colander.deferred
534 @colander.deferred
535 def possible_applications_validator(node, kw):
535 def possible_applications_validator(node, kw):
536 possible_apps = [int(a) for a in kw['resources']]
536 possible_apps = [int(a) for a in kw['resources']]
537 return colander.All(colander.ContainsOnly(possible_apps),
537 return colander.All(colander.ContainsOnly(possible_apps),
538 colander.Length(1))
538 colander.Length(1))
539
539
540
540
541 @colander.deferred
541 @colander.deferred
542 def possible_applications(node, kw):
542 def possible_applications(node, kw):
543 return [int(a) for a in kw['resources']]
543 return [int(a) for a in kw['resources']]
544
544
545
545
546 @colander.deferred
546 @colander.deferred
547 def today_start(node, kw):
547 def today_start(node, kw):
548 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
548 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
549 minute=0,
549 minute=0,
550 hour=0)
550 hour=0)
551
551
552
552
553 @colander.deferred
553 @colander.deferred
554 def today_end(node, kw):
554 def today_end(node, kw):
555 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
555 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
556 minute=59, hour=23)
556 minute=59, hour=23)
557
557
558
558
559 @colander.deferred
559 @colander.deferred
560 def old_start(node, kw):
560 def old_start(node, kw):
561 t_delta = datetime.timedelta(days=90)
561 t_delta = datetime.timedelta(days=90)
562 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
562 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
563 minute=0,
563 minute=0,
564 hour=0) - t_delta
564 hour=0) - t_delta
565
565
566
566
567 @colander.deferred
567 @colander.deferred
568 def today_end(node, kw):
568 def today_end(node, kw):
569 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
569 return datetime.datetime.utcnow().replace(second=0, microsecond=0,
570 minute=59, hour=23)
570 minute=59, hour=23)
571
571
572
572
573 class PermissiveDate(colander.DateTime):
573 class PermissiveDate(colander.DateTime):
574 """ Returns null for incorrect date format - also removes tz info"""
574 """ Returns null for incorrect date format - also removes tz info"""
575
575
576 def deserialize(self, node, cstruct):
576 def deserialize(self, node, cstruct):
577 if not cstruct:
577 if not cstruct:
578 return null
578 return null
579
579
580 try:
580 try:
581 result = colander.iso8601.parse_date(
581 result = colander.iso8601.parse_date(
582 cstruct, default_timezone=self.default_tzinfo)
582 cstruct, default_timezone=self.default_tzinfo)
583 except colander.iso8601.ParseError:
583 except colander.iso8601.ParseError:
584 return null
584 return null
585 return result.replace(tzinfo=None)
585 return result.replace(tzinfo=None)
586
586
587
587
588 class LogSearchSchema(colander.MappingSchema):
588 class LogSearchSchema(colander.MappingSchema):
589 def schema_type(self, **kw):
589 def schema_type(self, **kw):
590 return colander.Mapping(unknown='preserve')
590 return colander.Mapping(unknown='preserve')
591
591
592 resource = colander.SchemaNode(StringToAppList(),
592 resource = colander.SchemaNode(StringToAppList(),
593 validator=possible_applications_validator,
593 validator=possible_applications_validator,
594 missing=possible_applications)
594 missing=possible_applications)
595
595
596 message = colander.SchemaNode(colander.Sequence(accept_scalar=True),
596 message = colander.SchemaNode(colander.Sequence(accept_scalar=True),
597 colander.SchemaNode(colander.String()),
597 colander.SchemaNode(colander.String()),
598 missing=None)
598 missing=None)
599 level = colander.SchemaNode(colander.Sequence(accept_scalar=True),
599 level = colander.SchemaNode(colander.Sequence(accept_scalar=True),
600 colander.SchemaNode(colander.String()),
600 colander.SchemaNode(colander.String()),
601 preparer=lowercase_preparer,
601 preparer=lowercase_preparer,
602 missing=None)
602 missing=None)
603 namespace = colander.SchemaNode(colander.Sequence(accept_scalar=True),
603 namespace = colander.SchemaNode(colander.Sequence(accept_scalar=True),
604 colander.SchemaNode(colander.String()),
604 colander.SchemaNode(colander.String()),
605 preparer=lowercase_preparer,
605 preparer=lowercase_preparer,
606 missing=None)
606 missing=None)
607 request_id = colander.SchemaNode(colander.Sequence(accept_scalar=True),
607 request_id = colander.SchemaNode(colander.Sequence(accept_scalar=True),
608 colander.SchemaNode(colander.String()),
608 colander.SchemaNode(colander.String()),
609 preparer=lowercase_preparer,
609 preparer=lowercase_preparer,
610 missing=None)
610 missing=None)
611 start_date = colander.SchemaNode(PermissiveDate(),
611 start_date = colander.SchemaNode(PermissiveDate(),
612 missing=None)
612 missing=None)
613 end_date = colander.SchemaNode(PermissiveDate(),
613 end_date = colander.SchemaNode(PermissiveDate(),
614 missing=None)
614 missing=None)
615 page = colander.SchemaNode(colander.Integer(),
615 page = colander.SchemaNode(colander.Integer(),
616 validator=colander.Range(min=1),
616 validator=colander.Range(min=1),
617 missing=1)
617 missing=1)
618
618
619
619
620 class ReportSearchSchema(colander.MappingSchema):
620 class ReportSearchSchema(colander.MappingSchema):
621 def schema_type(self, **kw):
621 def schema_type(self, **kw):
622 return colander.Mapping(unknown='preserve')
622 return colander.Mapping(unknown='preserve')
623
623
624 resource = colander.SchemaNode(StringToAppList(),
624 resource = colander.SchemaNode(StringToAppList(),
625 validator=possible_applications_validator,
625 validator=possible_applications_validator,
626 missing=possible_applications)
626 missing=possible_applications)
627 request_id = colander.SchemaNode(colander.Sequence(accept_scalar=True),
627 request_id = colander.SchemaNode(colander.Sequence(accept_scalar=True),
628 colander.SchemaNode(colander.String()),
628 colander.SchemaNode(colander.String()),
629 missing=None)
629 missing=None)
630 start_date = colander.SchemaNode(PermissiveDate(),
630 start_date = colander.SchemaNode(PermissiveDate(),
631 missing=None)
631 missing=None)
632 end_date = colander.SchemaNode(PermissiveDate(),
632 end_date = colander.SchemaNode(PermissiveDate(),
633 missing=None)
633 missing=None)
634 page = colander.SchemaNode(colander.Integer(),
634 page = colander.SchemaNode(colander.Integer(),
635 validator=colander.Range(min=1),
635 validator=colander.Range(min=1),
636 missing=1)
636 missing=1)
637
637
638 min_occurences = colander.SchemaNode(
638 min_occurences = colander.SchemaNode(
639 colander.Sequence(accept_scalar=True),
639 colander.Sequence(accept_scalar=True),
640 colander.SchemaNode(colander.Integer()),
640 colander.SchemaNode(colander.Integer()),
641 missing=None)
641 missing=None)
642
642
643 http_status = colander.SchemaNode(colander.Sequence(accept_scalar=True),
643 http_status = colander.SchemaNode(colander.Sequence(accept_scalar=True),
644 colander.SchemaNode(colander.Integer()),
644 colander.SchemaNode(colander.Integer()),
645 missing=None)
645 missing=None)
646 priority = colander.SchemaNode(colander.Sequence(accept_scalar=True),
646 priority = colander.SchemaNode(colander.Sequence(accept_scalar=True),
647 colander.SchemaNode(colander.Integer()),
647 colander.SchemaNode(colander.Integer()),
648 missing=None)
648 missing=None)
649 error = colander.SchemaNode(colander.Sequence(accept_scalar=True),
649 error = colander.SchemaNode(colander.Sequence(accept_scalar=True),
650 colander.SchemaNode(colander.String()),
650 colander.SchemaNode(colander.String()),
651 missing=None)
651 missing=None)
652 url_path = colander.SchemaNode(colander.Sequence(accept_scalar=True),
652 url_path = colander.SchemaNode(colander.Sequence(accept_scalar=True),
653 colander.SchemaNode(colander.String()),
653 colander.SchemaNode(colander.String()),
654 missing=None)
654 missing=None)
655 url_domain = colander.SchemaNode(colander.Sequence(accept_scalar=True),
655 url_domain = colander.SchemaNode(colander.Sequence(accept_scalar=True),
656 colander.SchemaNode(colander.String()),
656 colander.SchemaNode(colander.String()),
657 missing=None)
657 missing=None)
658 report_status = colander.SchemaNode(colander.Sequence(accept_scalar=True),
658 report_status = colander.SchemaNode(colander.Sequence(accept_scalar=True),
659 colander.SchemaNode(colander.String()),
659 colander.SchemaNode(colander.String()),
660 missing=None)
660 missing=None)
661 min_duration = colander.SchemaNode(colander.Sequence(accept_scalar=True),
661 min_duration = colander.SchemaNode(colander.Sequence(accept_scalar=True),
662 colander.SchemaNode(colander.Float()),
662 colander.SchemaNode(colander.Float()),
663 missing=None)
663 missing=None)
664 max_duration = colander.SchemaNode(colander.Sequence(accept_scalar=True),
664 max_duration = colander.SchemaNode(colander.Sequence(accept_scalar=True),
665 colander.SchemaNode(colander.Float()),
665 colander.SchemaNode(colander.Float()),
666 missing=None)
666 missing=None)
667
667
668
668
669 class TagSchema(colander.MappingSchema):
669 class TagSchema(colander.MappingSchema):
670 """
670 """
671 Used in log search
671 Used in log search
672 """
672 """
673 name = colander.SchemaNode(colander.String(),
673 name = colander.SchemaNode(colander.String(),
674 validator=colander.Length(1, 32))
674 validator=colander.Length(1, 32))
675 value = colander.SchemaNode(colander.Sequence(accept_scalar=True),
675 value = colander.SchemaNode(colander.Sequence(accept_scalar=True),
676 colander.SchemaNode(colander.String(),
676 colander.SchemaNode(colander.String(),
677 validator=colander.Length(
677 validator=colander.Length(
678 1, 128)),
678 1, 128)),
679 missing=None)
679 missing=None)
680 op = colander.SchemaNode(colander.String(),
680 op = colander.SchemaNode(colander.String(),
681 validator=colander.Length(1, 128),
681 validator=colander.Length(1, 128),
682 missing=None)
682 missing=None)
683
683
684
684
685 class TagListSchema(colander.SequenceSchema):
685 class TagListSchema(colander.SequenceSchema):
686 tag = TagSchema()
686 tag = TagSchema()
687
687
688
688
689 class RuleFieldType(object):
689 class RuleFieldType(object):
690 """ Validator which succeeds if the value passed to it is one of
690 """ Validator which succeeds if the value passed to it is one of
691 a fixed set of values """
691 a fixed set of values """
692
692
693 def __init__(self, cast_to):
693 def __init__(self, cast_to):
694 self.cast_to = cast_to
694 self.cast_to = cast_to
695
695
696 def __call__(self, node, value):
696 def __call__(self, node, value):
697 try:
697 try:
698 if self.cast_to == 'int':
698 if self.cast_to == 'int':
699 int(value)
699 int(value)
700 elif self.cast_to == 'float':
700 elif self.cast_to == 'float':
701 float(value)
701 float(value)
702 elif self.cast_to == 'unicode':
702 elif self.cast_to == 'unicode':
703 str(value)
703 str(value)
704 except:
704 except:
705 raise colander.Invalid(node,
705 raise colander.Invalid(node,
706 "Can't cast {} to {}".format(
706 "Can't cast {} to {}".format(
707 value, self.cast_to))
707 value, self.cast_to))
708
708
709
709
710 def build_rule_schema(ruleset, check_matrix):
710 def build_rule_schema(ruleset, check_matrix):
711 """
711 """
712 Accepts ruleset and a map of fields/possible operations and builds
712 Accepts ruleset and a map of fields/possible operations and builds
713 validation class
713 validation class
714 """
714 """
715
715
716 schema = colander.SchemaNode(colander.Mapping())
716 schema = colander.SchemaNode(colander.Mapping())
717 schema.add(colander.SchemaNode(colander.String(), name='field'))
717 schema.add(colander.SchemaNode(colander.String(), name='field'))
718
718
719 if ruleset['field'] in ['__AND__', '__OR__', '__NOT__']:
719 if ruleset['field'] in ['__AND__', '__OR__', '__NOT__']:
720 subrules = colander.SchemaNode(colander.Tuple(), name='rules')
720 subrules = colander.SchemaNode(colander.Tuple(), name='rules')
721 for rule in ruleset['rules']:
721 for rule in ruleset['rules']:
722 subrules.add(build_rule_schema(rule, check_matrix))
722 subrules.add(build_rule_schema(rule, check_matrix))
723 schema.add(subrules)
723 schema.add(subrules)
724 else:
724 else:
725 op_choices = check_matrix[ruleset['field']]['ops']
725 op_choices = check_matrix[ruleset['field']]['ops']
726 cast_to = check_matrix[ruleset['field']]['type']
726 cast_to = check_matrix[ruleset['field']]['type']
727 schema.add(colander.SchemaNode(colander.String(),
727 schema.add(colander.SchemaNode(colander.String(),
728 validator=colander.OneOf(op_choices),
728 validator=colander.OneOf(op_choices),
729 name='op'))
729 name='op'))
730
730
731 schema.add(colander.SchemaNode(colander.String(),
731 schema.add(colander.SchemaNode(colander.String(),
732 name='value',
732 name='value',
733 validator=RuleFieldType(cast_to)))
733 validator=RuleFieldType(cast_to)))
734 return schema
734 return schema
735
735
736
736
737 class ConfigTypeSchema(colander.MappingSchema):
737 class ConfigTypeSchema(colander.MappingSchema):
738 type = colander.SchemaNode(colander.String(), missing=None)
738 type = colander.SchemaNode(colander.String(), missing=None)
739 config = colander.SchemaNode(UnknownType(), missing=None)
739 config = colander.SchemaNode(UnknownType(), missing=None)
740
740
741
741
742 class MappingListSchema(colander.SequenceSchema):
742 class MappingListSchema(colander.SequenceSchema):
743 config = colander.SchemaNode(UnknownType())
743 config = colander.SchemaNode(UnknownType())
General Comments 0
You need to be logged in to leave comments. Login now