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

# Source Code for Module Products.ZenEvents.WhereClause

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13  import types
     14  from Products.ZenUtils.jsonutils import json
     15
     16  negativeModes = (
     17      '!',    # is not
     18      '!~',   # does not contain
     19      '!^',   # does not begin with
     20      )
     21
     22 -def q(s):
     23      # turn string "fo'o" -> "'fo''o'"
     24      return "'%s'" % "''".join(s.split("'"))
     25
     26 -class Error(Exception): pass
     27
     28 -class WhereJavaScript:
     29      "Base class for converting to/from javascript"
     30      type = 'unknown'
     31 -    def __init__(self, label):
     32          self.label = label
     33 -    def genProperties(self, name):
     34          return '%s:{type:"%s",label:"%s"}' % (name, self.type, self.label)
     35 -    def buildClause(self, name, value, mode):
     36          foundNegativeMatch = False
     37          result = []
     38          for v in value:
     39              if not v: return None
     40              if mode in negativeModes: foundNegativeMatch = True
     41              result.append(self.buildClause1(name, v, mode))
     42          if not result:
     43              return None
     44          if foundNegativeMatch:
     45              return ' and '.join(result)
     46          else:
     47              return ' or '.join(result)
     48
     49 -class Text(WhereJavaScript):
     50      "Convert to/from javascript for text entries"
     51      type = 'text'
     52 -    def toJS(self, mode, value):
     53          if mode == 'like':
     54              if value.startswith('%') and not value.endswith('%'):
     55                  return '$', [value[1:]]
     56              elif not value.startswith('%') and value.endswith('%'):
     57                  return '^', [value[:-1]]
     58              elif value.startswith('%') and value.endswith('%'):
     59                  return '~', [value[1:-1]]
     60          if mode == 'not like':
     61              return '!~', [value[1:-1]]
     62          if mode == '=':
     63              return '', [value]
     64          if mode == '!=':
     65              return '!', [value]
     66 -    def buildClause1(self, name, v, mode):
     67          if mode == '~':
     68              return "%s like %s" % (name, q('%' + v + '%'))
     69          if mode == '^':
     70              return "%s like %s" % (name, q(v + '%'))
     71          if mode == '$':
     72              return "%s like %s" % (name, q('%' + v))
     73          if mode == '!~':
     74              return "%s not like %s" % (name, q('%' + v + '%'))
     75          if mode == '':
     76              return "%s = %s" % (name, q(v))
     77          if mode == '!':
     78              return "%s != %s" % (name, q(v))
     79
     80
     81 -class Select(WhereJavaScript):
     82      "Convert to/from javascript and where clause element for select entries"
     83      type = 'select'
     84 -    def __init__(self, label, options):
     85          WhereJavaScript.__init__(self, label)
     86          if options:
     87              if type(options[0]) != type(()):
     88                  options = zip(range(len(options)), options)
     89          self.options = options
     90 -    def labelFromValue(self, value):
     91          return dict(self.options).get(value, 'Unknown')
     92 -    def valueFromLabel(self, value):
     93          return dict([(v, l) for l, v in self.options]).get(value, -1)
     94 -    def toJS(self, operator, value):
     95          if operator == '=':
     96              return ('', [self.labelFromValue(value)])
     97          if operator == '!=':
     98              return ('!', [self.labelFromValue(value)])
     99          result = []
    100          if operator in ('<', '>', '<=', '>='):
    101              for i, name in self.options:
    102                  if eval('%d %s %d' % (i, operator, value)):
    103                      result.append(name)
    104          return ('', result)
    105 -    def genProperties(self, name):
    106          return '%s:{type:"%s",label:"%s", options:%r}' % (
    107              name, self.type, self.label, [s[1] for s in self.options])
    108 -    def buildClause1(self, name, v, mode):
    109          v = self.valueFromLabel(v)
    110          if type(v) in types.StringTypes:
    111              v = q(v)
    112          if mode == '':
    113              return "%s = %s" % (name, v)
    114          else:
    115              return "%s != %s" % (name, v)
    116
    117 -class Compare(WhereJavaScript):
    118      "Convert to/from javascript and where clause elements for numeric comparisons"
    119      type = 'compare'
    120 -    def toJS(self, operator, value):
    121          return operator, [value]
    122 -    def buildClause1(self, name, v, mode):
    123          return "%s %s %s" % (name, mode, v)
    124
    125 -class DeviceGroup(Select):
    126 -    def toJS(self, operator, value):
    127          if operator == 'like':
    128              return ['', [value[2:-1]]]
    129          if operator == 'not like':
    130              return ['!', [value[2:-1]]]
    131 -    def buildClause1(self, name, v, mode):
    132          if mode == '':
    133              return "%s like %s" % (name, q('%|' + v + '%'))
    134          else:
    135              return "%s not like %s" % (name, q('%|' + v + '%'))
    136
    137 -class EventClass(Select):
    138      type = 'evtClass'
    139 -    def toJS(self, operator, value):
    140          value = value.rstrip('%')
    141          if operator == '=':
    142              return ['', [value]]
    143          if operator == '!=':
    144              return ['!', [value]]
    145          if operator == 'like':
    146              return ['^', [value]]
    147          if operator == 'not like':
    148              return ['!^', [value]]
    149 -    def buildClause1(self, name, v, mode):
    150          if mode == '':
    151              return "%s = %s" % (name, q(v))
    152          elif mode == '^':
    153              return "%s like %s" % (name, q(v + '%'))
    154          elif mode == '!^':
    155              return "%s not like %s" % (name, q(v + '%'))
    156          else:
    157              return "%s != %s" % (name, q(v))
    158
    159
    160
    161 -class Enumerated(Select):
    162      "Convert to/from javascript and where clause elements for enumerated types"
    163      type='cselect'
    164 -    def toJS(self, operator, value):
    165          return operator, [self.labelFromValue(value)]
    166 -    def buildClause1(self, name, v, mode):
    167          return "%s %s %s" % (name, mode, self.valueFromLabel(v))
    168
    169  _Definitions = r'''
    170  def u(s):
    171      # turn string "'fo''o'" -> "fo'o"
    172      c = s[0]
    173      s = c.join(s.split(c+c))
    174      return s[1:-1]
    175
    176  '''
    177
    178  _ParseSpec = r'''
    179  parser WhereClause:
    180      ignore:    "[ \r\t\n]+"
    181      token END: "$"
    182      token NUM: "[0-9]+"
    183      token VAR: "[a-zA-Z0-9_]+"
    184      token BIN: ">=|<=|==|=|<|>|!=|<>"
    185      token STR: r'"([^\\"]+|\\.)*"'
    186      token STR2: r"'([^\\']+'{2,}|[^\\']+|\\.)*'"
    187
    188      rule goal:    andexp ? END       {{ return locals().get('andexp', None) }}
    189
    190      rule andexp:  orexp              {{ e = orexp }}
    191                    ( "and" orexp      {{ e = ('and', e, orexp) }}
    192                    )*                 {{ return e }}
    193
    194      rule orexp:   binary             {{ e = binary }}
    195                    ( "or" binary      {{ e = ('or', e, binary) }}
    196                    )*                 {{ return e }}
    197
    198      rule binary:  term               {{ e = term }}
    199                    ( BIN term         {{ e = (BIN, e, term) }}
    200                      | 'like' term    {{ e = ('like', e, term) }}
    201                      | 'not' 'like' term
    202                                       {{ e = ('not like', e, term) }}
    203                    )*                 {{ return e }}
    204
    205      rule term:    NUM                {{ return int(NUM) }}
    206                    | VAR              {{ return VAR }}
    207                    | STR              {{ return u(STR) }}
    208                    | STR2             {{ return u(STR2) }}
    209                    | "\\(" andexp "\\)" {{ return andexp }}
    210  '''
    211
    212
    213 -class _Parser:
    214 -    def __init__(self, spec):
    215          from yapps import grammar, yappsrt
    216          from StringIO import StringIO
    217          scanner = grammar.ParserDescriptionScanner(spec)
    218          parser = grammar.ParserDescription(scanner)
    219          parser = yappsrt.wrap_error_reporter(parser, 'Parser')
    220          parser.preparser = _Definitions
    221          parser.output = StringIO()
    222          parser.generate_output()
    223          exec parser.output.getvalue() in self.__dict__
    224
    225  where = _Parser(_ParseSpec)
    226
    227  @json
    228 -def toJavaScript(meta, clause):
    229      # sql is case insensitive, map column names to lower-case versions
    230      lmeta = dict([(n.lower(), n) for n in meta.keys()])
    231      tree = where.parse('goal', clause)
    232      def recurse(root, result):
    233          if type(root) == types.TupleType:
    234              n = len(root)
    235              if n == 1:
    236                  recurse(root[0], result)
    237              op, name, value = root
    238              if op in ('and', 'or'):
    239                  recurse(root[1], result)
    240                  recurse(root[2], result)
    241              else:
    242                  name = lmeta.get(name.lower(), None)
    243                  if name is not None:
    244                      op, value = meta[name].toJS(op, value)
    245                      result.append([name, op, value])
    246      result = []
    247      recurse(tree, result)
    248      result.sort()
    249      return result
    250
    251 -def fromFormVariables(meta, form):
    252      result = []
    253      for n, attrType in meta.items():
    254          if form.has_key(n):
    255              value = form[n]
    256              if type(value) != type([]):
    257                  value = [value]
    258              mode = form.get(n + "_mode", None)
    259              clause = attrType.buildClause(n, value, mode)
    260              if clause:
    261                  result.append('(%s)' % clause)
    262      return ' and '.join(result);
    263
    264
    265  if __name__ == '__main__':
    266      meta = {}
    267      toJavaScript(meta, 'severity = 3 or severity = 4')
    268      toJavaScript(meta, "severity >= 4 and eventState = 0 and prodState = 1000")
    269      toJavaScript(meta, "severity >= 2 and eventState = 0 and prodState = 1000")
    270      print toJavaScript(meta, '(prodState = 1000) and (eventState = 0 or eventState = 1) and (severity >= 3)')
    271      print fromFormVariables(meta,
    272                              dict(severity='Info',
    273                                   severity_mode='>',
    274                                   eventState='New',
    275                                   eventState_mode='=',
    276                                   prodState='Production',
    277                                   prodState_mode='='))
    278