Package Products :: Package ZenEvents :: Module WhereClause
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenEvents.WhereClause

  1  ############################################################################## 
  2  #  
  3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved. 
  4  #  
  5  # This content is made available according to terms specified in 
  6  # License.zenoss under the directory where your Zenoss product is installed. 
  7  #  
  8  ############################################################################## 
  9   
 10   
 11  import types 
 12  import logging 
 13  from Products.ZenUtils.jsonutils import json 
 14  from zenoss.protocols.protobufs import zep_pb2 as eventConstants 
 15   
 16  OLD_SYSLOG_MAPPING = { 
 17      0: eventConstants.SYSLOG_PRIORITY_DEBUG, 
 18      1: eventConstants.SYSLOG_PRIORITY_INFO, 
 19      2: eventConstants.SYSLOG_PRIORITY_NOTICE, 
 20      3: eventConstants.SYSLOG_PRIORITY_WARNING, 
 21      4: eventConstants.SYSLOG_PRIORITY_ERR, 
 22      5: eventConstants.SYSLOG_PRIORITY_CRIT, 
 23      6: eventConstants.SYSLOG_PRIORITY_ALERT, 
 24      7: eventConstants.SYSLOG_PRIORITY_EMERG, 
 25  } 
 26   
 27  log = logging.getLogger('zen.WhereClause') 
 28   
 29  new_name_mapping = { 
 30      'eventClass':'evt.event_class', 
 31      'summary':'evt.summary', 
 32      'message':'evt.message', 
 33      'eventKey':'evt.event_key', 
 34      'agent':'evt.agent', 
 35      'manager':'evt.monitor', 
 36      'severity':'evt.severity', 
 37      'eventState':'evt.status', 
 38      'count':'evt.count', 
 39      'prodState':'dev.production_state', 
 40      'device':'elem.name', 
 41      'devicePriority':'dev.priority', 
 42      'component':'sub_elem.name', 
 43      'eventClassKey':'evt.event_class_key', 
 44      'priority':'evt.syslog_priority', 
 45      'facility':'evt.syslog_facility', 
 46      'ntevid':'evt.nt_event_code', 
 47      'ownerId':'evt.current_user_name', 
 48      'deviceClass':'dev.device_class', 
 49      'systems':'dev.systems', 
 50      'deviceGroups':'dev.groups', 
 51      'ipAddress':'dev.ip_address', 
 52      'location':'dev.location', 
 53  } 
