1
2
3
4
5
6
7
8
9
10
11
12
13 import types
14 import logging
15 from Products.ZenUtils.jsonutils import json
16
17 log = logging.getLogger('zen.WhereClause')
18
19 new_name_mapping = {
20 'eventClass':'evt.event_class',
21 'summary':'evt.summary',
22 'message':'evt.message',
23 'eventKey':'evt.event_key',
24 'agent':'evt.agent',
25 'manager':'evt.monitor',
26 'severity':'evt.severity',
27 'eventState':'evt.status',
28 'count':'evt.count',
29 'prodState':'dev.production_state',
30 'device':'elem.name',
31 'devicePriority':'dev.priority',
32 'component':'sub_elem.name',
33 'eventClassKey':'evt.event_class_key',
34 'priority':'evt.syslog_priority',
35 'facility':'evt.syslog_facility',
36 'ntevid':'evt.nt_event_code',
37 'ownerId':'evt.current_user_name',
38 'deviceClass':'dev.device_class',
39 'systems':'dev.systems',
40 'deviceGroups':'dev.groups',
41 'ipAddress':'dev.ip_address',
42 'location':'dev.location',
43 }
52
54
55 if isinstance(val, basestring):
56 return '"%s"' % val.replace('"', '\"')
57 else:
58 return val
59
65
71
77
83
90
91
92 negativeModes = (
93 '!',
94 '!~',
95 '!^',
96 )
99
100 return "'%s'" % "''".join(s.split("'"))
101
102 -class Error(Exception): pass
103
105 "Base class for converting to/from javascript"
106 type = 'unknown'
107
110
112 return '%s:{type:"%s",label:"%s"}' % (name, self.type, self.label)
113
127
128 -class Text(WhereJavaScript):
129 "Convert to/from javascript for text entries"
130 type = 'text'
131
132 - def toJS(self, mode, value):
133 if mode == 'like':
134 if value.startswith('%') and not value.endswith('%'):
135 return '$', [value[1:]]
136 elif not value.startswith('%') and value.endswith('%'):
137 return '^', [value[:-1]]
138 elif value.startswith('%') and value.endswith('%'):
139 return '~', [value[1:-1]]
140 if mode == 'not like':
141 return '!~', [value[1:-1]]
142 if mode == '=':
143 return '', [value]
144 if mode == '!=':
145 return '!', [value]
146
147 - def buildPython(self, name, mode, value):
148 if mode == 'like':
149 if value.startswith('%') and not value.endswith('%'):
150 return getEndsWith(name, value[1:])
151 elif not value.startswith('%') and value.endswith('%'):
152 return getStartsWith(name, value[:-1])
153 elif value.startswith('%') and value.endswith('%'):
154 return getIn(name, value[1:-1])
155 if mode == 'not like':
156 return getNotIn(name, value[1:-1])
157 if mode == '=':
158 return getEquality(name, '==', value)
159 if mode == '!=':
160 return getEquality(name, '!=', value)
161
162 - def buildClause1(self, name, v, mode):
163 if mode == '~':
164 return "%s like %s" % (name, q('%' + v + '%'))
165 if mode == '^':
166 return "%s like %s" % (name, q(v + '%'))
167 if mode == '$':
168 return "%s like %s" % (name, q('%' + v))
169 if mode == '!~':
170 return "%s not like %s" % (name, q('%' + v + '%'))
171 if mode == '':
172 return "%s = %s" % (name, q(v))
173 if mode == '!':
174 return "%s != %s" % (name, q(v))
175
176
177 -class Select(WhereJavaScript):
178 "Convert to/from javascript and where clause element for select entries"
179 type = 'select'
180
187
189 return dict(self.options).get(value, 'Unknown')
190
192 return dict([(v, l) for l, v in self.options]).get(value, -1)
193
194 - def toJS(self, operator, value):
205
211
213 return '%s:{type:"%s",label:"%s", options:%r}' % (
214 name, self.type, self.label, [s[1] for s in self.options])
215
224
253
254
255 -class Compare(WhereJavaScript):
256 "Convert to/from javascript and where clause elements for numeric comparisons"
257 type = 'compare'
258
259 - def toJS(self, operator, value):
260 return operator, [value]
261
267
270
273 - def toJS(self, operator, value):
274 if operator == 'like':
275 return ['', [value[2:-1]]]
276 if operator == 'not like':
277 return ['!', [value[2:-1]]]
278
281
283 if mode == '':
284 return "%s like %s" % (name, q('%|' + v + '%'))
285 else:
286 return "%s not like %s" % (name, q('%|' + v + '%'))
287
289 type = 'evtClass'
290
291 - def toJS(self, operator, value):
292 value = value.rstrip('%')
293 if operator == '=':
294 return ['', [value]]
295 if operator == '!=':
296 return ['!', [value]]
297 if operator == 'like':
298 return ['^', [value]]
299 if operator == 'not like':
300 return ['!^', [value]]
301
320
322 if mode == '':
323 return "%s = %s" % (name, q(v))
324 elif mode == '^':
325 return "%s like %s" % (name, q(v + '%'))
326 elif mode == '!^':
327 return "%s not like %s" % (name, q(v + '%'))
328 else:
329 return "%s != %s" % (name, q(v))
330
334 "Convert to/from javascript and where clause elements for enumerated types"
335 type='cselect'
336
337 - def toJS(self, operator, value):
339
345
348
349 _Definitions = r'''
350 def u(s):
351 # turn string "'fo''o'" -> "fo'o"
352 c = s[0]
353 s = c.join(s.split(c+c))
354 return s[1:-1]
355
356 '''
357
358 _ParseSpec = r'''
359 parser WhereClause:
360 ignore: "[ \r\t\n]+"
361 token END: "$"
362 token NUM: "[0-9]+"
363 token VAR: "[a-zA-Z0-9_]+"
364 token BIN: ">=|<=|==|=|<|>|!=|<>"
365 token STR: r'"([^\\"]+|\\.)*"'
366 token STR2: r"'([^\\']+'{2,}|[^\\']+|\\.)*'"
367
368 rule goal: andexp ? END {{ return locals().get('andexp', None) }}
369
370 rule andexp: orexp {{ e = orexp }}
371 ( "and" orexp {{ e = ('and', e, orexp) }}
372 )* {{ return e }}
373
374 rule orexp: binary {{ e = binary }}
375 ( "or" binary {{ e = ('or', e, binary) }}
376 )* {{ return e }}
377
378 rule binary: term {{ e = term }}
379 ( BIN term {{ e = (BIN, e, term) }}
380 | 'like' term {{ e = ('like', e, term) }}
381 | 'not' 'like' term
382 {{ e = ('not like', e, term) }}
383 )* {{ return e }}
384
385 rule term: NUM {{ return int(NUM) }}
386 | VAR {{ return VAR }}
387 | STR {{ return u(STR) }}
388 | STR2 {{ return u(STR2) }}
389 | "\\(" andexp "\\)" {{ return andexp }}
390 '''
394 from yapps import grammar, yappsrt
395 from StringIO import StringIO
396
397 scanner = grammar.ParserDescriptionScanner(spec)
398 parser = grammar.ParserDescription(scanner)
399 parser = yappsrt.wrap_error_reporter(parser, 'Parser')
400 parser.preparser = _Definitions
401 parser.output = StringIO()
402 parser.generate_output()
403 exec parser.output.getvalue() in self.__dict__
404
405 where = _Parser(_ParseSpec)
409
410 lmeta = dict([(n.lower(), n) for n in meta.keys()])
411 tree = where.parse('goal', clause)
412
413 def recurse(root, result):
414 if type(root) == types.TupleType:
415 n = len(root)
416 if n == 1:
417 recurse(root[0], result)
418 op, name, value = root
419 if op in ('and', 'or'):
420 recurse(root[1], result)
421 recurse(root[2], result)
422 else:
423 name = lmeta.get(name.lower(), None)
424 if name is not None:
425 op, value = meta[name].toJS(op, value)
426 result.append([name, op, value])
427
428 result = []
429 recurse(tree, result)
430 result.sort()
431 return result
432
434 """
435 Collapses adjacent and/ors into one statement.
436 and(and(a, b), c) becomes and(a, b, c).
437 """
438 def _collapse(term, curopr=None, oprstack=None):
439 op = term[0]
440 if op == curopr:
441 _collapse(term[1], op, oprstack)
442 _collapse(term[2], op, oprstack)
443 elif op in ('and', 'or'):
444 s1, s2 = [], []
445 _collapse(term[1], op, s1)
446 _collapse(term[2], op, s2)
447 combined = [op] + s1 + s2
448 oprstack.append(combined)
449 else:
450 oprstack.append(term)
451
452 exprs = []
453 _collapse(tree, None, exprs)
454 return exprs
455
457 """
458 Exception thrown when a where clause fails conversion to a Python expression.
459 """
460 pass
461
463
464 lmeta = dict([(n.lower(), n) for n in meta.keys()])
465 tree = where.parse('goal', clause)
466
467 def recurse(root, result):
468 if isinstance(root, (list, tuple)):
469 op = root[0]
470 if op in ('and', 'or'):
471 all_sub_results = []
472 for clause in root[1:]:
473 sub_result = []
474 recurse(clause, sub_result)
475 all_sub_results.extend(sub_result)
476 result.append('(' + (' %s ' % op).join(all_sub_results) + ')')
477 else:
478 name, value = root[1], root[2]
479 orig_name = name.lower()
480 name = lmeta.get(orig_name, None)
481 if name is not None:
482
483 if orig_name == 'ntevid':
484 if op not in ('=','!='):
485 raise PythonConversionException('Unable to migrate ntevid starts/ends-with clause')
486 try:
487 value = int(value)
488 except ValueError:
489 raise PythonConversionException('Failed to convert ntevid to integer')
490
491 python_statement = meta[name].buildPython(name, op, value)
492 result.append('(%s)' % python_statement)
493
494 result = []
495 tree = collapse(tree)
496 recurse(tree[0], result)
497 rule = result[0]
498
499 if tree[0][0] in ('and', 'or'):
500 rule = rule[1:-1]
501 return rule
502
516
517
518 if __name__ == '__main__':
519 meta = {}
520 toJavaScript(meta, 'severity = 3 or severity = 4')
521 toJavaScript(meta, "severity >= 4 and eventState = 0 and prodState = 1000")
522 toJavaScript(meta, "severity >= 2 and eventState = 0 and prodState = 1000")
523 print toJavaScript(meta, '(prodState = 1000) and (eventState = 0 or eventState = 1) and (severity >= 3)')
524 print fromFormVariables(meta,
525 dict(severity='Info',
526 severity_mode='>',
527 eventState='New',
528 eventState_mode='=',
529 prodState='Production',
530 prodState_mode='='))
531