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