54 55 56 -def getName(str):
57 # lookup the old to new map 58 if str in new_name_mapping: 59 return new_name_mapping[str] 60 # don't know a better way to error yet. 61 return str
62
63 -def getValue(val):
64 # escape things 65 if isinstance(val, basestring): 66 return '"%s"' % val.replace('"', '\"') 67 else: 68 return val
69
70 -def getEndsWith(name, value):
71 return "{name}.endswith({value})".format( 72 name=getName(name), 73 value=getValue(value) 74 )
75
76 -def getStartsWith(name, value):
77 return "{name}.startswith({value})".format( 78 name=getName(name), 79 value=getValue(value) 80 )
81
82 -def getIn(name, value):
83 return "{value} in {name}".format( 84 name=getName(name), 85 value=getValue(value) 86 )
87
88 -def getNotIn(name, value):
89 return "{value} not in {name}".format( 90 name=getName(name), 91 value=getValue(value) 92 )
93
94 -def getEquality(name, op, value):
95 return "{name} {op} {value}".format( 96 name=getName(name), 97 op=op, 98 value=getValue(value) 99 )
100 101 102 negativeModes = ( 103 '!', # is not 104 '!~', # does not contain 105 '!^', # does not begin with 106 )
107 108 -def q(s):
109 # turn string "fo'o" -> "'fo''o'" 110 return "'%s'" % "''".join(s.split("'"))
111
112 -class Error(Exception): pass
113
114 -class WhereJavaScript:
115 "Base class for converting to/from javascript" 116 type = 'unknown' 117
118 - def __init__(self, label):
119 self.label = label
120
121 - def genProperties(self, name):
122 return '%s:{type:"%s",label:"%s"}' % (name, self.type, self.label)
123
124 - def buildClause(self, name, value, mode):
125 foundNegativeMatch = False 126 result = [] 127 for v in value: 128 if not v: return None 129 if mode in negativeModes: foundNegativeMatch = True 130 result.append(self.buildClause1(name, v, mode)) 131 if not result: 132 return None 133 if foundNegativeMatch: 134 return ' and '.join(result) 135 else: 136 return ' or '.join(result)
137
138 -class Text(WhereJavaScript):
139 "Convert to/from javascript for text entries" 140 type = 'text' 141
142 - def toJS(self, mode, value):
143 if mode == 'like': 144 if value.startswith('%') and not value.endswith('%'): 145 return '$', [value[1:]] 146 elif not value.startswith('%') and value.endswith('%'): 147 return '^', [value[:-1]] 148 elif value.startswith('%') and value.endswith('%'): 149 return '~', [value[1:-1]] 150 if mode == 'not like': 151 return '!~', [value[1:-1]] 152 if mode == '=': 153 return '', [value] 154 if mode == '!=': 155 return '!', [value]
156
157 - def buildPython(self, name, mode, value):
158 if mode == 'like': 159 if value.startswith('%') and not value.endswith('%'): 160 return getEndsWith(name, value[1:]) 161 elif not value.startswith('%') and value.endswith('%'): 162 return getStartsWith(name, value[:-1]) 163 elif value.startswith('%') and value.endswith('%'): 164 return getIn(name, value[1:-1]) 165 if mode == 'not like': 166 return getNotIn(name, value[1:-1]) 167 if mode == '=': 168 return getEquality(name, '==', value) 169 if mode == '!=': 170 return getEquality(name, '!=', value)
171
172 - def buildClause1(self, name, v, mode):
173 if mode == '~': 174 return "%s like %s" % (name, q('%' + v + '%')) 175 if mode == '^': 176 return "%s like %s" % (name, q(v + '%')) 177 if mode == '$': 178 return "%s like %s" % (name, q('%' + v)) 179 if mode == '!~': 180 return "%s not like %s" % (name, q('%' + v + '%')) 181 if mode == '': 182 return "%s = %s" % (name, q(v)) 183 if mode == '!': 184 return "%s != %s" % (name, q(v))
185
186 187 -class Select(WhereJavaScript):
188 "Convert to/from javascript and where clause element for select entries" 189 type = 'select' 190
191 - def __init__(self, label, options):
192 WhereJavaScript.__init__(self, label) 193 if options: 194 if type(options[0]) != type(()): 195 options = zip(range(len(options)), options) 196 self.options = options
197
198 - def labelFromValue(self, value):
199 return dict(self.options).get(value, 'Unknown')
200
201 - def valueFromLabel(self, value):
202 return dict([(v, l) for l, v in self.options]).get(value, -1)
203
204 - def toJS(self, operator, value):
205 if operator == '=': 206 return ('', [self.labelFromValue(value)]) 207 if operator == '!=': 208 return ('!', [self.labelFromValue(value)]) 209 result = [] 210 if operator in ('<', '>', '<=', '>='): 211 for i, name in self.options: 212 if eval('%d %s %d' % (i, operator, value)): 213 result.append(name) 214 return ('', result)
215
216 - def buildPython(self, name, operator, value):
217 if operator == '=': 218 return getEquality(name, '==', value) 219 else: 220 return getEquality(name, operator, value)
221
222 - def genProperties(self, name):
223 return '%s:{type:"%s",label:"%s", options:%r}' % ( 224 name, self.type, self.label, [s[1] for s in self.options])
225
226 - def buildClause1(self, name, v, mode):
227 v = self.valueFromLabel(v) 228 if type(v) in types.StringTypes: 229 v = q(v) 230 if mode == '': 231 return "%s = %s" % (name, v) 232 else: 233 return "%s != %s" % (name, v)
234
235 - def buildMultiValuePython(self, name, mode, value):
236 237 value = value.strip('%|') 238 239 # if mode == 'like': 240 # if value.startswith('%') and not value.endswith('%'): 241 # return 'any(d.endswith({value}) for d in {name})'.format(value=getValue(bare_value), name=getName(name)) 242 # elif not value.startswith('%') and value.endswith('%'): 243 # return 'any(d.startswith({value}) for d in {name})'.format(value=getValue(bare_value), name=getName(name)) 244 # elif value.startswith('%') and value.endswith('%'): 245 # return 'any({value} in d for d in {name})'.format(value=getValue(bare_value), name=getName(name)) 246 # else: 247 # return getIn(name, bare_value) 248 # 249 # if mode == 'not like': 250 # if value.startswith('%') and not value.endswith('%'): 251 # return 'not any(d.endswith({value}) for d in {name})'.format(value=getValue(bare_value), name=getName(name)) 252 # elif not value.startswith('%') and value.endswith('%'): 253 # return 'not any(d.startswith({value}) for d in {name})'.format(value=getValue(bare_value), name=getName(name)) 254 # elif value.startswith('%') and value.endswith('%'): 255 # return 'not any({value} in d for d in {name})'.format(value=getValue(bare_value), name=getName(name)) 256 # else: 257 # return getNotIn(name, bare_value) 258 259 if mode == 'like': 260 return getIn(name, value) 261 if mode == 'not like': 262 return getNotIn(name, value)
263
264 265 -class Compare(WhereJavaScript):
266 "Convert to/from javascript and where clause elements for numeric comparisons" 267 type = 'compare' 268
269 - def toJS(self, operator, value):
270 return operator, [value]
271
272 - def buildPython(self, name, operator, value):
273 if operator == '=': 274 return getEquality(name, '==', value) 275 else: 276 return getEquality(name, operator, value)
277
278 - def buildClause1(self, name, v, mode):
279 return "%s %s %s" % (name, mode, v)
280
281 282 -class DeviceGroup(Select):
283 - def toJS(self, operator, value):
284 if operator == 'like': 285 return ['', [value[2:-1]]] 286 if operator == 'not like': 287 return ['!', [value[2:-1]]]
288
289 - def buildPython(self, name, operator, value):
290 return self.buildMultiValuePython(name, operator, value)
291
292 - def buildClause1(self, name, v, mode):
293 if mode == '': 294 return "%s like %s" % (name, q('%|' + v + '%')) 295 else: 296 return "%s not like %s" % (name, q('%|' + v + '%'))
297
298 -class EventClass(Select):
299 type = 'evtClass' 300
301 - def toJS(self, operator, value):
302 value = value.rstrip('%') 303 if operator == '=': 304 return ['', [value]] 305 if operator == '!=': 306 return ['!', [value]] 307 if operator == 'like': 308 return ['^', [value]] 309 if operator == 'not like': 310 return ['!^', [value]]
311
312 - def buildPython(self, name, operator, value):
313 314 mode = operator 315 if mode == 'like': 316 if value.startswith('%') and not value.endswith('%'): 317 return getEndsWith(name, value[1:]) 318 elif not value.startswith('%') and value.endswith('%'): 319 return getStartsWith(name, value[:-1]) 320 elif value.startswith('%') and value.endswith('%'): 321 return getIn(name, value[1:-1]) 322 else: 323 return 324 if mode == 'not like': 325 return getNotIn(name, value[1:-1]) 326 if mode == '=': 327 return getEquality(name, '==', value) 328 if mode == '!=': 329 return getEquality(name, '!=', value)
330
331 - def buildClause1(self, name, v, mode):
332 if mode == '': 333 return "%s = %s" % (name, q(v)) 334 elif mode == '^': 335 return "%s like %s" % (name, q(v + '%')) 336 elif mode == '!^': 337 return "%s not like %s" % (name, q(v + '%')) 338 else: 339 return "%s != %s" % (name, q(v))
340
341 342 343 -class Enumerated(Select):
344 "Convert to/from javascript and where clause elements for enumerated types" 345 type='cselect' 346
347 - def toJS(self, operator, value):
348 return operator, [self.labelFromValue(value)]
349
350 - def buildPython(self, name, operator, value):
351 if operator == '=': 352 return getEquality(name, '==', value) 353 else: 354 return getEquality(name, operator, value)
355
356 - def buildClause1(self, name, v, mode):
357 return "%s %s %s" % (name, mode, self.valueFromLabel(v))
358 359 _Definitions = r''' 360 def u(s): 361 # turn string "'fo''o'" -> "fo'o" 362 c = s[0] 363 s = c.join(s.split(c+c)) 364 return s[1:-1] 365 366 ''' 367 368 _ParseSpec = r''' 369 parser WhereClause: 370 ignore: "[ \r\t\n]+" 371 token END: "$" 372 token NUM: "[0-9]+" 373 token VAR: "[a-zA-Z0-9_]+" 374 token BIN: ">=|<=|==|=|<|>|!=|<>" 375 token STR: r'"([^\\"]+|\\.)*"' 376 token STR2: r"'([^\\']+'{2,}|[^\\']+|\\.)*'" 377 378 rule goal: andexp ? END {{ return locals().get('andexp', None) }} 379 380 rule andexp: orexp {{ e = orexp }} 381 ( "and" orexp {{ e = ('and', e, orexp) }} 382 )* {{ return e }} 383 384 rule orexp: binary {{ e = binary }} 385 ( "or" binary {{ e = ('or', e, binary) }} 386 )* {{ return e }} 387 388 rule binary: term {{ e = term }} 389 ( BIN term {{ e = (BIN, e, term) }} 390 | 'like' term {{ e = ('like', e, term) }} 391 | 'not' 'like' term 392 {{ e = ('not like', e, term) }} 393 )* {{ return e }} 394 395 rule term: NUM {{ return int(NUM) }} 396 | VAR {{ return VAR }} 397 | STR {{ return u(STR) }} 398 | STR2 {{ return u(STR2) }} 399 | "\\(" andexp "\\)" {{ return andexp }} 400 '''
401 402 -class _Parser:
403 - def __init__(self, spec):
404 from yapps import grammar, yappsrt 405 from StringIO import StringIO 406 407 scanner = grammar.ParserDescriptionScanner(spec) 408 parser = grammar.ParserDescription(scanner) 409 parser = yappsrt.wrap_error_reporter(parser, 'Parser') 410 parser.preparser = _Definitions 411 parser.output = StringIO() 412 parser.generate_output() 413 exec parser.output.getvalue() in self.__dict__
414 415 where = _Parser(_ParseSpec)
416 417 @json 418 -def toJavaScript(meta, clause):
419 # sql is case insensitive, map column names to lower-case versions 420 lmeta = dict([(n.lower(), n) for n in meta.keys()]) 421 tree = where.parse('goal', clause) 422 423 def recurse(root, result): 424 if type(root) == types.TupleType: 425 n = len(root) 426 if n == 1: 427 recurse(root[0], result) 428 op, name, value = root 429 if op in ('and', 'or'): 430 recurse(root[1], result) 431 recurse(root[2], result) 432 else: 433 name = lmeta.get(name.lower(), None) 434 if name is not None: 435 op, value = meta[name].toJS(op, value) 436 result.append([name, op, value])
437 438 result = [] 439 recurse(tree, result) 440 result.sort() 441 return result 442
443 -def collapse(tree):
444 """ 445 Collapses adjacent and/ors into one statement. 446 and(and(a, b), c) becomes and(a, b, c). 447 """ 448 def _collapse(term, curopr=None, oprstack=None): 449 op = term[0] 450 if op == curopr: 451 _collapse(term[1], op, oprstack) 452 _collapse(term[2], op, oprstack) 453 elif op in ('and', 'or'): 454 s1, s2 = [], [] 455 _collapse(term[1], op, s1) 456 _collapse(term[2], op, s2) 457 combined = [op] + s1 + s2 458 oprstack.append(combined) 459 else: 460 oprstack.append(term)
461 462 exprs = [] 463 _collapse(tree, None, exprs) 464 return exprs 465
466 -class PythonConversionException(Exception):
467 """ 468 Exception thrown when a where clause fails conversion to a Python expression. 469 """ 470 pass
471
472 -def toPython(meta, clause):
473 # sql is case insensitive, map column names to lower-case versions 474 lmeta = dict([(n.lower(), n) for n in meta.keys()]) 475 tree = where.parse('goal', clause) 476 477 def recurse(root, result): 478 if isinstance(root, (list, tuple)): 479 op = root[0] 480 if op in ('and', 'or'): 481 all_sub_results = [] 482 for clause in root[1:]: 483 sub_result = [] 484 recurse(clause, sub_result) 485 all_sub_results.extend(sub_result) 486 result.append('(' + (' %s ' % op).join(all_sub_results) + ')') 487 else: 488 name, value = root[1], root[2] 489 orig_name = name.lower() 490 name = lmeta.get(orig_name, None) 491 if name is not None: 492 # Special case - ntevid must be convertable to integer and operator must be == or != 493 if orig_name == 'ntevid': 494 if op not in ('=','!='): 495 raise PythonConversionException('Unable to migrate ntevid starts/ends-with clause') 496 try: 497 value = int(value) 498 except ValueError: 499 raise PythonConversionException('Failed to convert ntevid to integer') 500 if orig_name == 'priority': 501 value = OLD_SYSLOG_MAPPING[value] 502 503 python_statement = meta[name].buildPython(name, op, value) 504 result.append('(%s)' % python_statement)
505 506 result = [] 507 tree = collapse(tree) 508 recurse(tree[0], result) 509 rule = result[0] 510 # And's and Or's already add parentheses - we need to strip the outermost parens 511 if tree[0][0] in ('and', 'or'): 512 rule = rule[1:-1] 513 return rule 514
515 516 -def fromFormVariables(meta, form):
517 result = [] 518 for n, attrType in meta.items(): 519 if form.has_key(n): 520 value = form[n] 521 if type(value) != type([]): 522 value = [value] 523 mode = form.get(n + "_mode", None) 524 clause = attrType.buildClause(n, value, mode) 525 if clause: 526 result.append('(%s)' % clause) 527 return ' and '.join(result)
528 529 530 if __name__ == '__main__': 531 meta = {} 532 toJavaScript(meta, 'severity = 3 or severity = 4') 533 toJavaScript(meta, "severity >= 4 and eventState = 0 and prodState = 1000") 534 toJavaScript(meta, "severity >= 2 and eventState = 0 and prodState = 1000") 535 print toJavaScript(meta, '(prodState = 1000) and (eventState = 0 or eventState = 1) and (severity >= 3)') 536 print fromFormVariables(meta, 537 dict(severity='Info', 538 severity_mode='>', 539 eventState='New', 540 eventState_mode='=', 541 prodState='Production', 542 prodState_mode='=')) 543