Package pywbem :: Module mof_compiler

[frames] | no frames]

# Source Code for Module pywbem.mof_compiler

       1  #!/usr/bin/env python
       2  #
       3  # (C) Copyright 2006-2007 Novell, Inc.
       4  #
       5  # This library is free software; you can redistribute it and/or
       6  # modify it under the terms of the GNU Lesser General Public
       7  # License as published by the Free Software Foundation; either
       8  # version 2.1 of the License, or (at your option) any later version.
       9  #
      10  # This program is distributed in the hope that it will be useful, but
      11  # WITHOUT ANY WARRANTY; without even the implied warranty of
      12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13  # Lesser General Public License for more details.
      14  #
      15  # You should have received a copy of the GNU Lesser General Public
      16  # License along with this program; if not, write to the Free Software
      17  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
      18  #
      19  # Author: Bart Whiteley <bwhiteley suse.de>
      20  #
      21
      22  import sys
      23  import os
      24  from getpass import getpass
      25
      26  from pywbem import lex, yacc, cim_obj
      27  from pywbem.cim_obj import CIMInstance, CIMInstanceName, CIMClass, \
      28                             CIMProperty, CIMMethod, CIMParameter, \
      29                             CIMQualifier, CIMQualifierDeclaration, NocaseDict
      30  from pywbem.cim_operations import CIMError, WBEMConnection
      31  from pywbem.cim_constants import *
      32
      33  __all__ = ['MOFParseError', 'MOFWBEMConnection', 'MOFCompiler']
      34
      35  _optimize = 1
      36  _tabmodule = 'mofparsetab'
      37  _lextab = 'moflextab'
      38  _outputdir = 'pywbem'
      39
      40  reserved = {
      41      'any':'ANY',
      42      'as':'AS',
      43      'association':'ASSOCIATION',
      44      'class':'CLASS',
      45      'disableoverride':'DISABLEOVERRIDE',
      46      'boolean':'DT_BOOL',
      47      'char16':'DT_CHAR16',
      48      'datetime':'DT_DATETIME',
      49      'pragma':'PRAGMA',
      50      'real32':'DT_REAL32',
      51      'real64':'DT_REAL64',
      52      'sint16':'DT_SINT16',
      53      'sint32':'DT_SINT32',
      54      'sint64':'DT_SINT64',
      55      'sint8':'DT_SINT8',
      56      'string':'DT_STR',
      57      'uint16':'DT_UINT16',
      58      'uint32':'DT_UINT32',
      59      'uint64':'DT_UINT64',
      60      'uint8':'DT_UINT8',
      61      'enableoverride':'ENABLEOVERRIDE',
      62      'false':'FALSE',
      63      'flavor':'FLAVOR',
      64      'indication':'INDICATION',
      65      'instance':'INSTANCE',
      66      'method':'METHOD',
      67      'null':'NULL',
      68      'of':'OF',
      69      'parameter':'PARAMETER',
      70      'property':'PROPERTY',
      71      'qualifier':'QUALIFIER',
      72      'ref':'REF',
      73      'reference':'REFERENCE',
      74      'restricted':'RESTRICTED',
      75      'schema':'SCHEMA',
      76      'scope':'SCOPE',
      77      'tosubclass':'TOSUBCLASS',
      78      'translatable':'TRANSLATABLE',
      79      'true':'TRUE',
      80      }
      81
      82  tokens = reserved.values() + [
      83      'IDENTIFIER',
      84      'stringValue',
      85      'floatValue',
      86      'charValue',
      87      'binaryValue',
      88      'octalValue',
      89      'decimalValue',
      90      'hexValue',
      91  ]
      92
      93  literals = '#(){};[],$:='
      94
      95  # UTF-8 (from Unicode 4.0.0 standard):
      96  # Table 3-6. Well-Formed UTF-8 Byte Sequences Code Points
      97  # 1st Byte 2nd Byte 3rd Byte 4th Byte
      98  # U+0000..U+007F     00..7F
      99  # U+0080..U+07FF     C2..DF   80..BF
     100  # U+0800..U+0FFF     E0       A0..BF   80..BF
     101  # U+1000..U+CFFF     E1..EC   80..BF   80..BF
     102  # U+D000..U+D7FF     ED       80..9F   80..BF
     103  # U+E000..U+FFFF     EE..EF   80..BF   80..BF
     104  # U+10000..U+3FFFF   F0       90..BF   80..BF   80..BF
     105  # U+40000..U+FFFFF   F1..F3   80..BF   80..BF   80..BF
     106  # U+100000..U+10FFFF F4       80..8F   80..BF   80..BF
     107
     108  utf8_2 = r'[\xC2-\xDF][\x80-\xBF]'
     109  utf8_3_1 = r'\xE0[\xA0-\xBF][\x80-\xBF]'
     110  utf8_3_2 = r'[\xE1-\xEC][\x80-\xBF][\x80-\xBF]'
     111  utf8_3_3 = r'\xED[\x80-\x9F][\x80-\xBF]'
     112  utf8_3_4 = r'[\xEE-\xEF][\x80-\xBF][\x80-\xBF]'
     113  utf8_4_1 = r'\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]'
     114  utf8_4_2 = r'[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]'
     115  utf8_4_3 = r'\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]'
     116
     117  utf8Char = r'(%s)|(%s)|(%s)|(%s)|(%s)|(%s)|(%s)|(%s)' % \
     118             (utf8_2, utf8_3_1, utf8_3_2, utf8_3_3, utf8_3_4, utf8_4_1,
     119              utf8_4_2, utf8_4_3)
     120
     121 -def t_COMMENT(t):
     122      r'//.*'
     123      pass
     124
     125 -def t_MCOMMENT(t):
     126      r'/\*(.|\n)*?\*/'
     127      t.lineno += t.value.count('\n')
     128
     129
     130  t_binaryValue = r'[+-]?[01]+[bB]'
     131  t_octalValue = r'[+-]?0[0-7]+'
     132  t_decimalValue = r'[+-]?([1-9][0-9]*|0)'
     133  t_hexValue = r'[+-]?0[xX][0-9a-fA-F]+'
     134  t_floatValue = r'[+-]?[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?'
     135
     136  simpleEscape = r"""[bfnrt'"\\]"""
     137  hexEscape = r'x[0-9a-fA-F]{1,4}'
     138  escapeSequence = r'[\\]((%s)|(%s))' % (simpleEscape, hexEscape)
     139  cChar = r"[^'\\\n\r]|(%s)" % escapeSequence
     140  sChar = r'[^"\\\n\r]|(%s)' % escapeSequence
     141  charValue = r"'%s'" % cChar
     142
     143  t_stringValue = r'"(%s)*"' % sChar
     144
     145  identifier_re = r'([a-zA-Z_]|(%s))([0-9a-zA-Z_]|(%s))*' % (utf8Char, utf8Char)
     146
     147  @lex.TOKEN(identifier_re)
     148 -def t_IDENTIFIER(t):
     149      # check for reserved word
     150      t.type = reserved.get(t.value.lower(), 'IDENTIFIER')
     151      return t
     152
     153  # Define a rule so we can track line numbers
     154 -def t_newline(t):
     155      r'\n+'
     156      t.lexer.lineno += len(t.value)
     157      t.lexer.linestart = t.lexpos
     158
     159  t_ignore = ' \r\t'
     160
     161  # Error handling rule
     162 -def t_error(t):
     163      msg = "Illegal character '%s' " % t.value[0]
     164      msg += "Line %d, col %d" % (t.lineno, _find_column(t.lexer.parser.mof, t))
     165      t.lexer.parser.log(msg)
     166      t.lexer.skip(1)
     167
     168 -class MOFParseError(ValueError):
     169      pass
     170
     171 -def p_error(p):
     172      ex = MOFParseError()
     173      if p is None:
     174          ex.args = ('Unexpected end of file',)
     175          raise ex
     176      ex.file = p.lexer.parser.file
     177      ex.lineno = p.lineno
     178      ex.column = _find_column(p.lexer.parser.mof, p)
     179      ex.context = _get_error_context(p.lexer.parser.mof, p)
     180      raise ex
     181
     182
     183 -def p_mofSpecification(p):
     184      """mofSpecification : mofProductionList"""
     185
     186 -def p_mofProductionList(p):
     187      """mofProductionList : empty
     188                           | mofProductionList mofProduction
     189                             """
     190
     191 -def p_mofProduction(p):
     192      """mofProduction : compilerDirective
     193                       | mp_createClass
     194                       | mp_setQualifier
     195                       | mp_createInstance
     196                       """
     197
     198
     199 -def _create_ns(p, handle, ns):
     200      # Figure out the flavor of cim server
     201      cimom_type = None
     202      ns = ns.strip('/')
     203      try:
     204          inames = handle.EnumerateInstanceNames('__Namespace', namespace='root')
     205          inames = [x['name'] for x in inames]
     206          if 'PG_InterOp' in inames:
     207              cimom_type = 'pegasus'
     208      except CIMError, ce:
     209          if ce.args[0] != CIM_ERR_NOT_FOUND:
     210              ce.file_line = (p.parser.file, p.lexer.lineno)
     211              raise
     212      if not cimom_type:
     213          try:
     214              inames = handle.EnumerateInstanceNames('CIM_Namespace',
     215                                                     namespace='Interop')
     216              inames = [x['name'] for x in inames]
     217              cimom_type = 'proper'
     218          except CIMError, ce:
     219              ce.file_line = (p.parser.file, p.lexer.lineno)
     220              raise
     221
     222      if not cimom_type:
     223          ce = CIMError(CIM_ERR_FAILED,
     224                        'Unable to determine CIMOM type')
     225          ce.file_line = (p.parser.file, p.lexer.lineno)
     226          raise ce
     227      if cimom_type == 'pegasus':
     228          # To create a namespace in Pegasus, create an instance of
     229          # __Namespace with  __Namespace.Name = '', and create it in
     230          # the target namespace to be created.
     231          inst = CIMInstance(
     232              '__Namespace',
     233              properties={'Name':''},
     234              path=CIMInstanceName(
     235                  '__Namespace',
     236                  keybindings={'Name':''},
     237                  namespace=ns))
     238          try:
     239              handle.CreateInstance(inst)
     240          except CIMError, ce:
     241              if ce.args[0] != CIM_ERR_ALREADY_EXISTS:
     242                  ce.file_line = (p.parser.file, p.lexer.lineno)
     243                  raise
     244
     245      elif cimom_type == 'proper':
     246          inst = CIMInstance(
     247              'CIM_Namespace',
     248              properties={'Name': ns},
     249              path=CIMInstanceName(
     250                  'CIM_Namespace',
     251                  namespace='root',
     252                  keybindings={'Name':ns}))
     253          handle.CreateInstance(inst)
     254
     255
     256
     257
     258 -def p_mp_createClass(p):
     259      """mp_createClass : classDeclaration
     260                        | assocDeclaration
     261                        | indicDeclaration
     262                        """
     263      ns = p.parser.handle.default_namespace
     264      cc = p[1]
     265      try:
     266          fixedNS = fixedRefs = fixedSuper = False
     267          while not fixedNS or not fixedRefs or not fixedSuper:
     268              try:
     269                  if p.parser.verbose:
     270                      p.parser.log('Creating class %s:%s' % (ns, cc.classname))
     271                  p.parser.handle.CreateClass(cc)
     272                  if p.parser.verbose:
     273                      p.parser.log('Created class %s:%s' % (ns, cc.classname))
     274                  p.parser.classnames[ns].append(cc.classname.lower())
     275                  break
     276              except CIMError, ce:
     277                  ce.file_line = (p.parser.file, p.lexer.lineno)
     278                  errcode = ce.args[0]
     279                  if errcode == CIM_ERR_INVALID_NAMESPACE:
     280                      if fixedNS:
     281                          raise
     282                      if p.parser.verbose:
     283                          p.parser.log('Creating namespace ' + ns)
     284                      _create_ns(p, p.parser.handle, ns)
     285                      fixedNS = True
     286                      continue
     287                  if not p.parser.search_paths:
     288                      raise
     289                  if errcode == CIM_ERR_INVALID_SUPERCLASS:
     290                      if fixedSuper:
     291                          raise
     292                      moffile = p.parser.mofcomp.find_mof(cc.superclass)
     293                      if not moffile:
     294                          raise
     295                      p.parser.mofcomp.compile_file(moffile, ns)
     296                      fixedSuper = True
     297                  elif errcode in [CIM_ERR_INVALID_PARAMETER,
     298                                   CIM_ERR_NOT_FOUND,
     299                                   CIM_ERR_FAILED]:
     300                      if fixedRefs:
     301                          raise
     302                      if not p.parser.qualcache[ns]:
     303                          for fname in ['qualifiers', 'qualifiers_optional']:
     304                              qualfile = p.parser.mofcomp.find_mof(fname)
     305                              if qualfile:
     306                                  p.parser.mofcomp.compile_file(qualfile, ns)
     307                      if not p.parser.qualcache[ns]:
     308                          # can't find qualifiers
     309                          raise
     310                      objects = cc.properties.values()
     311                      for meth in cc.methods.values():
     312                          objects += meth.parameters.values()
     313                      dep_classes = []
     314                      for obj in objects:
     315                          if obj.type not in ['reference', 'string']:
     316                              continue
     317                          if obj.type == 'reference':
     318                              if obj.reference_class.lower() not in dep_classes:
     319                                  dep_classes.append(obj.reference_class.lower())
     320                              continue
     321                          # else obj.type is 'string'
     322                          try:
     323                              embedded_inst = obj.qualifiers['embeddedinstance']
     324                          except KeyError:
     325                              continue
     326                          embedded_inst = embedded_inst.value.lower()
     327                          if embedded_inst not in dep_classes:
     328                              dep_classes.append(embedded_inst)
     329                          continue
     330                      for klass in dep_classes:
     331                          if klass in p.parser.classnames[ns]:
     332                              continue
     333                          try:
     334                              # don't limit it with LocalOnly=True,
     335                              # PropertyList, IncludeQualifiers=False, ...
     336                              # because of caching in case we're using the
     337                              # special WBEMConnection subclass used for
     338                              # removing schema elements
     339                              p.parser.handle.GetClass(klass,
     340                                                       LocalOnly=False,
     341                                                       IncludeQualifiers=True)
     342                              p.parser.classnames[ns].append(klass)
     343                          except CIMError:
     344                              moffile = p.parser.mofcomp.find_mof(klass)
     345                              if not moffile:
     346                                  raise
     347                              p.parser.mofcomp.compile_file(moffile, ns)
     348                              p.parser.classnames[ns].append(klass)
     349                      fixedRefs = True
     350                  else:
     351                      raise
     352
     353      except CIMError, ce:
     354          ce.file_line = (p.parser.file, p.lexer.lineno)
     355          if ce.args[0] != CIM_ERR_ALREADY_EXISTS:
     356              raise
     357          if p.parser.verbose:
     358              p.parser.log('Class %s already exist.  Modifying...' % cc.classname)
     359          try:
     360              p.parser.handle.ModifyClass(cc, ns)
     361          except CIMError, ce:
     362              p.parser.log('Error Modifying class %s: %s, %s' % \
     363                           (cc.classname, ce.args[0], ce.args[1]))
     364
     365 -def p_mp_createInstance(p):
     366      """mp_createInstance : instanceDeclaration"""
     367      inst = p[1]
     368      if p.parser.verbose:
     369          p.parser.log('Creating instance of %s.' % inst.classname)
     370      try:
     371          p.parser.handle.CreateInstance(inst)
     372      except CIMError, ce:
     373          if ce.args[0] == CIM_ERR_ALREADY_EXISTS:
     374              if p.parser.verbose:
     375                  p.parser.log('Instance of class %s already exist.  ' \
     376                               'Modifying...' % inst.classname)
     377              try:
     378                  p.parser.handle.ModifyInstance(inst)
     379              except CIMError, ce:
     380                  if ce.args[0] == CIM_ERR_NOT_SUPPORTED:
     381                      if p.parser.verbose:
     382                          p.parser.log('ModifyInstance not supported.  ' \
     383                                       'Deleting instance of %s: %s' % \
     384                                       (inst.classname, inst.path))
     385                      p.parser.handle.DeleteInstance(inst.path)
     386                      if p.parser.verbose:
     387                          p.parser.log('Creating instance of %s.' % \
     388                                       inst.classname)
     389                      p.parser.handle.CreateInstance(inst)
     390          else:
     391              ce.file_line = (p.parser.file, p.lexer.lineno)
     392              raise
     393
     394 -def p_mp_setQualifier(p):
     395      """mp_setQualifier : qualifierDeclaration"""
     396      qualdecl = p[1]
     397      ns = p.parser.handle.default_namespace
     398      if p.parser.verbose:
     399          p.parser.log('Setting qualifier %s' % qualdecl.name)
     400      try:
     401          p.parser.handle.SetQualifier(qualdecl)
     402      except CIMError, ce:
     403          if ce.args[0] == CIM_ERR_INVALID_NAMESPACE:
     404              if p.parser.verbose:
     405                  p.parser.log('Creating namespace ' + ns)
     406              _create_ns(p, p.parser.handle, ns)
     407              if p.parser.verbose:
     408                  p.parser.log('Setting qualifier %s' % qualdecl.name)
     409              p.parser.handle.SetQualifier(qualdecl)
     410          elif ce.args[0] == CIM_ERR_NOT_SUPPORTED:
     411              if p.parser.verbose:
     412                  p.parser.log('Qualifier %s already exists.  Deleting...' % \
     413                               qualdecl.name)
     414              p.parser.handle.DeleteQualifier(qualdecl.name)
     415              if p.parser.verbose:
     416                  p.parser.log('Setting qualifier %s' % qualdecl.name)
     417              p.parser.handle.SetQualifier(qualdecl)
     418          else:
     419              ce.file_line = (p.parser.file, p.lexer.lineno)
     420              raise
     421      p.parser.qualcache[ns][qualdecl.name] = qualdecl
     422
     423 -def p_compilerDirective(p):
     424      """compilerDirective : '#' PRAGMA pragmaName '(' pragmaParameter ')'"""
     425      directive = p[3].lower()
     426      param = p[5]
     427      if directive == 'include':
     428          fname = param
     429          #if p.parser.file:
     430          fname = os.path.dirname(p.parser.file) + '/' + fname
     431          p.parser.mofcomp.compile_file(fname, p.parser.handle.default_namespace)
     432      elif directive == 'namespace':
     433          p.parser.handle.default_namespace = param
     434          if param not in p.parser.qualcache:
     435              p.parser.qualcache[param] = NocaseDict()
     436
     437      p[0] = None
     438
     439 -def p_pragmaName(p):
     440      """pragmaName : identifier"""
     441      p[0] = p[1]
     442
     443 -def p_pragmaParameter(p):
     444      """pragmaParameter : stringValue"""
     445      p[0] = _fixStringValue(p[1])
     446
     447 -def p_classDeclaration(p):
     448      """classDeclaration :
     449            CLASS className '{' classFeatureList '}' ';'
     450          | CLASS className superClass '{' classFeatureList '}' ';'
     451          | CLASS className alias '{' classFeatureList '}' ';'
     452          | CLASS className alias superClass '{' classFeatureList '}' ';'
     453          | qualifierList CLASS className '{' classFeatureList '}' ';'
     454          | qualifierList CLASS className superClass '{' classFeatureList '}' ';'
     455          | qualifierList CLASS className alias '{' classFeatureList '}' ';'
     456          | qualifierList CLASS className alias superClass '{'
     457                                                classFeatureList '}' ';'
     458      """
     459      superclass = None
     460      alias = None
     461      quals = []
     462      if isinstance(p[1], basestring): # no class qualifiers
     463          cname = p[2]
     464          if p[3][0] == '$': # alias present
     465              alias = p[3]
     466              if p[4] == '{': # no superclass
     467                  cfl = p[5]
     468              else: # superclass
     469                  superclass = p[4]
     470                  cfl = p[6]
     471          else: # no alias
     472              if p[3] == '{': # no superclass
     473                  cfl = p[4]
     474              else: # superclass
     475                  superclass = p[3]
     476                  cfl = p[5]
     477      else: # class qualifiers
     478          quals = p[1]
     479          cname = p[3]
     480          if p[4][0] == '$': # alias present
     481              alias = p[4]
     482              if p[5] == '{': # no superclass
     483                  cfl = p[6]
     484              else: # superclass
     485                  superclass = p[5]
     486                  cfl = p[7]
     487          else: # no alias
     488              if p[4] == '{': # no superclass
     489                  cfl = p[5]
     490              else: # superclass
     491                  superclass = p[4]
     492                  cfl = p[6]
     493      quals = dict([(x.name, x) for x in quals])
     494      methods = {}
     495      props = {}
     496      for item in cfl:
     497          item.class_origin = cname
     498          if isinstance(item, CIMMethod):
     499              methods[item.name] = item
     500          else:
     501              props[item.name] = item
     502      p[0] = CIMClass(cname, properties=props, methods=methods,
     503                              superclass=superclass, qualifiers=quals)
     504      if alias:
     505          p.parser.aliases[alias] = p[0]
     506
     507 -def p_classFeatureList(p):
     508      """classFeatureList : empty
     509                          | classFeatureList classFeature
     510                          """
     511      if len(p) == 2:
     512          p[0] = []
     513      else:
     514          p[0] = p[1] + [p[2]]
     515
     516 -def p_assocDeclaration(p):
     517      """assocDeclaration :
     518            '[' ASSOCIATION qualifierListEmpty ']' CLASS className '{'
     519                  associationFeatureList '}' ';'
     520          | '[' ASSOCIATION qualifierListEmpty ']' CLASS className superClass '{'
     521                  associationFeatureList '}' ';'
     522          | '[' ASSOCIATION qualifierListEmpty ']' CLASS className alias '{'
     523                  associationFeatureList '}' ';'
     524          | '[' ASSOCIATION qualifierListEmpty ']' CLASS className alias
     525                  superClass '{' associationFeatureList '}' ';'
     526      """
     527      aqual = CIMQualifier('ASSOCIATION', True, type='boolean')
     528      # TODO flavor trash.
     529      quals = [aqual] + p[3]
     530      p[0] = _assoc_or_indic_decl(quals, p)
     531
     532 -def p_indicDeclaration(p):
     533      """indicDeclaration :
     534            '[' INDICATION qualifierListEmpty ']' CLASS className '{'
     535                  classFeatureList '}' ';'
     536          | '[' INDICATION qualifierListEmpty ']' CLASS className superClass '{'
     537                  classFeatureList '}' ';'
     538          | '[' INDICATION qualifierListEmpty ']' CLASS className alias '{'
     539                  classFeatureList '}' ';'
     540          | '[' INDICATION qualifierListEmpty ']' CLASS className alias
     541                  superClass '{' classFeatureList '}' ';'
     542      """
     543      iqual = CIMQualifier('INDICATION', True, type='boolean')
     544      # TODO flavor trash.
     545      quals = [iqual] + p[3]
     546      p[0] = _assoc_or_indic_decl(quals, p)
     547
     548 -def _assoc_or_indic_decl(quals, p):
     549      """(refer to grammer rules on p_assocDeclaration and p_indicDeclaration)"""
     550      superclass = None
     551      alias = None
     552      cname = p[6]
     553      if p[7] == '{':
     554          cfl = p[8]
     555      elif p[7][0] == '$': # alias
     556          alias = p[7]
     557          if p[8] == '{':
     558              cfl = p[9]
     559          else:
     560              superclass = p[8]
     561              cfl = p[10]
     562      else:
     563          superclass = p[7]
     564          cfl = p[9]
     565      props = {}
     566      methods = {}
     567      for item in cfl:
     568          item.class_origin = cname
     569          if isinstance(item, CIMMethod):
     570              methods[item.name] = item
     571          else:
     572              props[item.name] = item
     573      quals = dict([(x.name, x) for x in quals])
     574      cc = CIMClass(cname, properties=props, methods=methods,
     575                            superclass=superclass, qualifiers=quals)
     576      if alias:
     577          p.parser.aliases[alias] = cc
     578      return cc
     579
     580 -def p_qualifierListEmpty(p):
     581      """qualifierListEmpty : empty
     582                            | qualifierListEmpty ',' qualifier
     583                            """
     584      if len(p) == 2:
     585          p[0] = []
     586      else:
     587          p[0] = p[1] + [p[3]]
     588
     589 -def p_associationFeatureList(p):
     590      """associationFeatureList : empty
     591                                | associationFeatureList associationFeature
     592                                """
     593      if len(p) == 2:
     594          p[0] = []
     595      else:
     596          p[0] = p[1] + [p[2]]
     597
     598 -def p_className(p):
     599      """className : identifier"""
     600      p[0] = p[1]
     601
     602 -def p_alias(p):
     603      """alias : AS aliasIdentifier"""
     604      p[0] = p[2]
     605
     606 -def p_aliasIdentifier(p):
     607      """aliasIdentifier : '$' identifier"""
     608      p[0] = '$%s' % p[2]
     609
     610 -def p_superClass(p):
     611      """superClass : ':' className"""
     612      p[0] = p[2]
     613
     614 -def p_classFeature(p):
     615      """classFeature : propertyDeclaration
     616                      | methodDeclaration
     617                      | referenceDeclaration
     618                      """
     619      p[0] = p[1]
     620
     621 -def p_associationFeature(p):
     622      """associationFeature : classFeature"""
     623      p[0] = p[1]
     624
     625 -def p_qualifierList(p):
     626      """qualifierList : '[' qualifier qualifierListEmpty ']'"""
     627      p[0] = [p[2]] + p[3]
     628
     629 -def p_qualifier(p):
     630      """qualifier : qualifierName
     631                   | qualifierName ':' flavorList
     632                   | qualifierName qualifierParameter
     633                   | qualifierName qualifierParameter ':' flavorList
     634                   """
     635      qname = p[1]
     636      ns = p.parser.handle.default_namespace
     637      qval = None
     638      flavorlist = []
     639      if len(p) == 3:
     640          qval = p[2]
     641      elif len(p) == 4:
     642          flavorlist = p[3]
     643      elif len(p) == 5:
     644          qval = p[2]
     645          flavorlist = p[4]
     646      try:
     647          qualdecl = p.parser.qualcache[ns][qname]
     648      except KeyError:
     649          try:
     650              quals = p.parser.handle.EnumerateQualifiers()
     651          except CIMError, ce:
     652              if ce.args[0] != CIM_ERR_INVALID_NAMESPACE:
     653                  ce.file_line = (p.parser.file, p.lexer.lineno)
     654                  raise
     655              _create_ns(p, p.parser.handle, ns)
     656              quals = None
     657
     658          if quals:
     659              for qual in quals:
     660                  p.parser.qualcache[ns][qual.name] = qual
     661          else:
     662              for fname in ['qualifiers', 'qualifiers_optional']:
     663                  qualfile = p.parser.mofcomp.find_mof(fname)
     664                  if qualfile:
     665                      p.parser.mofcomp.compile_file(qualfile, ns)
     666      try:
     667          qualdecl = p.parser.qualcache[ns][qname]
     668      except KeyError:
     669          ce = CIMError(CIM_ERR_FAILED, 'Unknown Qualifier: %s' % qname)
     670          ce.file_line = (p.parser.file, p.lexer.lineno)
     671          raise ce
     672
     673      flavors = _build_flavors(flavorlist, qualdecl)
     674      if qval is None:
     675          if qualdecl.type == 'boolean':
     676              qval = True
     677          else:
     678              qval = qualdecl.value # default value
     679      else:
     680          qval = cim_obj.tocimobj(qualdecl.type, qval)
     681      p[0] = CIMQualifier(qname, qval, type=qualdecl.type, **flavors)
     682      # TODO propagated?
     683
     684 -def p_flavorList(p):
     685      """flavorList : flavor
     686                    | flavorList flavor
     687                    """
     688      if len(p) == 2:
     689          p[0] = [p[1]]
     690      else:
     691          p[0] = p[1] + [p[2]]
     692
     693 -def p_qualifierParameter(p):
     694      """qualifierParameter : '(' constantValue ')'
     695                            | arrayInitializer
     696                            """
     697      if len(p) == 2:
     698          p[0] = p[1]
     699      else:
     700          p[0] = p[2]
     701
     702 -def p_flavor(p):
     703      """flavor : ENABLEOVERRIDE
     704                | DISABLEOVERRIDE
     705                | RESTRICTED
     706                | TOSUBCLASS
     707                | TRANSLATABLE
     708                """
     709      p[0] = p[1].lower()
     710
     711 -def p_propertyDeclaration(p):
     712      """propertyDeclaration : propertyDeclaration_1
     713                             | propertyDeclaration_2
     714                             | propertyDeclaration_3
     715                             | propertyDeclaration_4
     716                             | propertyDeclaration_5
     717                             | propertyDeclaration_6
     718                             | propertyDeclaration_7
     719                             | propertyDeclaration_8
     720                             """
     721      p[0] = p[1]
     722
     723 -def p_propertyDeclaration_1(p):
     724      """propertyDeclaration_1 : dataType propertyName ';'"""
     725      p[0] = CIMProperty(p[2], None, type=p[1])
     726
     727 -def p_propertyDeclaration_2(p):
     728      """propertyDeclaration_2 : dataType propertyName defaultValue ';'"""
     729      p[0] = CIMProperty(p[2], p[3], type=p[1])
     730
     731 -def p_propertyDeclaration_3(p):
     732      """propertyDeclaration_3 : dataType propertyName array ';'"""
     733      p[0] = CIMProperty(p[2], None, type=p[1], is_array=True,
     734                                 array_size=p[3])
     735
     736 -def p_propertyDeclaration_4(p):
     737      """propertyDeclaration_4 : dataType propertyName array defaultValue ';'"""
     738      p[0] = CIMProperty(p[2], p[4], type=p[1], is_array=True,
     739                                 array_size=p[3])
     740
     741 -def p_propertyDeclaration_5(p):
     742      """propertyDeclaration_5 : qualifierList dataType propertyName ';'"""
     743      quals = dict([(x.name, x) for x in p[1]])
     744      p[0] = CIMProperty(p[3], None, type=p[2], qualifiers=quals)
     745
     746 -def p_propertyDeclaration_6(p):
     747      """propertyDeclaration_6 : qualifierList dataType propertyName
     748                                 defaultValue ';'"""
     749      quals = dict([(x.name, x) for x in p[1]])
     750      p[0] = CIMProperty(p[3], cim_obj.tocimobj(p[2], p[4]),
     751                                 type=p[2], qualifiers=quals)
     752
     753 -def p_propertyDeclaration_7(p):
     754      """propertyDeclaration_7 : qualifierList dataType propertyName array ';'"""
     755      quals = dict([(x.name, x) for x in p[1]])
     756      p[0] = CIMProperty(p[3], None, type=p[2], qualifiers=quals,
     757                                 is_array=True, array_size=p[4])
     758
     759 -def p_propertyDeclaration_8(p):
     760      """propertyDeclaration_8 : qualifierList dataType propertyName array
     761                                 defaultValue ';'"""
     762      quals = dict([(x.name, x) for x in p[1]])
     763      p[0] = CIMProperty(p[3], cim_obj.tocimobj(p[2], p[5]),
     764                                 type=p[2], qualifiers=quals, is_array=True,
     765                                 array_size=p[4])
     766
     767 -def p_referenceDeclaration(p):
     768      """referenceDeclaration :
     769            objectRef referenceName ';'
     770          | objectRef referenceName defaultValue ';'
     771          | qualifierList objectRef referenceName ';'
     772          | qualifierList objectRef referenceName defaultValue ';'
     773      """
     774      quals = []
     775      dv = None
     776      if isinstance(p[1], list): # qualifiers
     777          quals = p[1]
     778          cname = p[2]
     779          pname = p[3]
     780          if len(p) == 6:
     781              dv = p[4]
     782      else:
     783          cname = p[1]
     784          pname = p[2]
     785          if len(p) == 5:
     786              dv = p[3]
     787      quals = dict([(x.name, x) for x in quals])
     788      p[0] = CIMProperty(pname, dv, type='reference',
     789                                 reference_class=cname, qualifiers=quals)
     790
     791 -def p_methodDeclaration(p):
     792      """methodDeclaration :
     793            dataType methodName '(' ')' ';'
     794          | dataType methodName '(' parameterList ')' ';'
     795          | qualifierList dataType methodName '(' ')' ';'
     796          | qualifierList dataType methodName '(' parameterList ')' ';'
     797      """
     798      paramlist = []
     799      quals = []
     800      if isinstance(p[1], basestring): # no quals
     801          dt = p[1]
     802          mname = p[2]
     803          if p[4] != ')':
     804              paramlist = p[4]
     805      else: # quals present
     806          quals = p[1]
     807          dt = p[2]
     808          mname = p[3]
     809          if p[5] != ')':
     810              paramlist = p[5]
     811      params = dict([(param.name, param) for param in paramlist])
     812      quals = dict([(q.name, q) for q in quals])
     813      p[0] = CIMMethod(mname, return_type=dt, parameters=params,
     814                               qualifiers=quals)
     815      # note: class_origin is set when adding method to class.
     816      # TODO what to do with propagated?
     817
     818 -def p_propertyName(p):
     819      """propertyName : identifier"""
     820      p[0] = p[1]
     821
     822 -def p_referenceName(p):
     823      """referenceName : identifier"""
     824      p[0] = p[1]
     825
     826 -def p_methodName(p):
     827      """methodName : identifier"""
     828      p[0] = p[1]
     829
     830 -def p_dataType(p):
     831      """dataType : DT_UINT8
     832                  | DT_SINT8
     833                  | DT_UINT16
     834                  | DT_SINT16
     835                  | DT_UINT32
     836                  | DT_SINT32
     837                  | DT_UINT64
     838                  | DT_SINT64
     839                  | DT_REAL32
     840                  | DT_REAL64
     841                  | DT_CHAR16
     842                  | DT_STR
     843                  | DT_BOOL
     844                  | DT_DATETIME
     845                  """
     846      p[0] = p[1].lower()
     847
     848 -def p_objectRef(p):
     849      """objectRef : className REF"""
     850      p[0] = p[1]
     851
     852 -def p_parameterList(p):
     853      """parameterList : parameter
     854                       | parameterList ',' parameter
     855                       """
     856      if len(p) == 2:
     857          p[0] = [p[1]]
     858      else:
     859          p[0] = p[1] + [p[3]]
     860
     861 -def p_parameter(p):
     862      """parameter : parameter_1
     863                   | parameter_2
     864                   | parameter_3
     865                   | parameter_4
     866                   """
     867      p[0] = p[1]
     868
     869 -def p_parameter_1(p):
     870      """parameter_1 : dataType parameterName
     871                     | dataType parameterName array
     872                     """
     873      args = {}
     874      if len(p) == 4:
     875          args['is_array'] = True
     876          args['array_size'] = p[3]
     877      p[0] = CIMParameter(p[2], p[1], **args)
     878
     879 -def p_parameter_2(p):
     880      """parameter_2 : qualifierList dataType parameterName
     881                     | qualifierList dataType parameterName array
     882                     """
     883      args = {}
     884      if len(p) == 5:
     885          args['is_array'] = True
     886          args['array_size'] = p[4]
     887      quals = dict([(x.name, x) for x in p[1]])
     888      p[0] = CIMParameter(p[3], p[2], qualifiers=quals, **args)
     889
     890 -def p_parameter_3(p):
     891      """parameter_3 : objectRef parameterName
     892                     | objectRef parameterName array
     893                     """
     894      args = {}
     895      if len(p) == 4:
     896          args['is_array'] = True
     897          args['array_size'] = p[3]
     898      p[0] = CIMParameter(p[2], 'reference', reference_class=p[1],
     899                                  **args)
     900
     901 -def p_parameter_4(p):
     902      """parameter_4 : qualifierList objectRef parameterName
     903                     | qualifierList objectRef parameterName array
     904                     """
     905      args = {}
     906      if len(p) == 5:
     907          args['is_array'] = True
     908          args['array_size'] = p[4]
     909      quals = dict([(x.name, x) for x in p[1]])
     910      p[0] = CIMParameter(p[3], 'reference', qualifiers=quals,
     911                                  reference_class=p[2], **args)
     912
     913 -def p_parameterName(p):
     914      """parameterName : identifier"""
     915      p[0] = p[1]
     916
     917 -def p_array(p):
     918      """array : '[' ']'
     919               | '[' integerValue ']'
     920               """
     921      if len(p) == 3:
     922          p[0] = None
     923      else:
     924          p[0] = p[2]
     925
     926 -def p_defaultValue(p):
     927      """defaultValue : '=' initializer"""
     928      p[0] = p[2]
     929
     930 -def p_initializer(p):
     931      """initializer : constantValue
     932                     | arrayInitializer
     933                     | referenceInitializer
     934                     """
     935      p[0] = p[1]
     936
     937 -def p_arrayInitializer(p):
     938      """arrayInitializer : '{' constantValueList '}'
     939                          | '{' '}'
     940                          """
     941      if len(p) == 3:
     942          p[0] = []
     943      else:
     944          p[0] = p[2]
     945
     946 -def p_constantValueList(p):
     947      """constantValueList : constantValue
     948                           | constantValueList ',' constantValue
     949                           """
     950      if len(p) == 2:
     951          p[0] = [p[1]]
     952      else:
     953          p[0] = p[1] + [p[3]]
     954
     955 -def _fixStringValue(s):
     956      s = s[1:-1]
     957      rv = ''
     958      esc = False
     959      i = -1
     960      while i < len(s) -1:
     961          i += 1
     962          ch = s[i]
     963          if ch == '\\' and not esc:
     964              esc = True
     965              continue
     966          if not esc:
     967              rv += ch
     968              continue
     969
     970          if ch == '"': rv += '"'
     971          elif ch == 'n': rv += '\n'
     972          elif ch == 't': rv += '\t'
     973          elif ch == 'b': rv += '\b'
     974          elif ch == 'f': rv += '\f'
     975          elif ch == 'r': rv += '\r'
     976          elif ch == '\\': rv += '\\'
     977          elif ch in ['x', 'X']:
     978              hexc = 0
     979              j = 0
     980              i += 1
     981              while j < 4:
     982                  c = s[i+j];
     983                  c = c.upper()
     984                  if not c.isdigit() and not c in 'ABCDEF':
     985                      break;
     986                  hexc <<= 4
     987                  if c.isdigit():
     988                      hexc |= ord(c) - ord('0')
     989                  else:
     990                      hexc |= ord(c) - ord('A') + 0XA
     991                  j += 1
     992              rv += chr(hexc)
     993              i += j-1
     994
     995          esc = False
     996
     997      return rv
     998
     999
    1000
    1001 -def p_stringValueList(p):
    1002      """stringValueList : stringValue
    1003                         | stringValueList stringValue
    1004                         """
    1005      if len(p) == 2:
    1006          p[0] = _fixStringValue(p[1])
    1007      else:
    1008          p[0] = p[1] + _fixStringValue(p[2])
    1009
    1010
    1011 -def p_constantValue(p):
    1012      """constantValue : integerValue
    1013                       | floatValue
    1014                       | charValue
    1015                       | stringValueList
    1016                       | booleanValue
    1017                       | nullValue
    1018                       """
    1019      p[0] = p[1]
    1020
    1021 -def p_integerValue(p):
    1022      """integerValue : binaryValue
    1023                      | octalValue
    1024                      | decimalValue
    1025                      | hexValue
    1026                      """
    1027      p[0] = int(p[1])
    1028      # TODO deal with non-decimal values.
    1029
    1030 -def p_referenceInitializer(p):
    1031      """referenceInitializer : objectHandle
    1032                              | aliasIdentifier
    1033                              """
    1034      if p[1][0] == '$':
    1035          try:
    1036              p[0] = p.parser.aliases[p[1]]
    1037          except KeyError:
    1038              ce = CIMError(CIM_ERR_FAILED,
    1039                            'Unknown alias: ' + p[0])
    1040              ce.file_line = (p.parser.file, p.lexer.lineno)
    1041              raise ce
    1042      else:
    1043          p[0] = p[1]
    1044
    1045 -def p_objectHandle(p):
    1046      """objectHandle : identifier"""
    1047      p[0] = p[1]
    1048
    1049 -def p_qualifierDeclaration(p):
    1050      """qualifierDeclaration :
    1051            QUALIFIER qualifierName qualifierType scope ';'
    1052          | QUALIFIER qualifierName qualifierType scope defaultFlavor ';'
    1053      """
    1054      qualtype = p[3]
    1055      dt, is_array, array_size, value = qualtype
    1056      qualname = p[2]
    1057      scopes = p[4]
    1058      if len(p) == 5:
    1059          flist = []
    1060      else:
    1061          flist = p[5]
    1062      flavors = _build_flavors(flist)
    1063
    1064      p[0] = CIMQualifierDeclaration(
    1065          qualname, dt, value=value, is_array=is_array, array_size=array_size,
    1066          scopes=scopes, **flavors)
    1067
    1068 -def _build_flavors(flist, qualdecl=None):
    1069      flavors = {}
    1070      if qualdecl is not None:
    1071          flavors = {'overridable':qualdecl.overridable,
    1072                     'translatable':qualdecl.translatable,
    1073                     'toinstance':qualdecl.toinstance,
    1074                     'tosubclass':qualdecl.tosubclass}
    1075      if 'disableoverride' in flist:
    1076          flavors['overridable'] = False
    1077      if 'enableoverride' in flist:
    1078          flavors['overridable'] = True
    1079      if 'translatable' in flist:
    1080          flavors['translatable'] = True
    1081      if 'restricted' in flist:
    1082          flavors['tosubclass'] = False
    1083      if 'tosubclass' in flist:
    1084          flavors['tosubclass'] = True
    1085      try:
    1086          if flavors['tosubclass']:
    1087              flavors['toinstance'] = True
    1088      except KeyError:
    1089          pass
    1090      return flavors
    1091
    1092 -def p_qualifierName(p):
    1093      """qualifierName : identifier
    1094                       | ASSOCIATION
    1095                       | INDICATION
    1096                       """
    1097      p[0] = p[1]
    1098
    1099 -def p_qualifierType(p):
    1100      """qualifierType : qualifierType_1
    1101                       | qualifierType_2
    1102                       """
    1103      p[0] = p[1]
    1104
    1105 -def p_qualifierType_1(p):
    1106      """qualifierType_1 : ':' dataType array
    1107                         | ':' dataType array defaultValue
    1108                         """
    1109      dv = None
    1110      if len(p) == 5:
    1111          dv = p[4]
    1112      p[0] = (p[2], True, p[3], dv)
    1113
    1114 -def p_qualifierType_2(p):
    1115      """qualifierType_2 : ':' dataType
    1116                         | ':' dataType defaultValue
    1117                         """
    1118      dv = None
    1119      if len(p) == 4:
    1120          dv = p[3]
    1121      p[0] = (p[2], False, None, dv)
    1122
    1123 -def p_scope(p):
    1124      """scope : ',' SCOPE '(' metaElementList ')'"""
    1125      slist = p[4]
    1126      scopes = {}
    1127      for i in ('CLASS',
    1128                'ASSOCIATION',
    1129                'INDICATION',
    1130                'PROPERTY',
    1131                'REFERENCE',
    1132                'METHOD',
    1133                'PARAMETER',
    1134                'ANY'):
    1135          scopes[i] = i in slist
    1136      p[0] = scopes
    1137
    1138 -def p_metaElementList(p):
    1139      """metaElementList : metaElement
    1140                         | metaElementList ',' metaElement
    1141                         """
    1142      if len(p) == 2:
    1143          p[0] = [p[1]]
    1144      else:
    1145          p[0] = p[1] + [p[3]]
    1146
    1147 -def p_metaElement(p):
    1148      """metaElement : SCHEMA
    1149                     | CLASS
    1150                     | ASSOCIATION
    1151                     | INDICATION
    1152                     | QUALIFIER
    1153                     | PROPERTY
    1154                     | REFERENCE
    1155                     | METHOD
    1156                     | PARAMETER
    1157                     | ANY
    1158                     """
    1159      p[0] = p[1].upper()
    1160
    1161 -def p_defaultFlavor(p):
    1162      """defaultFlavor : ',' FLAVOR '(' flavorListWithComma ')'"""
    1163      flist = p[4]
    1164      flavors = {'ENABLEOVERRIDE':True,
    1165                 'TOSUBCLASS':True,
    1166                 'DISABLEOVERRIDE':False,
    1167                 'RESTRICTED':False,
    1168                 'TRANSLATABLE':False}
    1169      for i in flist:
    1170          flavors[i] = True
    1171      p[0] = flavors
    1172
    1173
    1174 -def p_flavorListWithComma(p):
    1175      """flavorListWithComma : flavor
    1176                             | flavorListWithComma ',' flavor
    1177                             """
    1178      if len(p) == 2:
    1179          p[0] = [p[1]]
    1180      else:
    1181          p[0] = p[1] + [p[3]]
    1182
    1183 -def p_instanceDeclaration(p):
    1184      """instanceDeclaration :
    1185            INSTANCE OF className '{' valueInitializerList '}' ';'
    1186          | INSTANCE OF className alias '{' valueInitializerList '}' ';'
    1187          | qualifierList INSTANCE OF className '{' valueInitializerList '}' ';'
    1188          | qualifierList INSTANCE OF className alias '{'
    1189                  valueInitializerList '}' ';'
    1190      """
    1191      alias = None
    1192      quals = {}
    1193      ns = p.parser.handle.default_namespace
    1194      if isinstance(p[1], basestring): # no qualifiers
    1195          cname = p[3]
    1196          if p[4] == '{':
    1197              props = p[5]
    1198          else:
    1199              props = p[6]
    1200              alias = p[4]
    1201      else:
    1202          cname = p[4]
    1203          #quals = p[1] # qualifiers on instances are deprecated -- rightly so.
    1204          if p[5] == '{':
    1205              props = p[6]
    1206          else:
    1207              props = p[7]
    1208              alias = p[5]
    1209
    1210      try:
    1211          cc = p.parser.handle.GetClass(cname, LocalOnly=False,
    1212                                        IncludeQualifiers=True)
    1213          p.parser.classnames[ns].append(cc.classname.lower())
    1214      except CIMError, ce:
    1215          ce.file_line = (p.parser.file, p.lexer.lineno)
    1216          if ce.args[0] == CIM_ERR_NOT_FOUND:
    1217              file_ = p.parser.mofcomp.find_mof(cname)
    1218              if p.parser.verbose:
    1219                  p.parser.log('Class %s does not exist' % cname)
    1220              if file_:
    1221                  p.parser.mofcomp.compile_file(file_, ns)
    1222                  cc = p.parser.handle.GetClass(cname, LocalOnly=False,
    1223                                                IncludeQualifiers=True)
    1224              else:
    1225                  if p.parser.verbose:
    1226                      p.parser.log("Can't find file to satisfy class")
    1227                  ce = CIMError(CIM_ERR_INVALID_CLASS, cname)
    1228                  ce.file_line = (p.parser.file, p.lexer.lineno)
    1229                  raise ce
    1230          else:
    1231              raise
    1232      path = CIMInstanceName(cname, namespace=ns)
    1233      inst = CIMInstance(cname, properties=cc.properties,
    1234                         qualifiers=quals, path=path)
    1235      for prop in props:
    1236          pname = prop[1]
    1237          pval = prop[2]
    1238          try:
    1239              cprop = inst.properties[pname]
    1240              cprop.value = cim_obj.tocimobj(cprop.type, pval)
    1241          except KeyError:
    1242              ce = CIMError(CIM_ERR_INVALID_PARAMETER,
    1243                            'Invalid property: %s' % pname)
    1244              ce.file_line = (p.parser.file, p.lexer.lineno)
    1245              raise ce
    1246          except ValueError, ve:
    1247              ce = CIMError(CIM_ERR_INVALID_PARAMETER,
    1248                            'Invalid value for property: %s: %s' % \
    1249                            (pname, ve.message))
    1250              ce.file_line = (p.parser.file, p.lexer.lineno)
    1251              raise ce
    1252
    1253      for prop in inst.properties.values():
    1254          if 'key' not in prop.qualifiers or not prop.qualifiers['key']:
    1255              continue
    1256          if prop.value is None:
    1257              ce = CIMError(CIM_ERR_FAILED,
    1258                            'Key property %s.%s is not set' % (cname, prop.name))
    1259              ce.file_line = (p.parser.file, p.lexer.lineno)
    1260              raise ce
    1261          inst.path.keybindings[prop.name] = prop.value
    1262
    1263      if alias:
    1264          p.parser.aliases[alias] = inst.path
    1265      p[0] = inst
    1266
    1267 -def p_valueInitializerList(p):
    1268      """valueInitializerList : valueInitializer
    1269                              | valueInitializerList valueInitializer
    1270                              """
    1271      if len(p) == 2:
    1272          p[0] = [p[1]]
    1273      else:
    1274          p[0] = p[1] + [p[2]]
    1275
    1276
    1277 -def p_valueInitializer(p):
    1278      """valueInitializer : identifier defaultValue ';'
    1279                          | qualifierList identifier defaultValue ';'
    1280                          """
    1281      if len(p) == 4:
    1282          id_ = p[1]
    1283          val = p[2]
    1284          quals = []
    1285      else:
    1286          quals = p[1]
    1287          id_ = p[2]
    1288          val = p[3]
    1289      p[0] = (quals, id_, val)
    1290
    1291 -def p_booleanValue(p):
    1292      """booleanValue : FALSE
    1293                      | TRUE
    1294                      """
    1295      p[0] = p[1].lower() == 'true'
    1296
    1297 -def p_nullValue(p):
    1298      """nullValue : NULL"""
    1299      p[0] = None
    1300
    1301 -def p_identifier(p):
    1302      """identifier : IDENTIFIER
    1303                    | ANY
    1304                    | AS
    1305                    | CLASS
    1306                    | DISABLEOVERRIDE
    1307                    | dataType
    1308                    | ENABLEOVERRIDE
    1309                    | FLAVOR
    1310                    | INSTANCE
    1311                    | METHOD
    1312                    | OF
    1313                    | PARAMETER
    1314                    | PRAGMA
    1315                    | PROPERTY
    1316                    | QUALIFIER
    1317                    | REFERENCE
    1318                    | RESTRICTED
    1319                    | SCHEMA
    1320                    | SCOPE
    1321                    | TOSUBCLASS
    1322                    | TRANSLATABLE
    1323                    """
    1324                    #| ASSOCIATION
    1325                    #| INDICATION
    1326      p[0] = p[1]
    1327
    1328 -def p_empty(p):
    1329      'empty :'
    1330      pass
    1331
    1332 -def _find_column(input, token):
    1333      i = token.lexpos
    1334      while i > 0:
    1335          if input[i] == '\n':
    1336              break
    1337          i -= 1
    1338      column = (token.lexpos - i)+1
    1339      return column
    1340
    1341 -def _get_error_context(input, token):
    1342      try:
    1343          line = input[token.lexpos : input.index('\n', token.lexpos)]
    1344      except ValueError:
    1345          line = input[token.lexpos:]
    1346      i = input.rfind('\n', 0, token.lexpos)
    1347      if i < 0:
    1348          i = 0
    1349      line = input[i:token.lexpos] + line
    1350      lines = [line.strip('\r\n')]
    1351      col = token.lexpos - i
    1352      while len(lines) < 5 and i > 0:
    1353          end = i
    1354          i = input.rfind('\n', 0, i)
    1355          if i < 0:
    1356              i = 0
    1357          lines.insert(0, input[i:end].strip('\r\n'))
    1358      pointer = ''
    1359      for ch in token.value:
    1360          pointer += '^'
    1361      pointline = ''
    1362      i = 0
    1363      while i < col -1:
    1364          if lines[-1][i].isspace():
    1365              pointline += lines[-1][i]
    1366              # otherwise, tabs complicate the alignment
    1367          else:
    1368              pointline += ' '
    1369          i += 1
    1370      lines.append(pointline + pointer)
    1371      return lines
    1372
    1373 -def _print_logger(str):
    1374      print str
    1375
    1376
    1377 -class MOFWBEMConnection(object):
    1378 -    def __init__(self, conn=None):
    1379          self.conn = conn
    1380          self.class_names = {}
    1381          self.qualifiers = {}
    1382          self.instances = {}
    1383          self.classes = {}
    1384          if conn is None:
    1385              self.__default_namespace = 'root/cimv2'
    1386
    1387 -    def setns(self, value):
    1388          if self.conn is not None:
    1389              self.conn.default_namespace = value
    1390          else:
    1391              self.__default_namespace = value
    1392
    1393 -    def getns(self):
    1394          if self.conn is not None:
    1395              return self.conn.default_namespace
    1396          else:
    1397              return self.__default_namespace
    1398
    1399      default_namespace = property(getns, setns, None,
    1400                                   "default_namespace property")
    1401
    1402 -    def GetClass(self, *args, **kwargs):
    1403          cname = len(args) > 0 and args[0] or kwargs['ClassName']
    1404          try:
    1405              cc = self.classes[self.default_namespace][cname]
    1406          except KeyError:
    1407              if self.conn is None:
    1408                  ce = CIMError(CIM_ERR_NOT_FOUND, cname)
    1409                  raise ce
    1410              cc = self.conn.GetClass(*args, **kwargs)
    1411              try:
    1412                  self.classes[self.default_namespace][cc.classname] = cc
    1413              except KeyError:
    1414                  self.classes[self.default_namespace] = \
    1415                          NocaseDict({cc.classname:cc})
    1416          if 'LocalOnly' in kwargs and not kwargs['LocalOnly']:
    1417              if cc.superclass:
    1418                  try:
    1419                      del kwargs['ClassName']
    1420                  except KeyError:
    1421                      pass
    1422                  if len(args) > 0:
    1423                      args = args[1:]
    1424                  super_ = self.GetClass(cc.superclass, *args, **kwargs)
    1425                  for prop in super_.properties.values():
    1426                      if prop.name not in cc.properties:
    1427                          cc.properties[prop.name] = prop
    1428                  for meth in super_.methods.values():
    1429                      if meth.name not in cc.methods:
    1430                          cc.methods[meth.name] = meth
    1431          return cc
    1432
    1433 -    def CreateClass(self, *args, **kwargs):
    1434          cc = len(args) > 0 and args[0] or kwargs['NewClass']
    1435          if cc.superclass:
    1436              try:
    1437                  super_ = self.GetClass(cc.superclass, LocalOnly=True,
    1438                                         IncludeQualifiers=False)
    1439              except CIMError, ce:
    1440                  if ce.args[0] == CIM_ERR_NOT_FOUND:
    1441                      ce.args = (CIM_ERR_INVALID_SUPERCLASS, cc.superclass)
    1442                      raise
    1443                  else:
    1444                      raise
    1445
    1446          try:
    1447              self.classes[self.default_namespace][cc.classname] = cc
    1448          except KeyError:
    1449              self.classes[self.default_namespace] = \
    1450                      NocaseDict({cc.classname:cc})
    1451
    1452          # TODO: should we see if it exists first with
    1453          # self.conn.GetClass()?  Do we want to create a class
    1454          # that already existed?
    1455          try:
    1456              self.class_names[self.default_namespace].append(cc.classname)
    1457          except KeyError:
    1458              self.class_names[self.default_namespace] = [cc.classname]
    1459
    1460 -    def ModifyClass(self, *args, **kwargs):
    1461          raise CIMError(CIM_ERR_FAILED,
    1462                         'This should not happen!')
    1463
    1464 -    def ModifyInstance(self, *args, **kwargs):
    1465          raise CIMError(CIM_ERR_FAILED,
    1466                         'This should not happen!')
    1467
    1468 -    def GetQualifier(self, *args, **kwargs):
    1469          qualname = len(args) > 0 and args[0] or kwargs['QualifierName']
    1470          try:
    1471              qual = self.qualifiers[self.default_namespace][qualname]
    1472          except KeyError:
    1473              if self.conn is None:
    1474                  raise CIMError(CIM_ERR_NOT_FOUND, qualname)
    1475              qual = self.conn.GetQualifier(*args, **kwargs)
    1476          return qual
    1477
    1478 -    def SetQualifier(self, *args, **kwargs):
    1479          qual = len(args) > 0 and args[0] or kwargs['QualifierDeclaration']
    1480          try:
    1481              self.qualifiers[self.default_namespace][qual.name] = qual
    1482          except KeyError:
    1483              self.qualifiers[self.default_namespace] = \
    1484                      NocaseDict({qual.name:qual})
    1485
    1486 -    def EnumerateQualifiers(self, *args, **kwargs):
    1487          if self.conn is not None:
    1488              rv = self.conn.EnumerateQualifiers(*args, **kwargs)
    1489          else:
    1490              rv = []
    1491          try:
    1492              rv += self.qualifiers[self.default_namespace].values()
    1493          except KeyError:
    1494              pass
    1495          return rv
    1496
    1497
    1498 -    def CreateInstance(self, *args, **kwargs):
    1499          inst = len(args) > 0 and args[0] or kwargs['NewInstance']
    1500          try:
    1501              self.instances[self.default_namespace].append(inst)
    1502          except KeyError:
    1503              self.instances[self.default_namespace] = [inst]
    1504          return inst.path
    1505
    1506 -    def rollback(self, verbose=False):
    1507          for ns, insts in self.instances.items():
    1508              insts.reverse()
    1509              for inst in insts:
    1510                  try:
    1511                      if verbose:
    1512                          print 'Deleting instance', inst.path
    1513                      self.conn.DeleteInstance(inst.path)
    1514                  except CIMError, ce:
    1515                      print 'Error deleting instance', inst.path
    1516                      print '    ', '%s %s' % (ce.args[0], ce.args[1])
    1517          for ns, cnames in self.class_names.items():
    1518              self.default_namespace = ns
    1519              cnames.reverse()
    1520              for cname in cnames:
    1521                  try:
    1522                      if verbose:
    1523                          print 'Deleting class %s:%s' % (ns, cname)
    1524                      self.conn.DeleteClass(cname)
    1525                  except CIMError, ce:
    1526                      print 'Error deleting class %s:%s' % (ns, cname)
    1527                      print '    ', '%s %s' % (ce.args[0], ce.args[1])
    1528          # TODO: do we want to do anything with qualifiers?
    1529
    1530
    1531 -def _errcode2string(code):
    1532      d = {
    1533          CIM_ERR_FAILED                 : 'A general error occurred',
    1534          CIM_ERR_ACCESS_DENIED          : 'Resource not available',
    1535          CIM_ERR_INVALID_NAMESPACE      : 'The target namespace does not exist',
    1536          CIM_ERR_INVALID_PARAMETER      : 'Parameter value(s) invalid',
    1537          CIM_ERR_INVALID_CLASS          : 'The specified Class does not exist',
    1538          CIM_ERR_NOT_FOUND              : 'Requested object could not be found',
    1539          CIM_ERR_NOT_SUPPORTED          : 'Operation not supported',
    1540          CIM_ERR_CLASS_HAS_CHILDREN     : 'Class has subclasses',
    1541          CIM_ERR_CLASS_HAS_INSTANCES    : 'Class has instances',
    1542          CIM_ERR_INVALID_SUPERCLASS     : 'Superclass does not exist',
    1543          CIM_ERR_ALREADY_EXISTS         : 'Object already exists',
    1544          CIM_ERR_NO_SUCH_PROPERTY       : 'Property does not exist',
    1545          CIM_ERR_TYPE_MISMATCH          : 'Value incompatible with type',
    1546          CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED   : 'Query language not supported',
    1547          CIM_ERR_INVALID_QUERY          : 'Query not valid',
    1548          CIM_ERR_METHOD_NOT_AVAILABLE   : 'Extrinsic method not executed',
    1549          CIM_ERR_METHOD_NOT_FOUND       : 'Extrinsic method does not exist',
    1550          }
    1551      try:
    1552          s = d[code]
    1553      except KeyError:
    1554          s = 'Unknown Error'
    1555      return s
    1556
    1557 -class MOFCompiler(object):
    1558 -    def __init__(self, handle, search_paths=[], verbose=False,
    1559                   log_func=_print_logger):
    1560          """Initialize the compiler.
    1561
    1562          Keyword arguments:
    1563          handle -- A WBEMConnection or similar object.  The following
    1564              attributes and methods need to be present, corresponding to the
    1565              the attributes and methods on pywbem.WBEMConnection having
    1566              the same names:
    1567              - default_namespace
    1568              - EnumerateInstanceNames()
    1569              - CreateClass()
    1570              - GetClass()
    1571              - ModifyClass()
    1572              - DeleteInstance()
    1573              - CreateInstance()
    1574              - ModifyInstance()
    1575              - DeleteQualifier()
    1576              - EnumerateQualifiers()
    1577              - SetQualifier()
    1578          search_paths -- A list of file system paths specifying where
    1579              missing schema elements should be looked for.
    1580          verbose -- True if extra messages should be printed.
    1581          log_func -- A callable that takes a single string argument.
    1582              The default logger prints to stdout.
    1583          """
    1584
    1585          self.parser = yacc.yacc(tabmodule=_tabmodule, optimize=_optimize)
    1586          self.parser.search_paths = search_paths
    1587          self.handle = handle
    1588          self.parser.handle = handle
    1589          self.lexer = lex.lex(lextab=_lextab, optimize=_optimize)
    1590          self.lexer.parser = self.parser
    1591          self.parser.qualcache = {handle.default_namespace: NocaseDict()}
    1592          self.parser.classnames = {handle.default_namespace: []}
    1593          self.parser.mofcomp = self
    1594          self.parser.verbose = verbose
    1595          self.parser.log = log_func
    1596          self.parser.aliases = {}
    1597
    1598 -    def compile_string(self, mof, ns, filename=None):
    1599          """Compile a string of MOF.
    1600
    1601          Arguments:
    1602          mof -- The string of MOF
    1603          ns -- The CIM namespace
    1604
    1605          Keyword arguments:
    1606          filename -- The name of the file that the MOF was read from.  This
    1607              is used in status and error messages.
    1608          """
    1609
    1610          lexer = self.lexer.clone()
    1611          lexer.parser = self.parser
    1612          try:
    1613              oldfile = self.parser.file
    1614          except AttributeError:
    1615              oldfile = None
    1616          self.parser.file = filename
    1617          try:
    1618              oldmof = self.parser.mof
    1619          except AttributeError:
    1620              oldmof = None
    1621          self.parser.mof = mof
    1622          self.parser.handle.default_namespace = ns
    1623          if ns not in self.parser.qualcache:
    1624              self.parser.qualcache[ns] = NocaseDict()
    1625          if ns not in self.parser.classnames:
    1626              self.parser.classnames[ns] = []
    1627          try:
    1628              rv = self.parser.parse(mof, lexer=lexer)
    1629              self.parser.file = oldfile
    1630              self.parser.mof = oldmof
    1631              return rv
    1632          except MOFParseError, pe:
    1633              self.parser.log('Syntax error:')
    1634              if hasattr(pe, 'file') and hasattr(pe, 'lineno'):
    1635                  self.parser.log('%s:%s:' % (pe.file, pe.lineno))
    1636              if hasattr(pe, 'context'):
    1637                  self.parser.log('\n'.join(pe.context))
    1638              if str(pe):
    1639                  self.parser.log(str(pe))
    1640              raise
    1641          except CIMError, ce:
    1642              if hasattr(ce, 'file_line'):
    1643                  self.parser.log('Fatal Error: %s:%s' % (ce.file_line[0],
    1644                                                          ce.file_line[1]))
    1645              else:
    1646                  self.parser.log('Fatal Error:')
    1647              self.parser.log('%s%s' % (_errcode2string(ce.args[0]),
    1648                                        ce.args[1] and ': '+ce.args[1] or ''))
    1649              raise
    1650
    1651 -    def compile_file(self, filename, ns):
    1652          """Compile MOF from a file.
    1653
    1654          Arguments:
    1655          filename -- The file to read MOF from
    1656          ns -- The CIM namespace
    1657          """
    1658
    1659          if self.parser.verbose:
    1660              self.parser.log('Compiling file ' + filename)
    1661          f = open(filename, 'r')
    1662          mof = f.read()
    1663          f.close()
    1664
    1665          return self.compile_string(mof, ns, filename=filename)
    1666
    1667 -    def find_mof(self, classname):
    1668          """Find a MOF file corresponding to a CIM class name.  The search_paths
    1669          provided to __init__() are searched recursively.
    1670
    1671          Arguments:
    1672          classname -- The name of the class to look for
    1673          """
    1674
    1675          classname = classname.lower()
    1676          for search in self.parser.search_paths:
    1677              for root, dirs, files in os.walk(search):
    1678                  for file_ in files:
    1679                      if file_.endswith('.mof') and \
    1680                              file_[:-4].lower() == classname:
    1681                          return root + '/' + file_
    1682          return None
    1683
    1684 -    def rollback(self, verbose=False):
    1685          self.handle.rollback(verbose=verbose)
    1686
    1687 -def _build():
    1688      yacc.yacc(optimize=_optimize, tabmodule=_tabmodule, outputdir=_outputdir)
    1689      lex.lex(optimize=_optimize, lextab=_lextab, outputdir=_outputdir)
    1690
    1691
    1692  if __name__ == '__main__':
    1693      from optparse import OptionParser
    1694      usage = 'usage: %prog -n <namespace> [options] <MOF file> ...'
    1695      oparser = OptionParser(usage=usage)
    1696      oparser.add_option('-s', '--search', dest='search',
    1697                         help='Search path to find missing schema elements.  ' \
    1698                              'This option can be present multiple times.',
    1699                         metavar='Path', action='append')
    1700      oparser.add_option('-n', '--namespace', dest='ns',
    1701                         help='Specify the namespace', metavar='Namespace')
    1702      oparser.add_option('-u', '--url', dest='url',
    1703                         help='URL to the CIM Server', metavar='URL',
    1704                         default='/var/run/tog-pegasus/cimxml.socket')
    1705      oparser.add_option('-v', '--verbose',
    1706                         action='store_true', dest='verbose', default=False,
    1707                         help='Print more messages to stdout')
    1708      oparser.add_option('-r', '--remove',
    1709                         action='store_true', dest='remove', default=False,
    1710                         help='Remove elements found in MOF, instead of ' \
    1711                              'create them')
    1712      oparser.add_option('-l', '--username',
    1713                         dest='username', metavar='Username',
    1714                         help='Specify the username')
    1715      oparser.add_option('-p', '--password',
    1716                         dest='password', metavar='Password',
    1717                         help='Specify the password')
    1718      oparser.add_option('-d', '--dry-run',
    1719                         dest='dry_run', default=False, action='store_true',
    1720                         help="Don't actually modify the repository, just " \
    1721                              "check mof file syntax. Connection to CIMOM is " \
    1722                              "still required to check qualifiers.")
    1723
    1724      (options, args) = oparser.parse_args()
    1725      search = options.search
    1726      if not args:
    1727          oparser.error('No input files given for parsing')
    1728      if options.ns is None:
    1729          oparser.error('No namespace given')
    1730
    1731      passwd = options.password
    1732      if options.username and not passwd:
    1733          passwd = getpass('Enter password for %s: ' % options.username)
    1734      if options.username:
    1735          conn = WBEMConnection(options.url, (options.username, passwd))
    1736      else:
    1737          conn = WBEMConnection(options.url)
    1738      if options.remove or options.dry_run:
    1739          conn = MOFWBEMConnection(conn=conn)
    1740      #conn.debug = True
    1741      conn.default_namespace = options.ns
    1742      if search is None:
    1743          search = []
    1744
    1745      search = [os.path.abspath(x) for x in search]
    1746      for fname in args:
    1747          path = os.path.abspath(os.path.dirname(fname))
    1748          for spath in search:
    1749              if path.startswith(spath):
    1750                  break
    1751          else:
    1752              search.append(path)
    1753
    1754      # if removing, we'll be verbose later when we actually remove stuff.
    1755      # We don't want MOFCompiler to be verbose, as that would be confusing.
    1756      verbose = options.verbose and not options.remove
    1757
    1758      mofcomp = MOFCompiler(handle=conn, search_paths=search,
    1759                            verbose=verbose)
    1760
    1761      try:
    1762          for fname in args:
    1763              if fname[0] != '/':
    1764                  fname = os.path.curdir + '/' + fname
    1765              mofcomp.compile_file(fname, options.ns)
    1766      except MOFParseError, pe:
    1767          sys.exit(1)
    1768      except CIMError, ce:
    1769          sys.exit(1)
    1770
    1771      if options.remove and not options.dry_run:
    1772          conn.rollback(verbose=options.verbose)
    1773