pywbem-0.7.0/0000755000175000001440000000000011120521334011660 5ustar bartuserspywbem-0.7.0/__init__.py0000644000175000001440000000300011102360476013772 0ustar bartusers# Package init file for pywbem # # (C) Copyright 2004,2006 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter # Martin Pool """pywbem - WBEM client bindings for Python. PyWBEM is a Python library for making CIM operations over HTTP using the CIM-XML protocol. It is based on the idea that a good WBEM client should be easy to use and not necessarily require a large amount of programming knowlege. PyWBEM is suitable for a large range of tasks from simply poking around to writing web and GUI applications. """ # There are submodules, but clients shouldn't need to know about them. # Importing just this module is enough. # These are explicitly safe for 'import *' from cim_types import * from cim_constants import * from cim_operations import * from cim_obj import * from tupleparse import ParseError pywbem-0.7.0/twisted_client.py0000644000175000001440000005402311102360476015267 0ustar bartusers# # (C) Copyright 2005,2007 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter """pywbem.twisted - WBEM client bindings for Twisted Python. This module contains factory classes that produce WBEMClient instances that perform WBEM requests over HTTP using the twisted.protocols.http.HTTPClient base class. """ from twisted.internet import reactor, protocol, defer from twisted.web import http, client, error from pywbem import CIMClass, CIMClassName, CIMInstance, CIMInstanceName, CIMError, cim_types, cim_xml try: from elementtree.ElementTree import fromstring, tostring except ImportError, arg: from xml.etree.ElementTree import fromstring, tostring import string from types import StringTypes from datetime import datetime, timedelta class WBEMClient(http.HTTPClient): """A HTTPClient subclass that handles WBEM requests.""" status = None def connectionMade(self): """Send a HTTP POST command with the appropriate CIM over HTTP headers and payload.""" self.factory.request_xml = str(self.factory.payload) self.sendCommand('POST', '/cimom') self.sendHeader('Host', '%s:%d' % (self.transport.addr[0], self.transport.addr[1])) self.sendHeader('User-Agent', 'pywbem/twisted') self.sendHeader('Content-length', len(self.factory.payload)) self.sendHeader('Content-type', 'application/xml') import base64 auth = base64.encodestring('%s:%s' % (self.factory.creds[0], self.factory.creds[1]))[:-1] self.sendHeader('Authorization', 'Basic %s' % auth) self.sendHeader('CIMOperation', str(self.factory.operation)) self.sendHeader('CIMMethod', str(self.factory.method)) self.sendHeader('CIMObject', str(self.factory.object)) self.endHeaders() # TODO: Figure out why twisted doesn't support unicode. An # exception should be thrown by the str() call if the payload # can't be converted to the current codepage. self.transport.write(str(self.factory.payload)) def handleResponse(self, data): """Called when all response data has been received.""" self.factory.response_xml = data if self.status == '200': self.factory.parseErrorAndResponse(data) self.factory.deferred = None self.transport.loseConnection() def handleStatus(self, version, status, message): """Save the status code for processing when we get to the end of the headers.""" self.status = status self.message = message def handleHeader(self, key, value): """Handle header values.""" import urllib if key == 'CIMError': self.CIMError = urllib.unquote(value) if key == 'PGErrorDetail': self.PGErrorDetail = urllib.unquote(value) def handleEndHeaders(self): """Check whether the status was OK and raise an error if not using previously saved header information.""" if self.status != '200': if not hasattr(self, 'cimerror') or \ not hasattr(self, 'errordetail'): self.factory.deferred.errback( CIMError(0, 'HTTP error %s: %s' % (self.status, self.message))) else: self.factory.deferred.errback( CIMError(0, '%s: %s' % (cimerror, errordetail))) class WBEMClientFactory(protocol.ClientFactory): """Create instances of the WBEMClient class.""" request_xml = None response_xml = None xml_header = '' def __init__(self, creds, operation, method, object, payload): self.creds = creds self.operation = operation self.method = method self.object = object self.payload = payload self.protocol = lambda: WBEMClient() self.deferred = defer.Deferred() def clientConnectionFailed(self, connector, reason): if self.deferred is not None: reactor.callLater(0, self.deferred.errback, reason) def clientConnectionLost(self, connector, reason): if self.deferred is not None: reactor.callLater(0, self.deferred.errback, reason) def imethodcallPayload(self, methodname, localnsp, **kwargs): """Generate the XML payload for an intrinsic methodcall.""" param_list = [pywbem.IPARAMVALUE(x[0], pywbem.tocimxml(x[1])) for x in kwargs.items()] payload = pywbem.CIM( pywbem.MESSAGE( pywbem.SIMPLEREQ( pywbem.IMETHODCALL( methodname, pywbem.LOCALNAMESPACEPATH( [pywbem.NAMESPACE(ns) for ns in string.split(localnsp, '/')]), param_list)), '1001', '1.0'), '2.0', '2.0') return self.xml_header + payload.toxml() def methodcallPayload(self, methodname, obj, namespace, **kwargs): """Generate the XML payload for an extrinsic methodcall.""" if isinstance(obj, CIMInstanceName): path = obj.copy() path.host = None path.namespace = None localpath = pywbem.LOCALINSTANCEPATH( pywbem.LOCALNAMESPACEPATH( [pywbem.NAMESPACE(ns) for ns in string.split(namespace, '/')]), path.tocimxml()) else: localpath = pywbem.LOCALCLASSPATH( pywbem.LOCALNAMESPACEPATH( [pywbem.NAMESPACE(ns) for ns in string.split(namespace, '/')]), obj) def paramtype(obj): """Return a string to be used as the CIMTYPE for a parameter.""" if isinstance(obj, cim_types.CIMType): return obj.cimtype elif type(obj) == bool: return 'boolean' elif isinstance(obj, StringTypes): return 'string' elif isinstance(obj, (datetime, timedelta)): return 'datetime' elif isinstance(obj, (CIMClassName, CIMInstanceName)): return 'reference' elif isinstance(obj, (CIMClass, CIMInstance)): return 'string' elif isinstance(obj, list): return paramtype(obj[0]) raise TypeError('Unsupported parameter type "%s"' % type(obj)) def paramvalue(obj): """Return a cim_xml node to be used as the value for a parameter.""" if isinstance(obj, (datetime, timedelta)): obj = CIMDateTime(obj) if isinstance(obj, (cim_types.CIMType, bool, StringTypes)): return cim_xml.VALUE(cim_types.atomic_to_cim_xml(obj)) if isinstance(obj, (CIMClassName, CIMInstanceName)): return cim_xml.VALUE_REFERENCE(obj.tocimxml()) if isinstance(obj, (CIMClass, CIMInstance)): return cim_xml.VALUE(obj.tocimxml().toxml()) if isinstance(obj, list): if isinstance(obj[0], (CIMClassName, CIMInstanceName)): return cim_xml.VALUE_REFARRAY([paramvalue(x) for x in obj]) return cim_xml.VALUE_ARRAY([paramvalue(x) for x in obj]) raise TypeError('Unsupported parameter type "%s"' % type(obj)) param_list = [pywbem.PARAMVALUE(x[0], paramvalue(x[1]), paramtype(x[1])) for x in kwargs.items()] payload = pywbem.CIM( pywbem.MESSAGE( pywbem.SIMPLEREQ( pywbem.METHODCALL(methodname, localpath, param_list)), '1001', '1.0'), '2.0', '2.0') return self.xml_header + payload.toxml() def parseErrorAndResponse(self, data): """Parse returned XML for errors, then convert into appropriate Python objects.""" xml = fromstring(data) error = xml.find('.//ERROR') if error is None: self.deferred.callback(self.parseResponse(xml)) return try: code = int(error.attrib['CODE']) except ValueError: code = 0 self.deferred.errback(CIMError(code, error.attrib['DESCRIPTION'])) def parseResponse(self, xml): """Parse returned XML and convert into appropriate Python objects. Override in subclass""" pass # TODO: Eww - we should get rid of the tupletree, tupleparse modules # and replace with elementtree based code. import pywbem.tupletree class EnumerateInstances(WBEMClientFactory): """Factory to produce EnumerateInstances WBEM clients.""" def __init__(self, creds, classname, namespace = 'root/cimv2', **kwargs): self.classname = classname self.namespace = namespace payload = self.imethodcallPayload( 'EnumerateInstances', namespace, ClassName = CIMClassName(classname), **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'EnumerateInstances', object = namespace, payload = payload) def __repr__(self): return '<%s(/%s:%s) at 0x%x>' % \ (self.__class__, self.namespace, self.classname, id(self)) def parseResponse(self, xml): tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//VALUE.NAMEDINSTANCE')] return [pywbem.tupleparse.parse_value_namedinstance(x) for x in tt] class EnumerateInstanceNames(WBEMClientFactory): """Factory to produce EnumerateInstanceNames WBEM clients.""" def __init__(self, creds, classname, namespace = 'root/cimv2', **kwargs): self.classname = classname self.namespace = namespace payload = self.imethodcallPayload( 'EnumerateInstanceNames', namespace, ClassName = CIMClassName(classname), **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'EnumerateInstanceNames', object = namespace, payload = payload) def __repr__(self): return '<%s(/%s:%s) at 0x%x>' % \ (self.__class__, self.namespace, self.classname, id(self)) def parseResponse(self, xml): tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//INSTANCENAME')] names = [pywbem.tupleparse.parse_instancename(x) for x in tt] [setattr(n, 'namespace', self.namespace) for n in names] return names class GetInstance(WBEMClientFactory): """Factory to produce GetInstance WBEM clients.""" def __init__(self, creds, instancename, namespace = 'root/cimv2', **kwargs): self.instancename = instancename self.namespace = namespace payload = self.imethodcallPayload( 'GetInstance', namespace, InstanceName = instancename, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'GetInstance', object = namespace, payload = payload) def __repr__(self): return '<%s(/%s:%s) at 0x%x>' % \ (self.__class__, self.namespace, self.instancename, id(self)) def parseResponse(self, xml): tt = pywbem.tupletree.xml_to_tupletree( tostring(xml.find('.//INSTANCE'))) return pywbem.tupleparse.parse_instance(tt) class DeleteInstance(WBEMClientFactory): """Factory to produce DeleteInstance WBEM clients.""" def __init__(self, creds, instancename, namespace = 'root/cimv2', **kwargs): self.instancename = instancename self.namespace = namespace payload = self.imethodcallPayload( 'DeleteInstance', namespace, InstanceName = instancename, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'DeleteInstance', object = namespace, payload = payload) def __repr__(self): return '<%s(/%s:%s) at 0x%x>' % \ (self.__class__, self.namespace, self.instancename, id(self)) class CreateInstance(WBEMClientFactory): """Factory to produce CreateInstance WBEM clients.""" # TODO: Implement __repr__ method def __init__(self, creds, instance, namespace = 'root/cimv2', **kwargs): payload = self.imethodcallPayload( 'CreateInstance', namespace, NewInstance = instance, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'CreateInstance', object = namespace, payload = payload) def parseResponse(self, xml): tt = pywbem.tupletree.xml_to_tupletree( tostring(xml.find('.//INSTANCENAME'))) return pywbem.tupleparse.parse_instancename(tt) class ModifyInstance(WBEMClientFactory): """Factory to produce ModifyInstance WBEM clients.""" # TODO: Implement __repr__ method def __init__(self, creds, instancename, instance, namespace = 'root/cimv2', **kwargs): wrapped_instance = CIMNamedInstance(instancename, instance) payload = self.imethodcallPayload( 'ModifyInstance', namespace, ModifiedInstance = wrapped_instance, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'ModifyInstance', object = namespace, payload = payload) class EnumerateClassNames(WBEMClientFactory): """Factory to produce EnumerateClassNames WBEM clients.""" def __init__(self, creds, namespace = 'root/cimv2', **kwargs): self.localnsp = LocalNamespacePath payload = self.imethodcallPayload( 'EnumerateClassNames', namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'EnumerateClassNames', object = LocalNamespacePath, payload = payload) def __repr__(self): return '<%s(/%s) at 0x%x>' % \ (self.__class__, self.namespace, id(self)) def parseResponse(self, xml): tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//CLASSNAME')] return [pywbem.tupleparse.parse_classname(x) for x in tt] class EnumerateClasses(WBEMClientFactory): """Factory to produce EnumerateClasses WBEM clients.""" def __init__(self, creds, namespace = 'root/cimv2', **kwargs): self.localnsp = LocalNamespacePath payload = self.imethodcallPayload( 'EnumerateClasses', namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'EnumerateClasses', object = namespace, payload = payload) def __repr__(self): return '<%s(/%s) at 0x%x>' % \ (self.__class__, self.namespace, id(self)) def parseResponse(self, xml): tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//CLASS')] return [pywbem.tupleparse.parse_class(x) for x in tt] class GetClass(WBEMClientFactory): """Factory to produce GetClass WBEM clients.""" def __init__(self, creds, classname, namespace = 'root/cimv2', **kwargs): self.classname = classname self.namespace = namespace payload = self.imethodcallPayload( 'GetClass', namespace, ClassName = CIMClassName(classname), **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'GetClass', object = namespace, payload = payload) def __repr__(self): return '<%s(/%s:%s) at 0x%x>' % \ (self.__class__, self.namespace, self.classname, id(self)) def parseResponse(self, xml): tt = pywbem.tupletree.xml_to_tupletree( tostring(xml.find('.//CLASS'))) return pywbem.tupleparse.parse_class(tt) class Associators(WBEMClientFactory): """Factory to produce Associators WBEM clients.""" # TODO: Implement __repr__ method def __init__(self, creds, obj, namespace = 'root/cimv2', **kwargs): if isinstance(obj, CIMInstanceName): kwargs['ObjectName'] = obj else: kwargs['ObjectName'] = CIMClassName(obj) payload = self.imethodcallPayload( 'Associators', namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'Associators', object = namespace, payload = payload) class AssociatorNames(WBEMClientFactory): """Factory to produce AssociatorNames WBEM clients.""" # TODO: Implement __repr__ method def __init__(self, creds, obj, namespace = 'root/cimv2', **kwargs): if isinstance(obj, CIMInstanceName): kwargs['ObjectName'] = obj else: kwargs['ObjectName'] = CIMClassName(obj) payload = self.imethodcallPayload( 'AssociatorNames', namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'AssociatorNames', object = namespace, payload = payload) def parseResponse(self, xml): if len(xml.findall('.//INSTANCENAME')) > 0: tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//INSTANCENAME')] return [pywbem.tupleparse.parse_instancename(x) for x in tt] else: tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//OBJECTPATH')] return [pywbem.tupleparse.parse_objectpath(x)[2] for x in tt] class References(WBEMClientFactory): """Factory to produce References WBEM clients.""" def __init__(self, creds, obj, namespace = 'root/cimv2', **kwargs): if isinstance(obj, CIMInstanceName): kwargs['ObjectName'] = obj else: kwargs['ObjectName'] = CIMClassName(obj) payload = self.imethodcallPayload( 'References', namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'References', object = namespace, payload = payload) class ReferenceNames(WBEMClientFactory): """Factory to produce ReferenceNames WBEM clients.""" # TODO: Implement __repr__ method def __init__(self, creds, obj, namespace = 'root/cimv2', **kwargs): if isinstance(obj, CIMInstanceName): kwargs['ObjectName'] = obj else: kwargs['ObjectName'] = CIMClassName(obj) payload = self.imethodcallPayload( 'ReferenceNames', namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = 'ReferenceNames', object = namespace, payload = payload) def parseResponse(self, xml): if len(xml.findall('.//INSTANCENAME')) > 0: tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//INSTANCENAME')] return [pywbem.tupleparse.parse_instancename(x) for x in tt] else: tt = [pywbem.tupletree.xml_to_tupletree(tostring(x)) for x in xml.findall('.//OBJECTPATH')] return [pywbem.tupleparse.parse_objectpath(x)[2] for x in tt] class InvokeMethod(WBEMClientFactory): """Factory to produce InvokeMethod WBEM clients.""" def __init__(self, creds, MethodName, ObjectName, namespace = 'root/cimv2', **kwargs): # Convert string to CIMClassName obj = ObjectName if isinstance(obj, StringTypes): obj = CIMClassName(obj, namespace = namespace) if isinstance(obj, CIMInstanceName) and obj.namespace is None: obj = ObjectName.copy() obj.namespace = namespace # Make the method call payload = self.methodcallPayload( MethodName, obj, namespace, **kwargs) WBEMClientFactory.__init__( self, creds, operation = 'MethodCall', method = MethodName, object = obj, payload = payload) pywbem-0.7.0/README0000644000175000001440000000031010734021565012545 0ustar bartusersPyWBEM is a pure-Python library for performing operations using the WBEM management protocol. Please see our web site at http://pywbem.sf.net for more details. It is licensed under the GNU LGPL v2. pywbem-0.7.0/tupletree.py0000644000175000001440000000603611102360476014260 0ustar bartusers# # (C) Copyright 2003, 2004 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Martin Pool """ tupletree - Convert XML DOM objects to and from tuple trees. DOM is the standard in-memory representation of XML documents, but it is very cumbersome for some types of processing where XML encodes object structures rather than text documents. Direct mapping to Python classes may not be a good match either. tupletrees may be created from an in-memory DOM using dom_to_tupletree(), or from a string using xml_to_tupletree(). Since the Python XML libraries deal mostly with Unicode strings they are also returned here. If plain Strings are passed in they will be converted by xmldom. Each node of the tuple tree is a Python 4-tuple, corresponding to an XML Element (i.e. ): (NAME, ATTRS, CONTENTS, None) The NAME is the name of the element. The ATTRS are a name-value hash of element attributes. The CONTENTS is a list of child elements. The fourth element is reserved. """ def dom_to_tupletree(node): """Convert a DOM object to a pyRXP-style tuple tree. Each element is a 4-tuple of (NAME, ATTRS, CONTENTS, None). Very nice for processing complex nested trees. """ import types if node.nodeType == node.DOCUMENT_NODE: # boring; pop down one level return dom_to_tupletree(node.firstChild) assert node.nodeType == node.ELEMENT_NODE name = node.nodeName attrs = {} contents = [] for child in node.childNodes: if child.nodeType == child.ELEMENT_NODE: contents.append(dom_to_tupletree(child)) elif child.nodeType == child.TEXT_NODE: assert isinstance(child.nodeValue, types.StringTypes), \ "text node %s is not a string" % `child` contents.append(child.nodeValue) else: raise RuntimeError("can't handle %s" % child) for i in range(node.attributes.length): attr_node = node.attributes.item(i) attrs[attr_node.nodeName] = attr_node.nodeValue # XXX: Cannot yet handle comments, cdata, processing instructions and # other XML batshit. # it's so easy in retrospect! return (name, attrs, contents, None) def xml_to_tupletree(xml_string): """Parse XML straight into tupletree.""" import xml.dom.minidom dom_xml = xml.dom.minidom.parseString(xml_string) return dom_to_tupletree(dom_xml) pywbem-0.7.0/cim_constants.py0000644000175000001440000000440011102360476015104 0ustar bartusers# # (C) Copyright 2003, 2004 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter """Useful CIM constants.""" # CIMError error code constants CIM_ERR_FAILED = 1 # A general error occurred CIM_ERR_ACCESS_DENIED = 2 # Resource not available CIM_ERR_INVALID_NAMESPACE = 3 # The target namespace does not exist CIM_ERR_INVALID_PARAMETER = 4 # Parameter value(s) invalid CIM_ERR_INVALID_CLASS = 5 # The specified Class does not exist CIM_ERR_NOT_FOUND = 6 # Requested object could not be found CIM_ERR_NOT_SUPPORTED = 7 # Operation not supported CIM_ERR_CLASS_HAS_CHILDREN = 8 # Class has subclasses CIM_ERR_CLASS_HAS_INSTANCES = 9 # Class has instances CIM_ERR_INVALID_SUPERCLASS = 10 # Superclass does not exist CIM_ERR_ALREADY_EXISTS = 11 # Object already exists CIM_ERR_NO_SUCH_PROPERTY = 12 # Property does not exist CIM_ERR_TYPE_MISMATCH = 13 # Value incompatible with type CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED = 14 # Query language not supported CIM_ERR_INVALID_QUERY = 15 # Query not valid CIM_ERR_METHOD_NOT_AVAILABLE = 16 # Extrinsic method not executed CIM_ERR_METHOD_NOT_FOUND = 17 # Extrinsic method does not exist # Provider types PROVIDERTYPE_CLASS = 1 PROVIDERTYPE_INSTANCE = 2 PROVIDERTYPE_ASSOCIATION = 3 PROVIDERTYPE_INDICATION = 4 PROVIDERTYPE_METHOD = 5 PROVIDERTYPE_CONSUMER = 6 # Indication consumer PROVIDERTYPE_QUERY = 7 pywbem-0.7.0/cim_obj.py0000644000175000001440000013630711102360476013656 0ustar bartusers# # (C) Copyright 2003-2007 Hewlett-Packard Development Company, L.P. # (C) Copyright 2006-2007 Novell, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter # Martin Pool # Bart Whiteley import string, re import cim_xml, cim_types from types import StringTypes from cim_types import atomic_to_cim_xml, CIMDateTime from cim_xml import * from datetime import datetime, timedelta """ Representations of CIM Objects. In general we try to map CIM objects directly into Python primitives, except when that is not possible or would be ambiguous. For example, CIM Class names are simply Python strings, but a ClassPath is represented as a special Python object. These objects can also be mapped back into XML, by the toxml() method which returns a string. """ class NocaseDict(object): """Yet another implementation of a case-insensitive dictionary.""" def __init__(self, *args, **kwargs): self.data = {} # Initialise from sequence object if len(args) == 1 and type(args[0]) == list: for item in args[0]: self[item[0]] = item[1] # Initialise from mapping object if len(args) == 1 and type(args[0]) == dict: self.update(args[0]) # Initialise from NocaseDict if len(args) == 1 and isinstance(args[0], NocaseDict): self.data = args[0].data.copy() # Initialise from keyword args self.update(kwargs) # Basic accessor and settor methods def __getitem__(self, key): k = key if isinstance(key, (str, unicode)): k = key.lower() return self.data[k][1] def __setitem__(self, key, value): if not isinstance(key, (str, unicode)): raise KeyError, 'Key must be string type' k = key.lower() self.data[k] = (key, value) def __delitem__(self, key): k = key if isinstance(key, (str, unicode)): k = key.lower() del self.data[k] def __len__(self): return len(self.data) def has_key(self, key): k = key if isinstance(key, (str, unicode)): k = key.lower() return self.data.has_key(k) def __contains__(self, key): k = key if isinstance(key, (str, unicode)): k = key.lower() return k in self.data def get(self, key, default = None): try: return self[key] except KeyError: return default def setdefault(self, key, default): if not self.has_key(key): self[key] = default return self[key] # Other accessor expressed in terms of iterators def keys(self): return list(self.iterkeys()) def values(self): return list(self.itervalues()) def items(self): return list(self.iteritems()) # Iterators def iterkeys(self): for item in self.data.iteritems(): yield item[1][0] def itervalues(self): for item in self.data.iteritems(): yield item[1][1] def iteritems(self): for item in self.data.iteritems(): yield item[1] # Other stuff def __repr__(self): items = ', '.join([('%r: %r' % (key, value)) for key, value in self.items()]) return 'NocaseDict({%s})' % items def update(self, *args, **kwargs): for mapping in args: if hasattr(mapping, 'items'): for k, v in mapping.items(): self[k] = v else: for (k, v) in mapping: self[k] = v for k, v in kwargs.items(): self[k] = v def clear(self): self.data.clear() def popitem(self): pass def copy(self): result = NocaseDict() result.data = self.data.copy() return result def __eq__(self, other): for key, value in self.iteritems(): if not (key in other) or not (other[key] == value): return 0 return len(self) == len(other) def __cmp__(self, other): for key, value in self.iteritems(): if not (key in other): return -1 rv = cmp(value, other[key]) if rv != 0: return rv return len(self) - len(other) def cmpname(name1, name2): """Compare to CIM names. The comparison is done case-insensitvely, and one or both of the names may be None.""" if name1 is None and name2 is None: return 0 if name1 is None: return -1 if name2 is None: return 1 lower_name1 = name1.lower() lower_name2 = name2.lower() return cmp(lower_name1, lower_name2) class CIMClassName(object): def __init__(self, classname, host = None, namespace = None): if not isinstance(classname, StringTypes): raise TypeError('classname argument must be a string') # TODO: There are some odd restrictions on what a CIM # classname can look like (i.e must start with a # non-underscore and only one underscore per classname). self.classname = classname self.host = host self.namespace = namespace def copy(self): return CIMClassName(self.classname, host = self.host, namespace = self.namespace) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMClassName): return 1 return (cmpname(self.classname, other.classname) or cmpname(self.host, other.host) or cmpname(self.namespace, other.namespace)) def __str__(self): s = '' if self.host is not None: s += '//%s/' % self.host if self.namespace is not None: s += '%s:' % self.namespace s += self.classname return s def __repr__(self): r = '%s(classname=%s' % (self.__class__.__name__, `self.classname`) if self.host is not None: r += ', host=%s' % `self.host` if self.namespace is not None: r += ', namespace=%s' % `self.namespace` r += ')' return r def tocimxml(self): classname = cim_xml.CLASSNAME(self.classname) if self.namespace is not None: localnsp = cim_xml.LOCALNAMESPACEPATH( [cim_xml.NAMESPACE(ns) for ns in string.split(self.namespace, '/')]) if self.host is not None: # Classname + namespace + host = CLASSPATH return cim_xml.CLASSPATH( cim_xml.NAMESPACEPATH(cim_xml.HOST(self.host), localnsp), classname) # Classname + namespace = LOCALCLASSPATH return cim_xml.LOCALCLASSPATH(localnsp, classname) # Just classname = CLASSNAME return cim_xml.CLASSNAME(self.classname) class CIMProperty(object): """A property of a CIMInstance. Property objects represent both properties on particular instances, and the property defined in a class. In the first case, the property will have a Value and in the second it will not. The property may hold an array value, in which case it is encoded in XML to PROPERTY.ARRAY containing VALUE.ARRAY.""" def __init__(self, name, value, type = None, class_origin = None, array_size = None, propagated = None, is_array = False, reference_class = None, qualifiers = {}, embedded_object = None): # Initialise members self.name = name self.value = value self.type = type self.class_origin = class_origin self.array_size = array_size self.propagated = propagated self.qualifiers = NocaseDict(qualifiers) self.is_array = is_array self.reference_class = reference_class self.embedded_object = embedded_object if isinstance(value, (datetime, timedelta)): value = CIMDateTime(value) import __builtin__ if __builtin__.type(value) == list: self.is_array = True # Determine type of value if not specified if type is None: # Can't work out what is going on if type and value are # both not set. if value is None: raise TypeError('Null property "%s" must have a type' % name) if self.is_array: # Determine type for list value if len(value) == 0: raise TypeError( 'Empty property array "%s" must have a type' % name) elif isinstance(value[0], CIMInstance): self.type = 'string' self.embedded_object = 'instance' elif isinstance(value[0], CIMClass): self.type = 'string' self.embedded_object = 'object' else: self.type = cim_types.cimtype(value[0]) elif isinstance(value, CIMInstanceName): self.type = 'reference' elif isinstance(value, CIMInstance): self.type = 'string' self.embedded_object = 'instance' elif isinstance(value, CIMClass): self.type = 'string' self.embedded_object = 'object' else: # Determine type for regular value self.type = cim_types.cimtype(value) def copy(self): return CIMProperty(self.name, self.value, type = self.type, class_origin = self.class_origin, array_size = self.array_size, propagated = self.propagated, is_array = self.is_array, reference_class = self.reference_class, qualifiers = self.qualifiers.copy()) def __repr__(self): return '%s(name=%s, type=%s, value=%s, is_array=%s)' % \ (self.__class__.__name__, `self.name`, `self.type`, `self.value`, `self.is_array`) def tocimxml(self): if self.is_array: value = self.value if value is not None: if value: if self.embedded_object is not None: value = [v.tocimxml().toxml() for v in value] value = VALUE_ARRAY([VALUE(atomic_to_cim_xml(v)) for v in value]) return PROPERTY_ARRAY( self.name, self.type, value, self.array_size, self.class_origin, self.propagated, qualifiers = [q.tocimxml() for q in self.qualifiers.values()], embedded_object = self.embedded_object) elif self.type == 'reference': value_reference = None if self.value is not None: value_reference = VALUE_REFERENCE(self.value.tocimxml()) return PROPERTY_REFERENCE( self.name, value_reference, reference_class = self.reference_class, class_origin = self.class_origin, propagated = self.propagated, qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) else: value = self.value if value is not None: if self.embedded_object is not None: value = value.tocimxml().toxml() else: value = atomic_to_cim_xml(value) value = VALUE(value) return PROPERTY( self.name, self.type, value, class_origin = self.class_origin, propagated = self.propagated, qualifiers = [q.tocimxml() for q in self.qualifiers.values()], embedded_object = self.embedded_object) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, self.__class__): return 1 return (cmpname(self.name, other.name) or cmp(self.value, other.value) or cmp(self.type, other.type) or cmp(self.class_origin, other.class_origin) or cmp(self.array_size, other.array_size) or cmp(self.propagated, other.propagated) or cmp(self.qualifiers, other.qualifiers) or cmp(self.is_array, other.is_array) or cmpname(self.reference_class, other.reference_class)) class CIMInstanceName(object): """Name (keys) identifying an instance. This may be treated as a dictionary to retrieve the keys.""" def __init__(self, classname, keybindings = {}, host = None, namespace = None): self.classname = classname self.keybindings = NocaseDict(keybindings) self.host = host self.namespace = namespace def copy(self): result = CIMInstanceName(self.classname) result.keybindings = self.keybindings.copy() result.host = self.host result.namespace = self.namespace return result def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMInstanceName): return 1 return (cmpname(self.classname, other.classname) or cmp(self.keybindings, other.keybindings) or cmpname(self.host, other.host) or cmpname(self.namespace, other.namespace)) def __str__(self): s = '' if self.host is not None: s += '//%s/' % self.host if self.namespace is not None: s += '%s:' % self.namespace s += '%s.' % self.classname for key, value in self.keybindings.items(): s +='%s=' % key if type(value) == int or type(value) == long: s += str(value) else: s += '"%s"' % value s += ',' return s[:-1] def __repr__(self): r = '%s(classname=%s, keybindings=%s' % \ (self.__class__.__name__, `self.classname`, `self.keybindings`) if self.host is not None: r += ', host=%s' % `self.host` if self.namespace is not None: r += ', namespace=%s' % `self.namespace` r += ')' return r # A whole bunch of dictionary methods that map to the equivalent # operation on self.keybindings. def __getitem__(self, key): return self.keybindings[key] def __contains__(self, key): return key in self.keybindings def __delitem__(self, key): del self.keybindings[key] def __setitem__(self, key, value): self.keybindings[key] = value def __len__(self): return len(self.keybindings) def has_key(self, key): return self.keybindings.has_key(key) def keys(self): return self.keybindings.keys() def values(self): return self.keybindings.values() def items(self): return self.keybindings.items() def iterkeys(self): return self.keybindings.iterkeys() def itervalues(self): return self.keybindings.itervalues() def iteritems(self): return self.keybindings.iteritems() def update(self, *args, **kwargs): self.keybindings.update(*args, **kwargs) def tocimxml(self): # Generate an XML representation of the instance classname and # keybindings. if type(self.keybindings) == str: # Class with single key string property instancename_xml = cim_xml.INSTANCENAME( self.classname, cim_xml.KEYVALUE(self.keybindings, 'string')) elif isinstance(self.keybindings, (long, float, int)): # Class with single key numeric property instancename_xml = cim_xml.INSTANCENAME( self.classname, cim_xml.KEYVALUE(str(self.keybindings), 'numeric')) elif isinstance(self.keybindings, (dict, NocaseDict)): # Dictionary of keybindings # NOCASE_TODO should remove dict below. kbs = [] for kb in self.keybindings.items(): # Keybindings can be integers, booleans, strings or # value references. if hasattr(kb[1], 'tocimxml'): kbs.append(cim_xml.KEYBINDING( kb[0], cim_xml.VALUE_REFERENCE(kb[1].tocimxml()))) continue if type(kb[1]) == bool: _type = 'boolean' if kb[1]: value = 'TRUE' else: value = 'FALSE' elif isinstance(kb[1], (long, float, int)): # pywbem.cim_type.{Sint32, Real64, ... } derive from # long or float _type = 'numeric' value = str(kb[1]) elif type(kb[1]) == str or type(kb[1]) == unicode: _type = 'string' value = kb[1] else: raise TypeError( 'Invalid keybinding type for keybinding ' '%s: %s' % (kb[0],`type(kb[1])`)) kbs.append(cim_xml.KEYBINDING( kb[0], cim_xml.KEYVALUE(value, _type))) instancename_xml = cim_xml.INSTANCENAME(self.classname, kbs) else: # Value reference instancename_xml = cim_xml.INSTANCENAME( self.classname, cim_xml.VALUE_REFERENCE(self.keybindings.tocimxml())) # Instance name plus namespace = LOCALINSTANCEPATH if self.host is None and self.namespace is not None: return cim_xml.LOCALINSTANCEPATH( cim_xml.LOCALNAMESPACEPATH( [cim_xml.NAMESPACE(ns) for ns in string.split(self.namespace, '/')]), instancename_xml) # Instance name plus host and namespace = INSTANCEPATH if self.host is not None and self.namespace is not None: return cim_xml.INSTANCEPATH( cim_xml.NAMESPACEPATH( cim_xml.HOST(self.host), cim_xml.LOCALNAMESPACEPATH([ cim_xml.NAMESPACE(ns) for ns in string.split(self.namespace, '/')])), instancename_xml) # Just a regular INSTANCENAME return instancename_xml class CIMInstance(object): """Instance of a CIM Object. Has a classname (string), and named arrays of properties and qualifiers. The properties is indexed by name and points to CIMProperty instances.""" def __init__(self, classname, properties = {}, qualifiers = {}, path = None, property_list = None): """Create CIMInstance. bindings is a concise way to initialize property values; it is a dictionary from property name to value. This is merely a convenience and gets the same result as the properties parameter. properties is a list of full CIMProperty objects. """ self.classname = classname self.qualifiers = NocaseDict(qualifiers) self.path = path if property_list is not None: self.property_list = [x.lower() for x in property_list] else: self.property_list = None # Assign initialised property values and run through # __setitem__ to enforce CIM types for each property. self.properties = NocaseDict() [self.__setitem__(k, v) for k, v in properties.items()] def update(self, *args, **kwargs): """D.update(E, **F) -> None. Update D from E and F: for k in E: D[k] = E[k] (if E has keys else: for (k, v) in E: D[k] = v) then: for k in F: D[k] = F[k] """ for mapping in args: if hasattr(mapping, 'items'): for k, v in mapping.items(): self[k] = v else: for (k, v) in mapping: self[k] = v for k, v in kwargs.items(): self[k] = v def update_existing(self, *args, **kwargs): """Update property values iff the property previously exists. Update D from E and F: for k in E: D[k] = E[k] (if E has keys else: for (k, v) in E: D[k] = v) then: for k in F: D[k] = F[k] Like update, but properties that are not already present in the instance are skipped. """ for mapping in args: if hasattr(mapping, 'items'): for k, v in mapping.items(): try: prop = self.properties[k] except KeyError: continue prop.value = tocimobj(prop.type, v) else: for (k, v) in mapping: try: prop = self.properties[k] except KeyError: continue prop.value = tocimobj(prop.type, v) for k, v in kwargs.items(): try: prop = self.properties[k] except KeyError: continue prop.value = tocimobj(prop.type, v) def copy(self): result = CIMInstance(self.classname) result.properties = self.properties.copy() result.qualifiers = self.qualifiers.copy() result.path = (self.path is not None and [self.path.copy()] or [None])[0] return result def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMInstance): return 1 return (cmpname(self.classname, other.classname) or cmp(self.path, other.path) or cmp(self.properties, other.properties) or cmp(self.qualifiers, other.qualifiers)) def __repr__(self): # Don't show all the properties and qualifiers because they're # just too big return '%s(classname=%s, ...)' % (self.__class__.__name__, `self.classname`) # A whole bunch of dictionary methods that map to the equivalent # operation on self.properties. def __contains__(self, key): return key in self.properties def __getitem__(self, key): return self.properties[key].value def __delitem__(self, key): del self.properties[key] def __len__(self): return len(self.properties) def has_key(self, key): return self.properties.has_key(key) def keys(self): return self.properties.keys() def values(self): return [v.value for v in self.properties.values()] def items(self): return [(k, v.value) for k, v in self.properties.items()] def iterkeys(self): return self.properties.iterkeys() def itervalues(self): for k, v in self.properties.iteritems(): yield v.value def iteritems(self): for k, v in self.properties.iteritems(): yield (k, v.value) def __setitem__(self, key, value): # Don't let anyone set integer or float values. You must use # a subclass from the cim_type module. if type(value) == int or type(value) == float or type(value) == long: raise TypeError('Must use a CIM type assigning numeric values.') if self.property_list is not None and key.lower() not in \ self.property_list: if self.path is not None and key not in self.path.keybindings: return # Convert value to appropriate PyWBEM type if isinstance(value, CIMProperty): v = value else: v = CIMProperty(key, value) self.properties[key] = v if self.path is not None and key in self.path.keybindings: self.path[key] = v.value def tocimxml(self): props = [] for key, value in self.properties.items(): # Value has already been converted into a CIM object # property type (e.g for creating null property values). if isinstance(value, CIMProperty): props.append(value) continue props.append(CIMProperty(key, value)) instance_xml = cim_xml.INSTANCE( self.classname, properties = [p.tocimxml() for p in props], qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) if self.path is None: return instance_xml return cim_xml.VALUE_NAMEDINSTANCE(self.path.tocimxml(), instance_xml) def tomof(self): def _prop2mof(_type, value): if value is None: val = 'NULL' elif isinstance(value, list): val = '{' for i,x in enumerate(value): if i > 0: val += ', ' val += _prop2mof(_type, x) val += '}' elif _type == 'string': val = '"' + value + '"' else: val = str(value) return val s = 'instance of %s {\n' % self.classname for p in self.properties.values(): s+= '\t%s = %s;\n' % (p.name, _prop2mof(p.type, p.value)) s+= '};\n' return s class CIMClass(object): def __init__(self, classname, properties = {}, methods = {}, superclass = None, qualifiers = {}): self.classname = classname self.properties = NocaseDict(properties) self.qualifiers = NocaseDict(qualifiers) self.methods = NocaseDict(methods) self.superclass = superclass def copy(self): result = CIMClass(self.classname) result.properties = self.properties.copy() result.methods = self.methods.copy() result.superclass = self.superclass result.qualifiers = self.qualifiers.copy() return result def __repr__(self): return "%s(%s, ...)" % (self.__class__.__name__, `self.classname`) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMClass): return 1 return (cmpname(self.classname, other.classname) or cmpname(self.superclass, other.superclass) or cmp(self.properties, other.properties) or cmp(self.qualifiers, other.qualifiers) or cmp(self.methods, other.methods)) def tocimxml(self): return cim_xml.CLASS( self.classname, properties = [p.tocimxml() for p in self.properties.values()], methods = [m.tocimxml() for m in self.methods.values()], qualifiers = [q.tocimxml() for q in self.qualifiers.values()], superclass = self.superclass) def tomof(self): def _makequalifiers(qualifiers, indent): """Return a mof fragment for a NocaseDict of qualifiers.""" if len(qualifiers) == 0: return '' return '[%s]' % ',\n '.ljust(indent+2).join([q.tomof() for q in qualifiers.values()]) # Class definition s = ' %s\n' % _makequalifiers(self.qualifiers, 4) s += 'class %s ' % self.classname # Superclass if self.superclass is not None: s += ': %s ' % self.superclass s += '{\n' # Properties for p in self.properties.values(): s += ' %s\n' % (_makequalifiers(p.qualifiers, 7)) s += ' %s %s;\n' % (p.type, p.name) # Methods for m in self.methods.values(): s += ' %s\n' % (_makequalifiers(m.qualifiers, 7)) s += ' %s\n' % m.tomof() s += '};\n' return s class CIMMethod(object): def __init__(self, methodname, return_type = None, parameters = {}, class_origin = None, propagated = False, qualifiers = {}): self.name = methodname self.return_type = return_type self.parameters = NocaseDict(parameters) self.class_origin = class_origin self.propagated = propagated self.qualifiers = NocaseDict(qualifiers) def copy(self): result = CIMMethod(self.name, return_type = self.return_type, class_origin = self.class_origin, propagated = self.propagated) result.parameters = self.parameters.copy() result.qualifiers = self.qualifiers.copy() return result def tocimxml(self): return cim_xml.METHOD( self.name, parameters = [p.tocimxml() for p in self.parameters.values()], return_type = self.return_type, class_origin = self.class_origin, propagated = self.propagated, qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) def __repr__(self): return '%s(name=%s, return_type=%s...)' % \ (self.__class__.__name__, `self.name`, `self.return_type`) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMMethod): return 1 return (cmpname(self.name, other.name) or cmp(self.parameters, other.parameters) or cmp(self.qualifiers, other.qualifiers) or cmp(self.class_origin, other.class_origin) or cmp(self.propagated, other.propagated) or cmp(self.return_type, other.return_type)) def tomof(self): s = '' if self.return_type is not None: s += '%s ' % self.return_type s += '%s(%s);' % \ (self.name, string.join([p.tomof() for p in self.parameters.values()], ', ')) return s class CIMParameter(object): def __init__(self, name, type, reference_class = None, is_array = None, array_size = None, qualifiers = {}, value = None): self.name = name self.type = type self.reference_class = reference_class self.is_array = is_array self.array_size = array_size self.qualifiers = NocaseDict(qualifiers) self.value = value def copy(self): result = CIMParameter(self.name, self.type, reference_class = self.reference_class, is_array = self.is_array, array_size = self.array_size, value = self.value) result.qualifiers = self.qualifiers.copy() return result def __repr__(self): return '%s(name=%s, type=%s, is_array=%s)' % \ (self.__class__.__name__, `self.name`, `self.type`, `self.is_array`) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, self.__class__): return 1 return (cmpname(self.name, other.name) or cmp(self.type, other.type) or cmpname(self.reference_class, other.reference_class) or cmp(self.is_array, other.is_array) or cmp(self.array_size, other.array_size) or cmp(self.qualifiers, other.qualifiers) or cmp(self.value, other.value)) def tocimxml(self): if self.type == 'reference': if self.is_array: array_size = None if self.array_size is not None: array_size = str(self.array_size) return cim_xml.PARAMETER_REFARRAY( self.name, self.reference_class, array_size, qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) else: return cim_xml.PARAMETER_REFERENCE( self.name, self.reference_class, qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) elif self.is_array: array_size = None if self.array_size is not None: array_size = str(self.array_size) return cim_xml.PARAMETER_ARRAY( self.name, self.type, array_size, qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) else: return cim_xml.PARAMETER( self.name, self.type, qualifiers = [q.tocimxml() for q in self.qualifiers.values()]) def tomof(self): return '%s %s' % (self.type, self.name) class CIMQualifier(object): """Represents static annotations of a class, method, property, etc. Includes information such as a documentation string and whether a property is a key.""" def __init__(self, name, value, type = None, propagated = None, overridable = None, tosubclass = None, toinstance = None, translatable = None): self.name = name self.type = type self.propagated = propagated self.overridable = overridable self.tosubclass = tosubclass self.toinstance = toinstance self.translatable = translatable # Determine type of value if not specified import __builtin__ if type is None: # Can't work out what is going on if type and value are # both not set. if value is None: raise TypeError('Null qualifier "%s" must have a type' % name) if __builtin__.type(value) == list: # Determine type for list value if len(value) == 0: raise TypeError( 'Empty qualifier array "%s" must have a type' % name) self.type = cim_types.cimtype(value[0]) else: # Determine type for regular value self.type = cim_types.cimtype(value) # Don't let anyone set integer or float values. You must use # a subclass from the cim_type module. if __builtin__.type(value) in (int, float, long): raise TypeError('Must use a CIM type for numeric qualifiers.') self.value = value def copy(self): return CIMQualifier(self.name, self.value, type = self.type, propagated = self.propagated, overridable = self.overridable, tosubclass = self.tosubclass, toinstance = self.toinstance, translatable = self.translatable) def __repr__(self): return "%s(%s, %s)" % \ (self.__class__.__name__, `self.name`, `self.value`) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMQualifier): return 1 return (cmpname(self.name, other.name) or cmp(self.value, other.value) or cmp(self.type, other.type) or cmp(self.propagated, other.propagated) or cmp(self.overridable, other.overridable) or cmp(self.tosubclass, other.tosubclass) or cmp(self.toinstance, other.toinstance) or cmp(self.translatable, other.translatable)) def tocimxml(self): value = None if type(self.value) == list: value = VALUE_ARRAY([VALUE(v) for v in self.value]) elif self.value is not None: value = VALUE(self.value) return QUALIFIER(self.name, self.type, value, propagated = self.propagated, overridable = self.overridable, tosubclass = self.tosubclass, toinstance = self.toinstance, translatable = self.translatable) def tomof(self): def valstr(v): if isinstance(v, basestring): return '"%s"' % v return str(v) if type(self.value) == list: return '%s {' % self.name + \ ', '.join([valstr(v) for v in self.value]) + '}' return '%s (%s)' % (self.name, valstr(self.value)) class CIMQualifierDeclaration(object): """Represents the declaration of a qualifier.""" # TODO: Scope and qualifier flavors def __init__(self, name, type, value = None, is_array = False, array_size = None, scopes = {}, overridable = None, tosubclass = None, toinstance = None, translatable = None): self.name = name self.type = type self.value = value self.is_array = is_array self.array_size = array_size self.scopes = NocaseDict(scopes) self.overridable = overridable self.tosubclass = tosubclass self.toinstance = toinstance self.translatable = translatable def copy(self): return CIMQualifierDeclaration(self.name, self.type, value=self.value, is_array=self.is_array, array_size=self.array_size, scopes=self.scopes, overridable=self.overridable, tosubclass=self.tosubclass, toinstance=self.toinstance, translatable=self.translatable) def __repr__(self): return "%s(%s)" % (self.__class__.__name__, `self.name`) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMQualifierDeclaration): return 1 return (cmpname(self.name, other.name) or cmp(self.type, other.type) or cmp(self.value, other.value) or cmp(self.is_array, other.is_array) or cmp(self.array_size, other.array_size) or cmp(self.scopes, other.scopes) or cmp(self.overridable, other.overridable) or cmp(self.tosubclass, other.tosubclass) or cmp(self.toinstance, other.toinstance) or cmp(self.translatable, other.translatable)) def tocimxml(self): return QUALIFIER_DECLARATION(self.name, self.type, self.value, is_array = self.is_array, array_size = self.array_size, qualifier_scopes = self.scopes, overridable=self.overridable, tosubclass=self.tosubclass, toinstance=self.toinstance, translatable=self.translatable) def tomof(self): mof = 'Qualifier %s : %s' % (self.name, self.type) if self.is_array: mof+= '[' if self.array_size is not None: mof+= str(self.array_size) mof+= ']' if self.value is not None: if isinstance(self.value, list): mof+= '{' mof+= ', '.join([atomic_to_cim_xml(tocimobj(self.type, x)) \ for x in self.value]) mof+= '}' else: mof+= ' = %s' % atomic_to_cim_xml(tocimobj(self.type,self.value)) mof+= ',\n ' mof+= 'Scope(' mof+= ', '.join([x.lower() for x, y in self.scopes.items() if y]) + ')' if not self.overridable and not self.tosubclass \ and not self.toinstance and not self.translatable: mof+= ';' return mof mof+= ',\n Flavor(' mof+= self.overridable and 'EnableOverride' or 'DisableOverride' mof+= ', ' mof+= self.tosubclass and 'ToSubclass' or 'Restricted' if self.toinstance: mof+= ', ToInstance' if self.translatable: mof+= ', Translatable' mof+= ');' return mof def tocimxml(value): """Convert an arbitrary object to CIM xml. Works with cim_obj objects and builtin types.""" # Python cim_obj object if hasattr(value, 'tocimxml'): return value.tocimxml() # CIMType or builtin type if isinstance(value, cim_types.CIMType) or \ type(value) in (str, unicode, int): return cim_xml.VALUE(unicode(value)) if isinstance(value, bool): if value: return cim_xml.VALUE('TRUE') else: return cim_xml.VALUE('FALSE') raise TypeError('Invalid boolean type: %s' % value) # List of values if type(value) == list: return cim_xml.VALUE_ARRAY(map(tocimxml, value)) raise ValueError("Can't convert %s (%s) to CIM XML" % (`value`, type(value))) def tocimobj(_type, value): """Convert a CIM type and a string value into an appropriate builtin type.""" if value is None or _type is None: return None if _type != 'string' and isinstance(value, basestring) and not value: return None # Lists of values if type(value) == list: return map(lambda x: tocimobj(_type, x), value) # Boolean type if _type == 'boolean': if isinstance(value, bool): return value elif isinstance(value, basestring): if value.lower() == 'true': return True elif value.lower() == 'false': return False raise ValueError('Invalid boolean value "%s"' % value) # String type if _type == 'string': return value # Integer types if _type == 'uint8': return cim_types.Uint8(value) if _type == 'sint8': return cim_types.Sint8(value) if _type == 'uint16': return cim_types.Uint16(value) if _type == 'sint16': return cim_types.Sint16(value) if _type == 'uint32': return cim_types.Uint32(value) if _type == 'sint32': return cim_types.Sint32(value) if _type == 'uint64': return cim_types.Uint64(value) if _type == 'sint64': return cim_types.Sint64(value) # Real types if _type == 'real32': return cim_types.Real32(value) if _type == 'real64': return cim_types.Real64(value) # Char16 if _type == 'char16': raise ValueError('CIMType char16 not handled') # Datetime if _type == 'datetime': return CIMDateTime(value) # REF def partition(s, seq): """ S.partition(sep) -> (head, sep, tail) Searches for the separator sep in S, and returns the part before it, the separator itself, and the part after it. If the separator is not found, returns S and two empty strings. """ try: return s.partition(seq) except AttributeError: try: idx = s.index(seq) except ValueError: return (s, '', '') return (s[:idx], seq, s[idx+len(seq):]) if _type == 'reference': # TODO doesn't handle double-quoting, as in refs to refs. Example: # r'ex_composedof.composer="ex_sampleClass.label1=9921,label2=\"SampleLabel\"",component="ex_sampleClass.label1=0121,label2=\"Component\""') if isinstance(value, (CIMInstanceName, CIMClassName)): return value elif isinstance(value, basestring): ns = host = None head, sep, tail = partition(value, '//') if sep and head.find('"') == -1: # we have a namespace type head, sep, tail = partition(tail, '/') host = head else: tail = head head, sep, tail = partition(tail, ':') if sep: ns = head else: tail = head head, sep, tail = partition(tail, '.') if not sep: return CIMClassName(head, host=host, namespace=ns) classname = head kb = {} while tail: head, sep, tail = partition(tail, ',') if head.count('"') == 1: # quoted string contains comma tmp, sep, tail = partition(tail,'"') head = '%s,%s' % (head, tmp) tail = partition(tail,',')[2] head = head.strip() key, sep, val = partition(head,'=') if sep: cn, s, k = partition(key, '.') if s: if cn != classname: raise ValueError('Invalid object path: "%s"' % \ value) key = k val = val.strip() if val[0] == '"' and val[-1] == '"': val = val.strip('"') else: if val.lower() in ('true','false'): val = val.lower() == 'true' elif val.isdigit(): val = int(val) else: try: val = float(val) except ValueError: try: val = CIMDateTime(val) except ValueError: raise ValueError('Invalid key binding: %s'\ % val) kb[key] = val return CIMInstanceName(classname, host=host, namespace=ns, keybindings=kb) else: raise ValueError('Invalid reference value') raise ValueError('Invalid CIM type "%s"' % _type) def byname(nlist): """Convert a list of named objects into a map indexed by name""" return dict([(x.name, x) for x in nlist]) pywbem-0.7.0/tupleparse.py0000644000175000001440000012110111102360476014422 0ustar bartusers# # (C) Copyright 2003, 2004 Hewlett-Packard Development Company, L.P. # (C) Copyright 2006-2007 Novell, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Martin Pool # Tim Potter # Bart Whiteley '''Tuple parser for the XML schema representing CIM messages. This framework is meant to add some value to the tuple-tree representation of CIM in XML by having the following properties: - Silently ignoring whitespace text elements - Conversion from tuple-tree representation into a python dictionary which can then be accessed in a readable fashion. - Validation of the XML elements and attributes without having to use the DTD file or any external tools. ''' # Implementation: this works by a recursive descent down the CIM XML # tupletree. As we walk down, we produce cim_obj and cim_type # objects representing the CIM message in digested form. # For each XML node type FOO there is one function parse_foo, which # returns the digested form by examining a tuple tree rooted at FOO. # The resulting objects are constrained to the shape of the CIM XML # tree: if one node in XML contains another, then the corresponding # CIM object will contain the second. However, there can be local # transformations at each node: some levels are ommitted, some are # transformed into lists or hashes. # We try to validate that the tree is well-formed too. The validation # is more strict than the DTD, but it is forgiving of implementation # quirks and bugs in Pegasus. # Bear in mind in the parse functions that each tupletree tuple is # structured as # tt[0]: name string == name(tt) # tt[1]: hash of attributes == attrs(tt) # tt[2]: sequence of children == kids(tt) # At the moment this layer is a little inconsistent: in some places it # returns tupletrees, and in others Python objects. It may be better # to hide the tupletree/XML representation from higher level code. # TODO: Maybe take a DTD fragment like "(DECLGROUP | # DECLGROUP.WITHNAME | DECLGROUP.WITHPATH)*", parse that and check it # directly. # TODO: Syntax-check some attributes with defined formats, such as NAME # TODO: Implement qualifiers by making subclasses of CIM types with a # .qualifiers property. import string, types import cim_obj from cim_obj import CIMProperty, byname from types import StringTypes from tupletree import xml_to_tupletree class ParseError(Exception): """This exception is raised when there is a validation error detected by the parser.""" pass def filter_tuples(l): """Return only the tuples in a list. In a tupletree, tuples correspond to XML elements. Useful for stripping out whitespace data in a child list.""" if l is None: return [] else: return [x for x in l if type(x) == tuple] def pcdata(tt): """Return the concatenated character data within a tt. The tt must not have non-character children.""" import types for x in tt[2]: if not isinstance(x, types.StringTypes): raise ParseError, 'unexpected node %s under %s' % (`x`, `tt`) return ''.join(tt[2]) def name(tt): return tt[0] def attrs(tt): return tt[1] def kids(tt): return filter_tuples(tt[2]) def check_node(tt, nodename, required_attrs = [], optional_attrs = [], allowed_children = None, allow_pcdata = False): """Check static local constraints on a single node. The node must have the given name. The required attrs must be present, and the optional attrs may be. If allowed_children is not None, the node may have children of the given types. It can be [] for nodes that may not have any children. If it's None, it is assumed the children are validated in some other way. If allow_pcdata is true, then non-whitespace text children are allowed. (Whitespace text nodes are always allowed.) """ if name(tt) <> nodename: raise ParseError('expected node type %s, not %s' % (nodename, name(tt))) # Check we have all the required attributes, and no unexpected ones tt_attrs = {} if attrs(tt) is not None: tt_attrs = attrs(tt).copy() for attr in required_attrs: if not tt_attrs.has_key(attr): raise ParseError('expected %s attribute on %s node, but only ' 'have %s' % (attr, name(tt),attrs(tt).keys())) del tt_attrs[attr] for attr in optional_attrs: if tt_attrs.has_key(attr): del tt_attrs[attr] if len(tt_attrs.keys()) > 0: raise ParseError('invalid extra attributes %s' % tt_attrs.keys()) if allowed_children is not None: for c in kids(tt): if name(c) not in allowed_children: raise ParseError('unexpected node %s under %s; wanted %s' % (name(c), name(tt), allowed_children)) if not allow_pcdata: for c in tt[2]: if isinstance(c, types.StringTypes): if c.lstrip(' \t\n') <> '': raise ParseError('unexpected non-blank pcdata node %s ' 'under %s' % (`c`, name(tt))) def one_child(tt, acceptable): """Parse children of a node with exactly one child node. PCData is ignored. """ k = kids(tt) if len(k) <> 1: raise ParseError('expecting just one %s, got %s' % (acceptable, [t[0] for t in k])) child = k[0] if name(child) not in acceptable: raise ParseError('expecting one of %s, got %s under %s' % (acceptable, name(child), name(tt))) return parse_any(child) def optional_child(tt, allowed): """Parse exactly zero or one of a list of elements from the child nodes.""" k = kids(tt) if len(k) > 1: raise ParseError('expecting zero or one of %s under %s' % (allowed, tt)) elif len(k) == 1: return one_child(tt, allowed) else: return None def list_of_various(tt, acceptable): """Parse zero or more of a list of elements from the child nodes. Each element of the list can be any type from the list of acceptable nodes.""" r = [] for child in kids(tt): if name(child) not in acceptable: raise ParseError('expected one of %s under %s, got %s' % (acceptable, name(tt), `name(child)`)) r.append(parse_any(child)) return r def list_of_matching(tt, matched): """Parse only the children of particular types under tt. Other children are ignored rather than giving an error.""" r = [] for child in kids(tt): if name(child) not in matched: continue r.append(parse_any(child)) return r def list_of_same(tt, acceptable): """Parse a list of elements from child nodes. The children can be any of the listed acceptable types, but they must all be the same. """ k = kids(tt) if not k: # empty list, consistent with list_of_various return [] w = name(k[0]) if w not in acceptable: raise ParseError('expected one of %s under %s, got %s' % (acceptable, name(tt), `w`)) r = [] for child in k: if name(child) <> w: raise ParseError('expected list of %s under %s, but found %s' % (w, name(child), name(tt))) r.append(parse_any(child)) return r def notimplemented(tt): raise ParseError('parser for %s not implemented' % name(tt)) # # Root element # def parse_cim(tt): """ """ check_node(tt, 'CIM', ['CIMVERSION', 'DTDVERSION']) if attrs(tt)['CIMVERSION'] <> '2.0': raise ParseError('CIMVERSION is %s, expected 2.0' % attrs(tt)[CIMVERSION]) child = one_child(tt, ['MESSAGE', 'DECLARATION']) return name(tt), attrs(tt), child # # Object value elements # def parse_value(tt): '''Return VALUE contents as a string''' ## check_node(tt, 'VALUE', [], [], [], True) return pcdata(tt) def parse_value_array(tt): """Return list of strings.""" ## check_node(tt, 'VALUE.ARRAY', [], [], ['VALUE']) return list_of_same(tt, ['VALUE']) def parse_value_reference(tt): """ """ check_node(tt, 'VALUE.REFERENCE', []) child = one_child(tt, ['CLASSPATH', 'LOCALCLASSPATH', 'CLASSNAME', 'INSTANCEPATH', 'LOCALINSTANCEPATH', 'INSTANCENAME']) # The VALUE.REFERENCE wrapper element is discarded return child def parse_value_refarray(tt): """ """ check_node(tt, 'VALUE.REFARRAY') children = list_of_various(tt, ['VALUE.REFERENCE']) # The VALUE.REFARRAY wrapper element is discarded return children def parse_value_object(tt): """ """ check_node(tt, 'VALUE.OBJECT') child = one_child(tt, ['CLASS', 'INSTANCE']) return (name(tt), attrs(tt), child) def parse_value_namedinstance(tt): """ """ check_node(tt, 'VALUE.NAMEDINSTANCE') k = kids(tt) if len(k) <> 2: raise ParseError('expecting (INSTANCENAME, INSTANCE), got %s' % `k`) instancename = parse_instancename(k[0]) instance = parse_instance(k[1]) instance.path = instancename return instance def parse_value_namedobject(tt): """ """ check_node(tt, 'VALUE.NAMEDOBJECT') k = kids(tt) if len(k) == 1: object = parse_class(k[0]) elif len(k) == 2: path = parse_instancename(kids(tt)[0]) object = parse_instance(kids(tt)[1]) object.path = path else: raise ParseError('Expecting one or two elements, got %s' % `kids(tt)`) return (name(tt), attrs(tt), object) def parse_value_objectwithlocalpath(tt): """ """ check_node(tt, 'VALUE.OBJECTWITHLOCALPATH') if len(kids(tt)) != 2: raise ParseError('Expecting two elements, got %s' % len(kids(tt))); if kids(tt)[0][0] == 'LOCALCLASSPATH': object = (parse_localclasspath(kids(tt)[0]), parse_class(kids(tt)[1])) else: path = parse_localinstancepath(kids(tt)[0]) object = parse_instance(kids(tt)[1]) object.path = path return (name(tt), attrs(tt), object) def parse_value_objectwithpath(tt): """ """ check_node(tt, 'VALUE.OBJECTWITHPATH') k = kids(tt) if len(k) != 2: raise ParseError('Expecting two elements, got %s' % k) if name(k[0]) == 'CLASSPATH': object = (parse_classpath(k[0]), parse_class(k[1])) else: path = parse_instancepath(k[0]) object = parse_instance(k[1]) object.path = path return (name(tt), attrs(tt), object) # # Object naming and locating elements # def parse_namespacepath(tt): """ """ check_node(tt, 'NAMESPACEPATH') if len(kids(tt)) != 2: raise ParseError('Expecting (HOST, LOCALNAMESPACEPATH) ' 'got %s' % kids(tt).keys()) host = parse_host(kids(tt)[0]) localnspath = parse_localnamespacepath(kids(tt)[1]) return (host, localnspath) def parse_localnamespacepath(tt): """ """ check_node(tt, 'LOCALNAMESPACEPATH', [], [], ['NAMESPACE']) if len(kids(tt)) == 0: raise ParseError('Expecting one or more of NAMESPACE, got nothing') ns_list = list_of_various(tt, ['NAMESPACE']) return string.join(ns_list, '/') def parse_host(tt): """ """ check_node(tt, 'HOST', allow_pcdata=True) return pcdata(tt) def parse_namespace(tt): """ """ check_node(tt, 'NAMESPACE', ['NAME'], [], []) return attrs(tt)['NAME'] def parse_classpath(tt): """ """ check_node(tt, 'CLASSPATH') if len(kids(tt)) != 2: raise ParseError('Expecting (NAMESPACEPATH, CLASSNAME) ' 'got %s' % kids(tt).keys()) nspath = parse_namespacepath(kids(tt)[0]) classname = parse_classname(kids(tt)[1]) return cim_obj.CIMClassName(classname.classname, host = nspath[0], namespace = nspath[1]) def parse_localclasspath(tt): """ """ check_node(tt, 'LOCALCLASSPATH') if len(kids(tt)) != 2: raise ParseError('Expecting (LOCALNAMESPACEPATH, CLASSNAME) ' 'got %s' % kids(tt).keys()) localnspath = parse_localnamespacepath(kids(tt)[0]) classname = parse_classname(kids(tt)[1]) return cim_obj.CIMClassName(classname.classname, namespace = localnspath) def parse_classname(tt): """ """ check_node(tt, 'CLASSNAME', ['NAME'], [], []) return cim_obj.CIMClassName(attrs(tt)['NAME']) def parse_instancepath(tt): """ """ check_node(tt, 'INSTANCEPATH') if len(kids(tt)) != 2: raise ParseError('Expecting (NAMESPACEPATH, INSTANCENAME), got %s' % `kids(tt)`) nspath = parse_namespacepath(kids(tt)[0]) instancename = parse_instancename(kids(tt)[1]) instancename.host = nspath[0] instancename.namespace = nspath[1] return instancename def parse_localinstancepath(tt): """ """ check_node(tt, 'LOCALINSTANCEPATH') if len(kids(tt)) != 2: raise ParseError('Expecting (LOCALNAMESPACEPATH, INSTANCENAME), ' 'got %s' % kids(tt).keys()) localnspath = parse_localnamespacepath(kids(tt)[0]) instancename = parse_instancename(kids(tt)[1]) instancename.namespace = localnspath return instancename def parse_instancename(tt): """Parse XML INSTANCENAME into CIMInstanceName object.""" ## ## from cim_obj import CIMInstanceName check_node(tt, 'INSTANCENAME', ['CLASSNAME']) if len(kids(tt)) == 0: # probably not ever going to see this, but it's valid # according to the grammar return CIMInstanceName(attrs(tt)['CLASSNAME'], {}) k0 = kids(tt)[0] w = name(k0) classname = attrs(tt)['CLASSNAME'] if w == 'KEYVALUE' or w == 'VALUE.REFERENCE': if len(kids(tt)) != 1: raise ParseError('expected only one %s under %s' % w, name(tt)) # FIXME: This is probably not the best representation of these forms... val = parse_any(k0) return CIMInstanceName(classname, {None: val}) elif w == 'KEYBINDING': kbs = {} for kb in list_of_various(tt, ['KEYBINDING']): kbs.update(kb) return CIMInstanceName(classname, kbs) else: raise ParseError('unexpected node %s under %s' % (name(kids(tt)[0]), name(tt))) def parse_objectpath(tt): """ """ check_node(tt, 'OBJECTPATH') child = one_child(tt, ['INSTANCEPATH', 'CLASSPATH']) return (name(tt), attrs(tt), child) def parse_keybinding(tt): ## ## """Returns one-item dictionary from name to Python value.""" check_node(tt, 'KEYBINDING', ['NAME']) child = one_child(tt, ['KEYVALUE', 'VALUE.REFERENCE']) return {attrs(tt)['NAME']: child} def parse_keyvalue(tt): ## ## """Parse VALUETYPE into Python primitive value""" check_node(tt, 'KEYVALUE', ['VALUETYPE'], ['TYPE'], [], True) vt = attrs(tt).get('VALUETYPE') p = pcdata(tt) if vt == 'string': return p elif vt == 'boolean': return unpack_boolean(p) elif vt == 'numeric': try: # XXX: Use TYPE attribute to create named CIM type. # if attrs(tt).has_key('TYPE'): # return cim_obj.tocimobj(attrs(tt)['TYPE'], p.strip()) # XXX: Would like to use long() here, but that tends to cause # trouble when it's written back out as '2L' return int(p.strip()) except ValueError, e: raise ParseError('invalid numeric %s under %s' % (`p`, name(tt))) else: raise ParseError('invalid VALUETYPE %s in %s', vt, name(tt)) # # Object definition elements # def parse_class(tt): ## ## # This doesn't check the ordering of elements, but it's not very important check_node(tt, 'CLASS', ['NAME'], ['SUPERCLASS'], ['QUALIFIER', 'PROPERTY', 'PROPERTY.REFERENCE', 'PROPERTY.ARRAY', 'METHOD']) superclass = attrs(tt).get('SUPERCLASS') # TODO: Return these as maps, not lists properties = byname(list_of_matching(tt, ['PROPERTY', 'PROPERTY.REFERENCE', 'PROPERTY.ARRAY'])) qualifiers = byname(list_of_matching(tt, ['QUALIFIER'])) methods = byname(list_of_matching(tt, ['METHOD'])) return cim_obj.CIMClass(attrs(tt)['NAME'], superclass=superclass, properties=properties, qualifiers=qualifiers, methods=methods) def parse_instance(tt): """Return a CIMInstance. The instance contains the properties, qualifiers and classname for the instance""" ## ## check_node(tt, 'INSTANCE', ['CLASSNAME'], ['QUALIFIER', 'PROPERTY', 'PROPERTY.ARRAY', 'PROPERTY.REFERENCE']) ## XXX: This does not enforce ordering constraint ## XXX: This does not enforce the constraint that there be only ## one PROPERTY or PROPERTY.ARRAY. ## TODO: Parse instance qualifiers qualifiers = {} props = list_of_matching(tt, ['PROPERTY.REFERENCE', 'PROPERTY', 'PROPERTY.ARRAY']) obj = cim_obj.CIMInstance(attrs(tt)['CLASSNAME'], qualifiers = qualifiers) [obj.__setitem__(p.name, p) for p in props] return obj def parse_scope(tt): # # ## check_node(tt, 'QUALIFIER.DECLARATION', ['NAME', 'TYPE'], ['ISARRAY', 'ARRAYSIZE', 'OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 'TRANSLATABLE'], ['SCOPE', 'VALUE', 'VALUE.ARRAY']) a = attrs(tt) qname = a['NAME'] type = a['TYPE'] try: is_array = a['ISARRAY'].lower() == 'true' except KeyError: is_array = False try: array_size = int(a['ARRAYSIZE']) except KeyError: array_size = None flavors = {} for f in ['OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 'TRANSLATABLE']: try: flavors[f.lower()] = a[f].lower() == 'true' except KeyError: pass scopes = None value = None for child in kids(tt): if name(child) == 'SCOPE': if scopes is not None: raise ParseError("Multiple SCOPE tags encountered") scopes = parse_any(child) else: if value is not None: raise ParseError("Multiple VALUE/VALUE.ARRAY tags encountered") value = cim_obj.tocimobj(type, parse_any(child)) return cim_obj.CIMQualifierDeclaration(qname, type, value, is_array, array_size, scopes, **flavors) def parse_qualifier(tt): ## ## check_node(tt, 'QUALIFIER', ['NAME', 'TYPE'], ['OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 'TRANSLATABLE', 'PROPAGATED'], ['VALUE', 'VALUE.ARRAY']) a = attrs(tt) q = cim_obj.CIMQualifier(a['NAME'], unpack_value(tt), type=a['TYPE']) ## TODO: Lift this out? for i in ['OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 'TRANSLATABLE', 'PROPAGATED']: rv = a.get(i) if rv not in ['true', 'false', None]: raise ParseError("invalid value %s for %s on %s" % (`rv`, i, name(tt))) if rv == 'true': rv = True elif rv == 'false': rv = False setattr(q, i.lower(), rv) return q def parse_property(tt): """Parse PROPERTY into a CIMProperty object. VAL is just the pcdata of the enclosed VALUE node.""" ## ## ## TODO: Parse this into NAME, VALUE, where the value contains ## magic fields for the qualifiers and the propagated flag. check_node(tt, 'PROPERTY', ['TYPE', 'NAME'], ['NAME', 'CLASSORIGIN', 'PROPAGATED', 'EmbeddedObject', 'EMBEDDEDOBJECT'], ['QUALIFIER', 'VALUE']) quals = {} for q in list_of_matching(tt, ['QUALIFIER']): quals[q.name] = q val = unpack_value(tt) a = attrs(tt) embedded_object=None if 'EmbeddedObject' in a or 'EMBEDDEDOBJECT' in a: try: embedded_object = a['EmbeddedObject'] except KeyError: embedded_object = a['EMBEDDEDOBJECT'] if embedded_object is not None: val = parse_embeddedObject(val) return CIMProperty(a['NAME'], val, a['TYPE'], class_origin=a.get('CLASSORIGIN'), propagated=unpack_boolean(a.get('PROPAGATED')), qualifiers=quals, embedded_object=embedded_object) def parse_property_array(tt): """ """ from cim_obj import tocimobj check_node(tt, 'PROPERTY.ARRAY', ['NAME', 'TYPE'], ['REFERENCECLASS', 'CLASSORIGIN', 'PROPAGATED', 'ARRAYSIZE', 'EmbeddedObject', 'EMBEDDEDOBJECT'], ['QUALIFIER', 'VALUE.ARRAY']) quals = {} for q in list_of_matching(tt, ['QUALIFIER']): quals[q.name] = q values = unpack_value(tt) a = attrs(tt) embedded_object = None if 'EmbeddedObject' in a or 'EMBEDDEDOBJECT' in a: try: embedded_object = a['EmbeddedObject'] except KeyError: embedded_object = a['EMBEDDEDOBJECT'] if embedded_object is not None: values = parse_embeddedObject(values) obj = CIMProperty(a['NAME'], values, a['TYPE'], class_origin=a.get('CLASSORIGIN'), qualifiers=quals, is_array=True, embedded_object=embedded_object) ## TODO: qualifiers, other attributes return obj def parse_property_reference(tt): """ """ check_node(tt, 'PROPERTY.REFERENCE', ['NAME'], ['REFERENCECLASS', 'CLASSORIGIN', 'PROPAGATED']) value = list_of_matching(tt, ['VALUE.REFERENCE']) if value is None or len(value) == 0: value = None elif len(value) == 1: value = value[0] else: raise ParseError('Too many VALUE.REFERENCE elements.') attributes = attrs(tt) pref = cim_obj.CIMProperty(attributes['NAME'], value, type = 'reference') for q in list_of_matching(tt, ['QUALIFIER']): pref.qualifiers[q.name] = q if attributes.has_key('REFERENCECLASS'): pref.reference_class = attributes['REFERENCECLASS'] if attributes.has_key('CLASSORIGIN'): pref.class_origin = attributes['CLASSORIGIN'] if attributes.has_key('PROPAGATED'): pref.propagated = attributes['PROPAGATED'] return pref def parse_method(tt): """ """ check_node(tt, 'METHOD', ['NAME'], ['TYPE', 'CLASSORIGIN', 'PROPAGATED'], ['QUALIFIER', 'PARAMETER', 'PARAMETER.REFERENCE', 'PARAMETER.ARRAY', 'PARAMETER.REFARRAY']) qualifiers = byname(list_of_matching(tt, ['QUALIFIER'])) parameters = byname(list_of_matching(tt, ['PARAMETER', 'PARAMETER.REFERENCE', 'PARAMETER.ARRAY', 'PARAMETER.REFARRAY',])) a = attrs(tt) return cim_obj.CIMMethod(a['NAME'], return_type=a.get('TYPE'), parameters=parameters, qualifiers=qualifiers, class_origin=a.get('CLASSORIGIN'), propagated=unpack_boolean(a.get('PROPAGATED'))) def parse_parameter(tt): """ """ check_node(tt, 'PARAMETER', ['NAME', 'TYPE'], []) quals = {} for q in list_of_matching(tt, ['QUALIFIER']): quals[q.name] = q a = attrs(tt) return cim_obj.CIMParameter(a['NAME'], type=a['TYPE'], qualifiers=quals) def parse_parameter_reference(tt): """ """ check_node(tt, 'PARAMETER.REFERENCE', ['NAME'], ['REFERENCECLASS']) quals = {} for q in list_of_matching(tt, ['QUALIFIER']): quals[q.name] = q a = attrs(tt) return cim_obj.CIMParameter(a['NAME'], type='reference', reference_class=a.get('REFERENCECLASS'), qualifiers=quals) def parse_parameter_array(tt): """ """ check_node(tt, 'PARAMETER.ARRAY', ['NAME', 'TYPE'], ['ARRAYSIZE']) quals = {} for q in list_of_matching(tt, ['QUALIFIER']): quals[q.name] = q a = attrs(tt) array_size = a.get('ARRAYSIZE') if array_size is not None: array_size = int(array_size) return cim_obj.CIMParameter(a['NAME'], type=a['TYPE'], is_array = True, array_size=array_size, qualifiers=quals) def parse_parameter_refarray(tt): """ """ check_node(tt, 'PARAMETER.REFARRAY', ['NAME'], ['REFERENCECLASS', 'ARRAYSIZE']) quals = {} for q in list_of_matching(tt, ['QUALIFIER']): quals[q.name] = q a = attrs(tt) array_size = a.get('ARRAYSIZE') if array_size is not None: array_size = int(array_size) return cim_obj.CIMParameter(a['NAME'], 'reference', is_array = True, reference_class=a.get('REFERENCECLASS'), array_size=array_size, qualifiers=quals) # # Message elements # def parse_message(tt): """ """ check_node(tt, 'MESSAGE', ['ID', 'PROTOCOLVERSION']) messages = one_child( tt, ['SIMPLEREQ', 'MULTIREQ', 'SIMPLERSP', 'MULTIRSP', 'SIMPLEEXPREQ']) if type(messages) is not list: # make single and multi forms consistent messages = [messages] return name(tt), attrs(tt), messages def parse_multireq(tt): raise ParseError('MULTIREQ parser not implemented') def parse_multiexpreq(tt): raise ParseError('MULTIEXPREQ parser not implemented') def parse_simpleexpreq(tt): """ """ child = one_child(tt, ['EXPMETHODCALL']) return name(tt), attrs(tt), child def parse_simplereq(tt): """ """ check_node(tt, 'SIMPLEREQ') child = one_child(tt, ['IMETHODCALL', 'METHODCALL']) return name(tt), attrs(tt), child def parse_imethodcall(tt): """ """ check_node(tt, 'IMETHODCALL', ['NAME']) if len(kids(tt)) < 1: raise ParseError('Expecting LOCALNAMESPACEPATH, got nothing') localnspath = parse_localnamespacepath(kids(tt)[0]) params = map(lambda x: parse_iparamvalue(x), kids(tt)[1:]) return (name(tt), attrs(tt), localnspath, params) def parse_methodcall(tt): """ """ check_node(tt, 'METHODCALL', ['NAME'], [], ['LOCALCLASSPATH', 'LOCALINSTANCEPATH', 'PARAMVALUE']) path = list_of_matching(tt, ['LOCALCLASSPATH','LOCALINSTANCEPATH']) if len(path) != 1: raise ParseError('Expecting one of LOCALCLASSPATH or LOCALINSTANCEPATH, got %s' % `path`) path = path[0] params = list_of_matching(tt, ['PARAMVALUE']) return (name(tt), attrs(tt), path, params) def parse_expmethodcall(tt): """ """ check_node(tt, 'EXPMETHODCALL', ['NAME'], [], ['EXPPARAMVALUE']) params = list_of_matching(tt, ['EXPPARAMVALUE']) return (name(tt), attrs(tt), params) def parse_paramvalue(tt): ## ## ## Version 2.1.1 of the DTD lacks the %ParamType attribute but it ## is present in version 2.2. Make it optional to be backwards ## compatible. check_node(tt, 'PARAMVALUE', ['NAME'], ['PARAMTYPE','EmbeddedObject', 'EMBEDDEDOBJECT']) child = optional_child(tt, ['VALUE', 'VALUE.REFERENCE', 'VALUE.ARRAY', 'VALUE.REFARRAY',]) if attrs(tt).has_key('PARAMTYPE'): paramtype = attrs(tt)['PARAMTYPE'] else: paramtype = None if 'EmbeddedObject' in attrs(tt) or 'EMBEDDEDOBJECT' in attrs(tt): child = parse_embeddedObject(child) return attrs(tt)['NAME'], paramtype, child def parse_iparamvalue(tt): ## ## """Returns NAME, VALUE pair.""" check_node(tt, 'IPARAMVALUE', ['NAME'], []) child = optional_child(tt, ['VALUE', 'VALUE.ARRAY', 'VALUE.REFERENCE', 'INSTANCENAME', 'CLASSNAME', 'QUALIFIER.DECLARATION', 'CLASS', 'INSTANCE', 'VALUE.NAMEDINSTANCE']) name = attrs(tt)['NAME'] if isinstance(child, basestring) and \ name.lower() in ['deepinheritance', 'localonly', 'includequalifiers', 'includeclassorigin']: if child.lower() in ['true', 'false']: child = child.lower() == 'true' return name, child def parse_expparamvalue(tt): """ """ check_node(tt, 'EXPPARAMVALUE', ['NAME'], [], ['INSTANCE']) child = optional_child(tt, ['INSTANCE']) name = attrs(tt)['NAME'] return name, child def parse_multirsp(tt): raise ParseError('MULTIRSP parser not implemented') def parse_multiexprsp(tt): raise ParseError('MULTIEXPRSP parser not implemented') def parse_simplersp(tt): ## check_node(tt, 'SIMPLERSP', [], []) child = one_child(tt, ['METHODRESPONSE', 'IMETHODRESPONSE']) return name(tt), attrs(tt), child def parse_simpleexprsp(tt): raise ParseError('SIMPLEEXPRSP parser not implemented') def parse_methodresponse(tt): ## ## check_node(tt, 'METHODRESPONSE', ['NAME'], []) return name(tt), attrs(tt), list_of_various(tt, ['ERROR', 'RETURNVALUE', 'PARAMVALUE']) def parse_expmethodresponse(tt): raise ParseError('EXPMETHODRESPONSE parser not implemented') def parse_imethodresponse(tt): ## ## check_node(tt, 'IMETHODRESPONSE', ['NAME'], []) return name(tt), attrs(tt), optional_child(tt, ['ERROR', 'IRETURNVALUE']) def parse_error(tt): """ """ ## TODO: Return a CIMError object, not a tuple check_node(tt, 'ERROR', ['CODE'], ['DESCRIPTION']) return (name(tt), attrs(tt), None) def parse_returnvalue(tt): ## ## ## Version 2.1.1 of the DTD lacks the %ParamType attribute but it ## is present in version 2.2. Make it optional to be backwards ## compatible. check_node(tt, 'RETURNVALUE', [], ['PARAMTYPE']) return name(tt), attrs(tt), one_child(tt, ['VALUE', 'VALUE.ARRAY', 'VALUE.REFERENCE', 'VALUE.REFARRAY']) def parse_ireturnvalue(tt): ## check_node(tt, 'IRETURNVALUE', [], []) # XXX: doesn't prohibit the case of only one VALUE.ARRAY or # VALUE.REFERENCE. But why is that required? Why can it return # multiple VALUEs but not multiple VALUE.REFERENCEs? values = list_of_same(tt, ['CLASSNAME', 'INSTANCENAME', 'VALUE', 'VALUE.OBJECTWITHPATH', 'VALUE.OBJECT', 'OBJECTPATH', 'QUALIFIER.DECLARATION', 'VALUE.ARRAY', 'VALUE.REFERENCE', 'CLASS', 'INSTANCE', 'VALUE.NAMEDINSTANCE',]) ## TODO: Call unpack_value if appropriate return name(tt), attrs(tt), values # # Object naming and locating elements # def parse_any(tt): """Parse any fragment of XML.""" nodename = name(tt).lower().replace('.', '_') fn_name = 'parse_' + nodename fn = globals().get(fn_name) if fn is None: raise ParseError('no parser for node type %s' % name(tt)) else: return fn(tt) def parse_embeddedObject(val): if isinstance(val, list): return [parse_embeddedObject(obj) for obj in val] if val is None: return None tt = xml_to_tupletree(val) if tt[0] == 'INSTANCE': return parse_instance(tt) elif tt[0] == 'CLASS': return parse_class(tt) else: raise ParseError('Error parsing embedded object') def unpack_value(tt): """Find VALUE or VALUE.ARRAY under TT and convert to a Python value. Looks at the TYPE of the node to work out how to decode it. Handles nodes with no value (e.g. in CLASS.) """ ## TODO: Handle VALUE.REFERENCE, VALUE.REFARRAY valtype = attrs(tt)['TYPE'] raw_val = list_of_matching(tt, ['VALUE', 'VALUE.ARRAY']) if len(raw_val) == 0: return None elif len(raw_val) > 1: raise ParseError('more than one VALUE or VALUE.ARRAY under %s' % name(tt)) raw_val = raw_val[0] if isinstance(raw_val, list): return [cim_obj.tocimobj(valtype, x) for x in raw_val] elif len(raw_val) == 0 and valtype != 'string': return None else: return cim_obj.tocimobj(valtype, raw_val) def unpack_boolean(p): """Unpack a boolean, represented as "TRUE" or "FALSE" in CIM.""" if p is None: return None ## CIM-XML says "These values MUST be treated as case-insensitive" ## (even though the XML definition requires them to be lowercase.) p = p.strip().lower() # ignore space if p == 'true': return True elif p == 'false': return False elif p == '': return None else: raise ParseError('invalid boolean %s' % `p`) pywbem-0.7.0/wbemcli.py0000755000175000001440000001752211102360476013676 0ustar bartusers#!/usr/bin/python # (C) Copyright 2008 Hewlett-Packard Development Company, L.P. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # Author: Tim Potter # # A small utility to wrap up a PyWBEM session in a Python interactive # console. # # Usage: # # wbemcli.py HOSTNAME [-u USERNAME -p PASSWORD] [-n namespace] [--no-ssl] \ # [--port PORT] # # CIM operations can be executed by using the PyWBEM connection object # called 'cli' in the global scope. There are two sets of aliases # available for usage in the interpreter. For example the following # three commands are equivalent: # # >>> cli.EnumerateInstanceNames('SMX_ComputerSystem') # >>> EnumerateInstanceNames('SMX_ComputerSystem') # >>> ein('SMX_ComputerSystem') # # Pretty-printing of results is also available using the 'pp' # function. For example: # # >>> cs = ei('SMX_ComputerSystem')[0] # >>> pp(cs.items()) # [(u'RequestedState', 12L), # (u'Dedicated', [1L]), # (u'StatusDescriptions', [u'System is Functional']), # (u'IdentifyingNumber', u'6F880AA1-F4F5-11D5-8C45-C0116FBAE02A'), # ... # import os, stat, sys, string, getpass, errno from pywbem import * from code import InteractiveConsole from optparse import OptionParser from pprint import pprint as pp # Conditional support of readline module have_readline = False try: import readline have_readline = True except ImportError, arg: pass # # Parse command line args # optparser = OptionParser( usage = '%prog HOSTNAME [-u USER -p PASS] [-n NAMESPACE] [--no-ssl]') # Username and password optparser.add_option('-u', '--user', dest = 'user', action = 'store', type = 'string', help = 'user to connect as') optparser.add_option('-p', '--password', dest = 'password', action = 'store', type = 'string', help = 'password to connect user as') # Change the default namespace used optparser.add_option('-n', '--namespace', dest = 'namespace', action = 'store', type = 'string', default = 'root/cimv2', help = 'default namespace to use') # Don't use SSL for remote connections optparser.add_option('--no-ssl', dest = 'no_ssl', action = 'store_true', help = 'don\'t use SSL') # Specify non-standard port optparser.add_option('--port', dest = 'port', action = 'store', type = 'int', help = 'port to connect as', default = None) # Check usage (opts, argv) = optparser.parse_args() if len(argv) != 1: optparser.print_usage() sys.exit(1) # # Set up a client connection # def remote_connection(): """Initiate a remote connection, via PyWBEM.""" if argv[0][0] == '/': url = argv[0] else: proto = 'https' if opts.no_ssl: proto = 'http' url = '%s://%s' % (proto, argv[0]) if opts.port is not None: url += ':%d' % opts.port creds = None if opts.user is not None and opts.password is None: opts.password = getpass.getpass('Enter password for %s: ' % opts.user) if opts.user is not None or opts.password is not None: creds = (opts.user, opts.password) cli = WBEMConnection(url, creds, default_namespace = opts.namespace) cli.debug = True return cli cli = remote_connection() # # Create some convenient global functions to reduce typing # def EnumerateInstanceNames(classname, namespace = None): """Enumerate the names of the instances of a CIM Class (including the names of any subclasses) in the target namespace.""" return cli.EnumerateInstanceNames(classname, namespace = namespace) def EnumerateInstances(classname, namespace = None, LocalOnly = True, DeepInheritance = True, IncludeQualifiers = False, IncludeClassOrigin = False): """Enumerate instances of a CIM Class (includeing the instances of any subclasses in the target namespace.""" return cli.EnumerateInstances(classname, namespace = namespace, DeepInheritance = DeepInheritance, IncludeQualifiers = IncludeQualifiers, IncludeClassOrigin = IncludeClassOrigin) def GetInstance(instancename, LocalOnly = True, IncludeQualifiers = False, IncludeClassOrigin = False): """Return a single CIM instance corresponding to the instance name given.""" return cli.GetInstance(instancename, LocalOnly = LocalOnly, IncludeQualifiers = IncludeQualifiers, IncludeClassOrigin = IncludeClassOrigin) def DeleteInstance(instancename): """Delete a single CIM instance.""" return cli.DeleteInstance(instancename) def ModifyInstance(*args, **kwargs): return cli.ModifyInstance(*args, **kwargs) def CreateInstance(*args, **kwargs): return cli.CreateInstance(*args, **kwargs) def InvokeMethod(*args, **kwargs): return cli.InvokeMethod(*args, **kwargs) def AssociatorNames(*args, **kwargs): return cli.AssociatorNames(*args, **kwargs) def Associators(*args, **kwargs): return cli.Associators(*args, **kwargs) def ReferenceNames(*args, **kwargs): return cli.ReferenceNames(*args, **kwargs) def References(*args, **kwargs): return cli.References(*args, **kwargs) def EnumerateClassNames(*args, **kwargs): return cli.EnumerateClassNames(*args, **kwargs) def EnumerateClasses(*args, **kwargs): return cli.EnumerateClasses(*args, **kwargs) def GetClass(*args, **kwargs): return cli.GetClass(*args, **kwargs) def DeleteClass(*args, **kwargs): return cli.DeleteClass(*args, **kwargs) def ModifyClass(*args, **kwargs): return cli.ModifyClass(*args, **kwargs) def CreateClass(*args, **kwargs): return cli.CreateClass(*args, **kwargs) def EnumerateQualifiers(*args, **kwargs): return cli.EnumerateQualifiers(*args, **kwargs) def GetQualifier(*args, **kwargs): return cli.GetQualifier(*args, **kwargs) def SetQualifier(*args, **kwargs): return cli.SetQualifier(*args, **kwargs) def DeleteQualifier(*args, **kwargs): return cli.DeleteQualifier(*args, **kwargs) # Aliases for global functions above ein = EnumerateInstanceNames ei = EnumerateInstances gi = GetInstance di = DeleteInstance mi = ModifyInstance ci = CreateInstance im = InvokeMethod an = AssociatorNames ao = Associators rn = ReferenceNames re = References ecn = EnumerateClassNames ec = EnumerateClasses gc = GetClass dc = DeleteClass mc = ModifyClass cc = CreateClass eq = EnumerateQualifiers gq = GetQualifier sq = SetQualifier dq = DeleteQualifier # # Enter interactive console # def get_banner(): result = '' # Note how we are connected result += 'Connected to %s' % cli.url if cli.creds is not None: result += ' as %s' % cli.creds[0] return result # Read previous command line history histfile = '%s/.wbemcli_history' % os.environ['HOME'] try: if have_readline: readline.read_history_file(histfile) except IOError, arg: if arg[0] != errno.ENOENT: raise # Interact i = InteractiveConsole(globals()) i.interact(get_banner()) # Save command line history if have_readline: readline.write_history_file(histfile) pywbem-0.7.0/lex.py0000644000175000001440000010440711066461100013033 0ustar bartusers# ----------------------------------------------------------------------------- # ply: lex.py # # Author: David M. Beazley (dave@dabeaz.com) # # Copyright (C) 2001-2008, David M. Beazley # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # See the file COPYING for a complete copy of the LGPL. # ----------------------------------------------------------------------------- __version__ = "2.5" __tabversion__ = "2.4" # Version of table file used import re, sys, types, copy, os # This regular expression is used to match valid token names _is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') # _INSTANCETYPE sets the valid set of instance types recognized # by PLY when lexers are defined by a class. In order to maintain # backwards compatibility with Python-2.0, we have to check for # the existence of ObjectType. try: _INSTANCETYPE = (types.InstanceType, types.ObjectType) except AttributeError: _INSTANCETYPE = types.InstanceType class object: pass # Note: needed if no new-style classes present # Exception thrown when invalid token encountered and no default error # handler is defined. class LexError(Exception): def __init__(self,message,s): self.args = (message,) self.text = s # An object used to issue one-time warning messages for various features class LexWarning(object): def __init__(self): self.warned = 0 def __call__(self,msg): if not self.warned: sys.stderr.write("ply.lex: Warning: " + msg+"\n") self.warned = 1 _SkipWarning = LexWarning() # Warning for use of t.skip() on tokens # Token class. This class is used to represent the tokens produced. class LexToken(object): def __str__(self): return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) def __repr__(self): return str(self) def skip(self,n): self.lexer.skip(n) _SkipWarning("Calling t.skip() on a token is deprecated. Please use t.lexer.skip()") # ----------------------------------------------------------------------------- # Lexer class # # This class encapsulates all of the methods and data associated with a lexer. # # input() - Store a new string in the lexer # token() - Get the next token # ----------------------------------------------------------------------------- class Lexer: def __init__(self): self.lexre = None # Master regular expression. This is a list of # tuples (re,findex) where re is a compiled # regular expression and findex is a list # mapping regex group numbers to rules self.lexretext = None # Current regular expression strings self.lexstatere = {} # Dictionary mapping lexer states to master regexs self.lexstateretext = {} # Dictionary mapping lexer states to regex strings self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names self.lexstate = "INITIAL" # Current lexer state self.lexstatestack = [] # Stack of lexer states self.lexstateinfo = None # State information self.lexstateignore = {} # Dictionary of ignored characters for each state self.lexstateerrorf = {} # Dictionary of error functions for each state self.lexreflags = 0 # Optional re compile flags self.lexdata = None # Actual input data (as a string) self.lexpos = 0 # Current position in input text self.lexlen = 0 # Length of the input text self.lexerrorf = None # Error rule (if any) self.lextokens = None # List of valid tokens self.lexignore = "" # Ignored characters self.lexliterals = "" # Literal characters that can be passed through self.lexmodule = None # Module self.lineno = 1 # Current line number self.lexdebug = 0 # Debugging mode self.lexoptimize = 0 # Optimized mode def clone(self,object=None): c = copy.copy(self) # If the object parameter has been supplied, it means we are attaching the # lexer to a new object. In this case, we have to rebind all methods in # the lexstatere and lexstateerrorf tables. if object: newtab = { } for key, ritem in self.lexstatere.items(): newre = [] for cre, findex in ritem: newfindex = [] for f in findex: if not f or not f[0]: newfindex.append(f) continue newfindex.append((getattr(object,f[0].__name__),f[1])) newre.append((cre,newfindex)) newtab[key] = newre c.lexstatere = newtab c.lexstateerrorf = { } for key, ef in self.lexstateerrorf.items(): c.lexstateerrorf[key] = getattr(object,ef.__name__) c.lexmodule = object return c # ------------------------------------------------------------ # writetab() - Write lexer information to a table file # ------------------------------------------------------------ def writetab(self,tabfile,outputdir=""): if isinstance(tabfile,types.ModuleType): return basetabfilename = tabfile.split(".")[-1] filename = os.path.join(outputdir,basetabfilename)+".py" tf = open(filename,"w") tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__)) tf.write("_lextokens = %s\n" % repr(self.lextokens)) tf.write("_lexreflags = %s\n" % repr(self.lexreflags)) tf.write("_lexliterals = %s\n" % repr(self.lexliterals)) tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo)) tabre = { } # Collect all functions in the initial state initial = self.lexstatere["INITIAL"] initialfuncs = [] for part in initial: for f in part[1]: if f and f[0]: initialfuncs.append(f) for key, lre in self.lexstatere.items(): titem = [] for i in range(len(lre)): titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i]))) tabre[key] = titem tf.write("_lexstatere = %s\n" % repr(tabre)) tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore)) taberr = { } for key, ef in self.lexstateerrorf.items(): if ef: taberr[key] = ef.__name__ else: taberr[key] = None tf.write("_lexstateerrorf = %s\n" % repr(taberr)) tf.close() # ------------------------------------------------------------ # readtab() - Read lexer information from a tab file # ------------------------------------------------------------ def readtab(self,tabfile,fdict): if isinstance(tabfile,types.ModuleType): lextab = tabfile else: exec "import %s as lextab" % tabfile self.lextokens = lextab._lextokens self.lexreflags = lextab._lexreflags self.lexliterals = lextab._lexliterals self.lexstateinfo = lextab._lexstateinfo self.lexstateignore = lextab._lexstateignore self.lexstatere = { } self.lexstateretext = { } for key,lre in lextab._lexstatere.items(): titem = [] txtitem = [] for i in range(len(lre)): titem.append((re.compile(lre[i][0],lextab._lexreflags),_names_to_funcs(lre[i][1],fdict))) txtitem.append(lre[i][0]) self.lexstatere[key] = titem self.lexstateretext[key] = txtitem self.lexstateerrorf = { } for key,ef in lextab._lexstateerrorf.items(): self.lexstateerrorf[key] = fdict[ef] self.begin('INITIAL') # ------------------------------------------------------------ # input() - Push a new string into the lexer # ------------------------------------------------------------ def input(self,s): # Pull off the first character to see if s looks like a string c = s[:1] if not (isinstance(c,types.StringType) or isinstance(c,types.UnicodeType)): raise ValueError, "Expected a string" self.lexdata = s self.lexpos = 0 self.lexlen = len(s) # ------------------------------------------------------------ # begin() - Changes the lexing state # ------------------------------------------------------------ def begin(self,state): if not self.lexstatere.has_key(state): raise ValueError, "Undefined state" self.lexre = self.lexstatere[state] self.lexretext = self.lexstateretext[state] self.lexignore = self.lexstateignore.get(state,"") self.lexerrorf = self.lexstateerrorf.get(state,None) self.lexstate = state # ------------------------------------------------------------ # push_state() - Changes the lexing state and saves old on stack # ------------------------------------------------------------ def push_state(self,state): self.lexstatestack.append(self.lexstate) self.begin(state) # ------------------------------------------------------------ # pop_state() - Restores the previous state # ------------------------------------------------------------ def pop_state(self): self.begin(self.lexstatestack.pop()) # ------------------------------------------------------------ # current_state() - Returns the current lexing state # ------------------------------------------------------------ def current_state(self): return self.lexstate # ------------------------------------------------------------ # skip() - Skip ahead n characters # ------------------------------------------------------------ def skip(self,n): self.lexpos += n # ------------------------------------------------------------ # token() - Return the next token from the Lexer # # Note: This function has been carefully implemented to be as fast # as possible. Don't make changes unless you really know what # you are doing # ------------------------------------------------------------ def token(self): # Make local copies of frequently referenced attributes lexpos = self.lexpos lexlen = self.lexlen lexignore = self.lexignore lexdata = self.lexdata while lexpos < lexlen: # This code provides some short-circuit code for whitespace, tabs, and other ignored characters if lexdata[lexpos] in lexignore: lexpos += 1 continue # Look for a regular expression match for lexre,lexindexfunc in self.lexre: m = lexre.match(lexdata,lexpos) if not m: continue # Create a token for return tok = LexToken() tok.lexer = self tok.value = m.group() tok.lineno = self.lineno tok.lexpos = lexpos i = m.lastindex func,tok.type = lexindexfunc[i] if not func: # If no token type was set, it's an ignored token if tok.type: self.lexpos = m.end() return tok else: lexpos = m.end() break lexpos = m.end() # if func not callable, it means it's an ignored token if not callable(func): break # If token is processed by a function, call it # Set additional attributes useful in token rules self.lexmatch = m self.lexpos = lexpos newtok = func(tok) # Every function must return a token, if nothing, we just move to next token if not newtok: lexpos = self.lexpos # This is here in case user has updated lexpos. lexignore = self.lexignore # This is here in case there was a state change break # Verify type of the token. If not in the token map, raise an error if not self.lexoptimize: if not self.lextokens.has_key(newtok.type): raise LexError, ("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( func.func_code.co_filename, func.func_code.co_firstlineno, func.__name__, newtok.type),lexdata[lexpos:]) return newtok else: # No match, see if in literals if lexdata[lexpos] in self.lexliterals: tok = LexToken() tok.lexer = self tok.value = lexdata[lexpos] tok.lineno = self.lineno tok.type = tok.value tok.lexpos = lexpos self.lexpos = lexpos + 1 return tok # No match. Call t_error() if defined. if self.lexerrorf: tok = LexToken() tok.value = self.lexdata[lexpos:] tok.lineno = self.lineno tok.type = "error" tok.lexer = self tok.lexpos = lexpos self.lexpos = lexpos newtok = self.lexerrorf(tok) if lexpos == self.lexpos: # Error method didn't change text position at all. This is an error. raise LexError, ("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) lexpos = self.lexpos if not newtok: continue return newtok self.lexpos = lexpos raise LexError, ("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) self.lexpos = lexpos + 1 if self.lexdata is None: raise RuntimeError, "No input string given with input()" return None # ----------------------------------------------------------------------------- # _validate_file() # # This checks to see if there are duplicated t_rulename() functions or strings # in the parser input file. This is done using a simple regular expression # match on each line in the given file. If the file can't be located or opened, # a true result is returned by default. # ----------------------------------------------------------------------------- def _validate_file(filename): import os.path base,ext = os.path.splitext(filename) if ext != '.py': return 1 # No idea what the file is. Return OK try: f = open(filename) lines = f.readlines() f.close() except IOError: return 1 # Couldn't find the file. Don't worry about it fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') counthash = { } linen = 1 noerror = 1 for l in lines: m = fre.match(l) if not m: m = sre.match(l) if m: name = m.group(1) prev = counthash.get(name) if not prev: counthash[name] = linen else: print >>sys.stderr, "%s:%d: Rule %s redefined. Previously defined on line %d" % (filename,linen,name,prev) noerror = 0 linen += 1 return noerror # ----------------------------------------------------------------------------- # _funcs_to_names() # # Given a list of regular expression functions, this converts it to a list # suitable for output to a table file # ----------------------------------------------------------------------------- def _funcs_to_names(funclist,namelist): result = [] for f,name in zip(funclist,namelist): if f and f[0]: result.append((name, f[1])) else: result.append(f) return result # ----------------------------------------------------------------------------- # _names_to_funcs() # # Given a list of regular expression function names, this converts it back to # functions. # ----------------------------------------------------------------------------- def _names_to_funcs(namelist,fdict): result = [] for n in namelist: if n and n[0]: result.append((fdict[n[0]],n[1])) else: result.append(n) return result # ----------------------------------------------------------------------------- # _form_master_re() # # This function takes a list of all of the regex components and attempts to # form the master regular expression. Given limitations in the Python re # module, it may be necessary to break the master regex into separate expressions. # ----------------------------------------------------------------------------- def _form_master_re(relist,reflags,ldict,toknames): if not relist: return [] regex = "|".join(relist) try: lexre = re.compile(regex,re.VERBOSE | reflags) # Build the index to function map for the matching engine lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) lexindexnames = lexindexfunc[:] for f,i in lexre.groupindex.items(): handle = ldict.get(f,None) if type(handle) in (types.FunctionType, types.MethodType): lexindexfunc[i] = (handle,toknames[f]) lexindexnames[i] = f elif handle is not None: lexindexnames[i] = f if f.find("ignore_") > 0: lexindexfunc[i] = (None,None) else: lexindexfunc[i] = (None, toknames[f]) return [(lexre,lexindexfunc)],[regex],[lexindexnames] except Exception,e: m = int(len(relist)/2) if m == 0: m = 1 llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) return llist+rlist, lre+rre, lnames+rnames # ----------------------------------------------------------------------------- # def _statetoken(s,names) # # Given a declaration name s of the form "t_" and a dictionary whose keys are # state names, this function returns a tuple (states,tokenname) where states # is a tuple of state names and tokenname is the name of the token. For example, # calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') # ----------------------------------------------------------------------------- def _statetoken(s,names): nonstate = 1 parts = s.split("_") for i in range(1,len(parts)): if not names.has_key(parts[i]) and parts[i] != 'ANY': break if i > 1: states = tuple(parts[1:i]) else: states = ('INITIAL',) if 'ANY' in states: states = tuple(names.keys()) tokenname = "_".join(parts[i:]) return (states,tokenname) # ----------------------------------------------------------------------------- # lex(module) # # Build all of the regular expression rules from definitions in the supplied module # ----------------------------------------------------------------------------- def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir=""): global lexer ldict = None stateinfo = { 'INITIAL' : 'inclusive'} error = 0 files = { } lexobj = Lexer() lexobj.lexdebug = debug lexobj.lexoptimize = optimize global token,input if nowarn: warn = 0 else: warn = 1 if object: module = object if module: # User supplied a module object. if isinstance(module, types.ModuleType): ldict = module.__dict__ elif isinstance(module, _INSTANCETYPE): _items = [(k,getattr(module,k)) for k in dir(module)] ldict = { } for (i,v) in _items: ldict[i] = v else: raise ValueError,"Expected a module or instance" lexobj.lexmodule = module else: # No module given. We might be able to get information from the caller. try: raise RuntimeError except RuntimeError: e,b,t = sys.exc_info() f = t.tb_frame f = f.f_back # Walk out to our calling function if f.f_globals is f.f_locals: # Collect global and local variations from caller ldict = f.f_globals else: ldict = f.f_globals.copy() ldict.update(f.f_locals) if optimize and lextab: try: lexobj.readtab(lextab,ldict) token = lexobj.token input = lexobj.input lexer = lexobj return lexobj except ImportError: pass # Get the tokens, states, and literals variables (if any) tokens = ldict.get("tokens",None) states = ldict.get("states",None) literals = ldict.get("literals","") if not tokens: raise SyntaxError,"lex: module does not define 'tokens'" if not (isinstance(tokens,types.ListType) or isinstance(tokens,types.TupleType)): raise SyntaxError,"lex: tokens must be a list or tuple." # Build a dictionary of valid token names lexobj.lextokens = { } if not optimize: for n in tokens: if not _is_identifier.match(n): print >>sys.stderr, "lex: Bad token name '%s'" % n error = 1 if warn and lexobj.lextokens.has_key(n): print >>sys.stderr, "lex: Warning. Token '%s' multiply defined." % n lexobj.lextokens[n] = None else: for n in tokens: lexobj.lextokens[n] = None if debug: print "lex: tokens = '%s'" % lexobj.lextokens.keys() try: for c in literals: if not (isinstance(c,types.StringType) or isinstance(c,types.UnicodeType)) or len(c) > 1: print >>sys.stderr, "lex: Invalid literal %s. Must be a single character" % repr(c) error = 1 continue except TypeError: print >>sys.stderr, "lex: Invalid literals specification. literals must be a sequence of characters." error = 1 lexobj.lexliterals = literals # Build statemap if states: if not (isinstance(states,types.TupleType) or isinstance(states,types.ListType)): print >>sys.stderr, "lex: states must be defined as a tuple or list." error = 1 else: for s in states: if not isinstance(s,types.TupleType) or len(s) != 2: print >>sys.stderr, "lex: invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')" % repr(s) error = 1 continue name, statetype = s if not isinstance(name,types.StringType): print >>sys.stderr, "lex: state name %s must be a string" % repr(name) error = 1 continue if not (statetype == 'inclusive' or statetype == 'exclusive'): print >>sys.stderr, "lex: state type for state %s must be 'inclusive' or 'exclusive'" % name error = 1 continue if stateinfo.has_key(name): print >>sys.stderr, "lex: state '%s' already defined." % name error = 1 continue stateinfo[name] = statetype # Get a list of symbols with the t_ or s_ prefix tsymbols = [f for f in ldict.keys() if f[:2] == 't_' ] # Now build up a list of functions and a list of strings funcsym = { } # Symbols defined as functions strsym = { } # Symbols defined as strings toknames = { } # Mapping of symbols to token names for s in stateinfo.keys(): funcsym[s] = [] strsym[s] = [] ignore = { } # Ignore strings by state errorf = { } # Error functions by state if len(tsymbols) == 0: raise SyntaxError,"lex: no rules of the form t_rulename are defined." for f in tsymbols: t = ldict[f] states, tokname = _statetoken(f,stateinfo) toknames[f] = tokname if callable(t): for s in states: funcsym[s].append((f,t)) elif (isinstance(t, types.StringType) or isinstance(t,types.UnicodeType)): for s in states: strsym[s].append((f,t)) else: print >>sys.stderr, "lex: %s not defined as a function or string" % f error = 1 # Sort the functions by line number for f in funcsym.values(): f.sort(lambda x,y: cmp(x[1].func_code.co_firstlineno,y[1].func_code.co_firstlineno)) # Sort the strings by regular expression length for s in strsym.values(): s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) regexs = { } # Build the master regular expressions for state in stateinfo.keys(): regex_list = [] # Add rules defined by functions first for fname, f in funcsym[state]: line = f.func_code.co_firstlineno file = f.func_code.co_filename files[file] = None tokname = toknames[fname] ismethod = isinstance(f, types.MethodType) if not optimize: nargs = f.func_code.co_argcount if ismethod: reqargs = 2 else: reqargs = 1 if nargs > reqargs: print >>sys.stderr, "%s:%d: Rule '%s' has too many arguments." % (file,line,f.__name__) error = 1 continue if nargs < reqargs: print >>sys.stderr, "%s:%d: Rule '%s' requires an argument." % (file,line,f.__name__) error = 1 continue if tokname == 'ignore': print >>sys.stderr, "%s:%d: Rule '%s' must be defined as a string." % (file,line,f.__name__) error = 1 continue if tokname == 'error': errorf[state] = f continue if f.__doc__: if not optimize: try: c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | reflags) if c.match(""): print >>sys.stderr, "%s:%d: Regular expression for rule '%s' matches empty string." % (file,line,f.__name__) error = 1 continue except re.error,e: print >>sys.stderr, "%s:%d: Invalid regular expression for rule '%s'. %s" % (file,line,f.__name__,e) if '#' in f.__doc__: print >>sys.stderr, "%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'." % (file,line, f.__name__) error = 1 continue if debug: print "lex: Adding rule %s -> '%s' (state '%s')" % (f.__name__,f.__doc__, state) # Okay. The regular expression seemed okay. Let's append it to the master regular # expression we're building regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) else: print >>sys.stderr, "%s:%d: No regular expression defined for rule '%s'" % (file,line,f.__name__) # Now add all of the simple rules for name,r in strsym[state]: tokname = toknames[name] if tokname == 'ignore': if "\\" in r: print >>sys.stderr, "lex: Warning. %s contains a literal backslash '\\'" % name ignore[state] = r continue if not optimize: if tokname == 'error': raise SyntaxError,"lex: Rule '%s' must be defined as a function" % name error = 1 continue if not lexobj.lextokens.has_key(tokname) and tokname.find("ignore_") < 0: print >>sys.stderr, "lex: Rule '%s' defined for an unspecified token %s." % (name,tokname) error = 1 continue try: c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | reflags) if (c.match("")): print >>sys.stderr, "lex: Regular expression for rule '%s' matches empty string." % name error = 1 continue except re.error,e: print >>sys.stderr, "lex: Invalid regular expression for rule '%s'. %s" % (name,e) if '#' in r: print >>sys.stderr, "lex: Make sure '#' in rule '%s' is escaped with '\\#'." % name error = 1 continue if debug: print "lex: Adding rule %s -> '%s' (state '%s')" % (name,r,state) regex_list.append("(?P<%s>%s)" % (name,r)) if not regex_list: print >>sys.stderr, "lex: No rules defined for state '%s'" % state error = 1 regexs[state] = regex_list if not optimize: for f in files.keys(): if not _validate_file(f): error = 1 if error: raise SyntaxError,"lex: Unable to build lexer." # From this point forward, we're reasonably confident that we can build the lexer. # No more errors will be generated, but there might be some warning messages. # Build the master regular expressions for state in regexs.keys(): lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,toknames) lexobj.lexstatere[state] = lexre lexobj.lexstateretext[state] = re_text lexobj.lexstaterenames[state] = re_names if debug: for i in range(len(re_text)): print "lex: state '%s'. regex[%d] = '%s'" % (state, i, re_text[i]) # For inclusive states, we need to add the INITIAL state for state,type in stateinfo.items(): if state != "INITIAL" and type == 'inclusive': lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) lexobj.lexstateinfo = stateinfo lexobj.lexre = lexobj.lexstatere["INITIAL"] lexobj.lexretext = lexobj.lexstateretext["INITIAL"] # Set up ignore variables lexobj.lexstateignore = ignore lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") # Set up error functions lexobj.lexstateerrorf = errorf lexobj.lexerrorf = errorf.get("INITIAL",None) if warn and not lexobj.lexerrorf: print >>sys.stderr, "lex: Warning. no t_error rule is defined." # Check state information for ignore and error rules for s,stype in stateinfo.items(): if stype == 'exclusive': if warn and not errorf.has_key(s): print >>sys.stderr, "lex: Warning. no error rule is defined for exclusive state '%s'" % s if warn and not ignore.has_key(s) and lexobj.lexignore: print >>sys.stderr, "lex: Warning. no ignore rule is defined for exclusive state '%s'" % s elif stype == 'inclusive': if not errorf.has_key(s): errorf[s] = errorf.get("INITIAL",None) if not ignore.has_key(s): ignore[s] = ignore.get("INITIAL","") # Create global versions of the token() and input() functions token = lexobj.token input = lexobj.input lexer = lexobj # If in optimize mode, we write the lextab if lextab and optimize: lexobj.writetab(lextab,outputdir) return lexobj # ----------------------------------------------------------------------------- # runmain() # # This runs the lexer as a main program # ----------------------------------------------------------------------------- def runmain(lexer=None,data=None): if not data: try: filename = sys.argv[1] f = open(filename) data = f.read() f.close() except IndexError: print "Reading from standard input (type EOF to end):" data = sys.stdin.read() if lexer: _input = lexer.input else: _input = input _input(data) if lexer: _token = lexer.token else: _token = token while 1: tok = _token() if not tok: break print "(%s,%r,%d,%d)" % (tok.type, tok.value, tok.lineno,tok.lexpos) # ----------------------------------------------------------------------------- # @TOKEN(regex) # # This decorator function can be used to set the regex expression on a function # when its docstring might need to be set in an alternative way # ----------------------------------------------------------------------------- def TOKEN(r): def set_doc(f): if callable(r): f.__doc__ = r.__doc__ else: f.__doc__ = r return f return set_doc # Alternative spelling of the TOKEN decorator Token = TOKEN pywbem-0.7.0/cim_provider2.py0000644000175000001440000024736711102360476015031 0ustar bartusers# # (C) Copyright 2003-2007 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Bart Whiteley # Jon Carey #### """Python CIM Providers (aka "nirvana") This module is an abstraction and utility layer between a CIMOM and Python providers. The CIMOM uses this module to load Python providers, and route requests to those providers. Python Provider Modules Python Providers are implemented as Python modules. By convention these modules are installed into /usr/lib/pycim. However, they can be anywhere. These modules are loaded on demand using load_module() from the imp module. The CIMOM's pycim interface stores the timestamp of the provider modules. If the modules change, the CIMOM reloads the modules. This is very useful while developing providers, since the latest code will always be loaded and used. A Python Provider Module will contain functions, attributes, and instances that will be accessed and manipulated by this module. Providers are often classified in the following catagories: Instance -- Instrument the retrieval, creation, modification, and deletion of CIM instances. Association -- Instrument CIM associations (CIM classes with the Association qualifier). Method -- Instrument methods as defined on CIM instances or CIM classes. Indication -- Generates indications based on indication subscriptions. Indication Consumer -- "Consumes" (or "Handles") an indication, possibly delivering it through some other means, such as email. Polled -- A polled provider is allowed to run periodically (by calling its poll function). This allows a provider to do some periodic work, without the need to create its own thread. An Instance, Association, and/or Method provider is created by defining one or more subclasses of CIMProvider2 within the provider module, and registering instances of the subclass(es) with CIM class names by way of the get_providers function (described below). Refer to the documentation for CIMProvider2 in this module. Indication, Indication Consumer, and Polled providers are defined by implementing some functions within the provider module. Provider module functions: init(env): This module function is optional. It is called immediately after the provider module is imported. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) get_providers(env): Return a dict that maps CIM class names to instances of CIMProvider2 subclasses. Note that multiple classes can be instrumented by the same instance of a CIMProvider2 subclass. The CIM class names are case-insensitive, since this dict is converted to a NocaseDict. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) For example, a Python Provider Module may contain the following: class Py_FooBarProvider(CIMProvider2): ... def get_providers(env): _fbp = Py_FooBarProvider() return {'Py_Foo':_fbp, 'Py_Bar':_fbp} can_unload(env): Return True if the provider can be unloaded. The CIMOM may try to unload a provider after a period of inactivity. Before unloading a provider, the CIMOM asks the provider if it can be unloaded. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) shutdown(env): Perform any cleanup tasks prior to being unloaded. The provider will shortly be unloaded, and is given an opportunity to perform any needed cleanup. The provider may be unloaded after a period of inactivity (see the documentation for can_unload), or because the CIMOM is shutting down. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) handle_indication(env, ns, handler_instance, indication_instance): Process an indication. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) ns -- The namespace where the event occurred handler_instance -- indication_instance -- The indication authorize_filter (env, filter, ns, classes, owner): Allow or disallow an indication subscription request. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- The WQL select statement namespace -- The namepace where the indication is registered for classes -- The classpath of the indication registered for owner -- The name of the principal (cimom user) activate_filter (env, filter, ns, classes, first_activation): Activate an indication subscription. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- The WQL select statement namespace -- The namepace where the indication is registered for classes -- The classpath of the indication registered for first_activation -- boolean - whether first activation deactivate_filter(env, filter, ns, classes, last_activation): Deactivate an indication subscription. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- The WQL select statement ns -- The namepace where the indication is registered for classes -- The classpath of the indication registered for last_activation -- boolean - whether last activation enable_indications(env): Enable indications. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) disable_indications(env): Disable indications. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) Provider Environment A pycimmb.ProviderEnvironment is passed to many functions. This is a handle back into the CIMOM. You can use it for logging and for making "up-calls" to the CIMOM. For example: logger = env.get_logger() logger.log_debug('Debug Info') ch = env.get_cimom_handle() other_inst = ch.GetInstance(inst_path, LocalOnly=False, IncludeQualifiers=False, IncludeClassOrigin=False) The API of the pycimmb.CIMOMHandle resembles that of pywbem.WBEMConnection. For more information on the ProviderEnvironments, and other features provided by pycimmb, refer to the pycimmb documentation. CodeGen The codegen function can be used to generate provider stub code for a given CIM class. This is a quick way to get started writing a provider. """ import sys from os.path import dirname import pywbem import types import sys # for sys.modules import os import imp import threading g_mod_lock = threading.RLock() __all__ = ['CIMProvider2', 'codegen'] def _paths_equal(lhs, rhs): """If one object path doesn't inlcude a host, don't include the hosts in the comparison """ if lhs is rhs: return True if lhs.host is not None and rhs.host is not None and lhs.host != rhs.host: return False # need to make sure this stays in sync with CIMInstanceName.__cmp__() return not (pywbem.cmpname(rhs.classname, lhs.classname) or cmp(rhs.keybindings, lhs.keybindings) or pywbem.cmpname(rhs.namespace, lhs.namespace)) class CIMProvider2(object): """Base class for CIM Providers. A derived class might normally override the following: - enum_instances - get_instance - set_instance - delete_instance - references If the provider is a "read-only" instance provider, set_instance and delete_instance need not be overridden. Only association providers need to override references. A method provider should implement a method of the form: def cim_method_(self, env, object_name, method, param_, param_, ...): Where is the name of the method from the CIM schema. needs to be all lowercase, regardless of the case of the method name in the CIM schema (CIM method names are case insensitive). Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName or pywbem.CIMClassname specifying the object on which the method is to be invoked. method -- A pywbem.CIMMethod, representing the method to execute. param_ -- Corresponds to the input parameter from the CIM schema. needs to be all lowercase, regardless of the case of the parameter name in the CIM schema (CIM parameter names are case insensitive). The method returns a two-tuple containing the return value of the method, and a dictionary containing the output parameters. Example: def cim_method_requeststatechange(self, env, object_name, method, param_requestedstate, param_timeoutperiod): # do stuff. out_params = {'job': pywbem.CIMInstanceName(...)} rval = pywbem.Uint32(0) return (rval, out_params) The methods prefixed with "MI_" correspond to the WBEM operations from http://www.dmtf.org/standards/published_documents/DSP200.html The default implementations of these methods call the methods described above. These will not normally be overridden or extended by a subclass. """ def get_instance (self, env, model): """Return an instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) model -- A template of the pywbem.CIMInstance to be returned. The key properties are set on this instance to correspond to the instanceName that was requested. The properties of the model are already filtered according to the PropertyList from the request. Only properties present in the model need to be given values. If you prefer, you can set all of the values, and the instance will be filtered for you. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) """ return None def enum_instances(self, env, model, keys_only): """Enumerate instances. The WBEM operations EnumerateInstances and EnumerateInstanceNames are both mapped to this method. This method is a python generator Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) model -- A template of the pywbem.CIMInstances to be generated. The properties of the model are already filtered according to the PropertyList from the request. Only properties present in the model need to be given values. If you prefer, you can always set all of the values, and the instance will be filtered for you. keys_only -- A boolean. True if only the key properties should be set on the generated instances. Possible Errors: CIM_ERR_FAILED (some other unspecified error occurred) """ pass def set_instance(self, env, instance, modify_existing): """Return a newly created or modified instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) instance -- The new pywbem.CIMInstance. If modifying an existing instance, the properties on this instance have been filtered by the PropertyList from the request. modify_existing -- True if ModifyInstance, False if CreateInstance Return the new instance. The keys must be set on the new instance. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only valid if modify_existing is False, indicating that the operation was CreateInstance) CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid if modify_existing is True, indicating that the operation was ModifyInstance) CIM_ERR_FAILED (some other unspecified error occurred) """ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, "") def delete_instance(self, env, instance_name): """Delete an instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) instance_name -- A pywbem.CIMInstanceName specifying the instance to delete. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_NAMESPACE CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified namespace) CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) """ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, "") def references(self, env, object_name, model, result_class_name, role, result_role, keys_only): """Instrument Associations. All four association-related operations (Associators, AssociatorNames, References, ReferenceNames) are mapped to this method. This method is a python generator Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName that defines the source CIM Object whose associated Objects are to be returned. model -- A template pywbem.CIMInstance to serve as a model of the objects to be returned. Only properties present on this model need to be set. result_class_name -- If not empty, this string acts as a filter on the returned set of Instances by mandating that each returned Instances MUST represent an association between object_name and an Instance of a Class whose name matches this parameter or a subclass. role -- If not empty, MUST be a valid Property name. It acts as a filter on the returned set of Instances by mandating that each returned Instance MUST refer to object_name via a Property whose name matches the value of this parameter. result_role -- If not empty, MUST be a valid Property name. It acts as a filter on the returned set of Instances by mandating that each returned Instance MUST represent associations of object_name to other Instances, where the other Instances play the specified result_role in the association (i.e. the name of the Property in the Association Class that refers to the Object related to object_name MUST match the value of this parameter). keys_only -- A boolean. True if only the key properties should be set on the generated instances. The following diagram may be helpful in understanding the role, result_role, and result_class_name parameters. +------------------------+ +-------------------+ | object_name.classname | | result_class_name | | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ | +------------------------+ +-------------------+ | +-----------------------------------+ | | | [Association] model.classname | | | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | +--------------+ object_name.classname REF role | | (CIMInstanceName) | result_class_name REF result_role +------+ | |(CIMInstanceName) +-----------------------------------+ Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_NAMESPACE CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_FAILED (some other unspecified error occurred) """ # Don't change this return value. If affects the behavior # of the MI_* methods. return None def simple_refs(self, env, object_name, model, result_class_name, role, result_role, keys_only): gen = self.enum_instances(env, model, keys_only) for inst in gen: for prop in inst.properties.values(): if prop.type != 'reference': continue if role and prop.name.lower() != role: continue if self.paths_equal(object_name, prop.value): yield inst def paths_equal(self, lhs, rhs): """If one object path doesn't inlcude a host, don't include the hosts in the comparison """ if lhs is rhs: return True if lhs.host is not None and rhs.host is not None and lhs.host != rhs.host: return False # need to make sure this stays in sync with CIMInstanceName.__cmp__() return not (pywbem.cmpname(rhs.classname, lhs.classname) or cmp(rhs.keybindings, lhs.keybindings) or pywbem.cmpname(rhs.namespace, lhs.namespace)) def MI_enumInstanceNames(self, env, objPath): """Return instance names of a given CIM class Implements the WBEM operation EnumerateInstanceNames in terms of the enum_instances method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_enumInstanceNames called...') model = pywbem.CIMInstance(classname=objPath.classname, path=objPath) gen = self.enum_instances(env=env, model=model, keys_only=True) try: iter(gen) except TypeError: logger.log_debug('CIMProvider2 MI_enumInstanceNames returning') return for inst in gen: yield inst.path logger.log_debug('CIMProvider2 MI_enumInstanceNames returning') def MI_enumInstances(self, env, objPath, propertyList): """Return instances of a given CIM class Implements the WBEM operation EnumerateInstances in terms of the enum_instances method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_enumInstances called...') model = pywbem.CIMInstance(classname=objPath.classname, path=objPath) gen = self.enum_instances(env=env, model=model, keys_only=False) try: iter(gen) except TypeError: logger.log_debug('CIMProvider2 MI_enumInstances returning') return return gen def MI_getInstance(self, env, instanceName, propertyList): """Return a specific CIM instance Implements the WBEM operation GetInstance in terms of the get_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_getInstance called...') plist = None if propertyList is not None: plist = [s.lower() for s in propertyList] plist+= [s.lower() for s in instanceName.keybindings.keys()] model = pywbem.CIMInstance(classname=instanceName.classname, path=instanceName, property_list=plist) model.update(model.path.keybindings) rval = self.get_instance(env=env, model=model) logger.log_debug('CIMProvider2 MI_getInstance returning') if rval is None: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "") return rval def MI_createInstance(self, env, instance): """Create a CIM instance, and return its instance name Implements the WBEM operation CreateInstance in terms of the set_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_createInstance called...') rval = None ''' ch = env.get_cimom_handle() cimClass = ch.GetClass(instance.classname, instance.path.namespace, LocalOnly=False, IncludeQualifiers=True) ''' # CIMOM has already filled in default property values for # props with default values, if values not supplied by client. rval = self.set_instance(env=env, instance=instance, modify_existing=False) logger.log_debug('CIMProvider2 MI_createInstance returning') return rval.path def MI_modifyInstance(self, env, modifiedInstance, propertyList): """Modify a CIM instance Implements the WBEM operation ModifyInstance in terms of the set_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_modifyInstance called...') plist = None if propertyList is not None: plist = [s.lower() for s in propertyList] plist+= [s.lower() for s in modifiedInstance.path.keybindings.keys()] self.filter_instance(modifiedInstance, plist) modifiedInstance.property_list = plist modifiedInstance.update(modifiedInstance.path) self.set_instance(env=env, instance=modifiedInstance, modify_existing=True) logger.log_debug('CIMProvider2 MI_modifyInstance returning') def MI_deleteInstance(self, env, instanceName): """Delete a CIM instance Implements the WBEM operation DeleteInstance in terms of the delete_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_deleteInstance called...') self.delete_instance(env=env, instance_name=instanceName) logger.log_debug('CIMProvider2 MI_deleteInstance returning') def MI_associators(self, env, objectName, assocClassName, resultClassName, role, resultRole, propertyList): """Return instances associated to a given object. Implements the WBEM operation Associators in terms of the references method. A derived class will not normally override this method. """ # NOTE: This should honor the parameters resultClassName, role, resultRole, # and propertyList logger = env.get_logger() logger.log_debug('CIMProvider2 MI_associators called. assocClass: %s' % (assocClassName)) if not assocClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty assocClassName passed to Associators") ch = env.get_cimom_handle() model = pywbem.CIMInstance(classname=assocClassName) model.path = pywbem.CIMInstanceName(classname=assocClassName, namespace=objectName.namespace) gen = self.references(env=env, object_name=objectName, model=model, result_class_name=resultClassName, role=role, result_role=None, keys_only=False) if gen is None: logger.log_debug('references() returned None instead of generator object') return for inst in gen: for prop in inst.properties.values(): lpname = prop.name.lower() if prop.type != 'reference': continue if role and role.lower() == lpname: continue if resultRole and resultRole.lower() != lpname: continue if self.paths_equal(prop.value, objectName): continue if resultClassName and \ resultClassName.lower() != prop.value.classname.lower(): continue try: if prop.value.namespace is None: prop.value.namespace = objectName.namespace inst = ch.GetInstance(prop.value, propertyList) except pywbem.CIMError, (num, msg): if num == pywbem.CIM_ERR_NOT_FOUND: continue else: raise if inst.path is None: inst.path = prop.value yield inst logger.log_debug('CIMProvider2 MI_associators returning') def MI_associatorNames(self, env, objectName, assocClassName, resultClassName, role, resultRole): """Return instances names associated to a given object. Implements the WBEM operation AssociatorNames in terms of the references method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_associatorNames called. assocClass: %s' % (assocClassName)) if not assocClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty assocClassName passed to AssociatorNames") model = pywbem.CIMInstance(classname=assocClassName) model.path = pywbem.CIMInstanceName(classname=assocClassName, namespace=objectName.namespace) gen = self.references(env=env, object_name=objectName, model=model, result_class_name=resultClassName, role=role, result_role=None, keys_only=False) if gen is None: logger.log_debug('references() returned None instead of generator object') return for inst in gen: for prop in inst.properties.values(): lpname = prop.name.lower() if prop.type != 'reference': continue if role and role.lower() == lpname: continue if resultRole and resultRole.lower() != lpname: continue if self.paths_equal(prop.value, objectName): continue if resultClassName and \ resultClassName.lower() != prop.value.classname.lower(): continue if prop.value.namespace is None: prop.value.namespace = objectName.namespace yield prop.value logger.log_debug('CIMProvider2 MI_associatorNames returning') def MI_references(self, env, objectName, resultClassName, role, propertyList): """Return instances of an association class. Implements the WBEM operation References in terms of the references method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_references called. resultClass: %s' % (resultClassName)) if not resultClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty resultClassName passed to References") plist = None if propertyList is not None: plist = [s.lower() for s in propertyList] model = pywbem.CIMInstance(classname=resultClassName, property_list=plist) model.path = pywbem.CIMInstanceName(classname=resultClassName, namespace=objectName.namespace) if role: if role not in model.properties: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "** this shouldn't happen") model[role] = objectName gen = self.references(env=env, object_name=objectName, model=model, result_class_name='', role=role, result_role=None, keys_only=False) if gen is None: logger.log_debug('references() returned None instead of generator object') return for inst in gen: for prop in inst.properties.values(): if hasattr(prop.value, 'namespace') and \ prop.value.namespace is None: prop.value.namespace = objectName.namespace yield inst logger.log_debug('CIMProvider2 MI_references returning') def MI_referenceNames(self, env, objectName, resultClassName, role): """Return instance names of an association class. Implements the WBEM operation ReferenceNames in terms of the references method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_referenceNames <2> called. resultClass: %s' % (resultClassName)) if not resultClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty resultClassName passed to ReferenceNames") model = pywbem.CIMInstance(classname=resultClassName) model.path = pywbem.CIMInstanceName(classname=resultClassName, namespace=objectName.namespace) if role: if role not in model.properties: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "** this shouldn't happen") model[role] = objectName gen = self.references(env=env, object_name=objectName, model=model, result_class_name='', role=role, result_role=None, keys_only=True) if gen is None: logger.log_debug('references() returned None instead of generator object') return for inst in gen: for prop in inst.properties.values(): if hasattr(prop.value, 'namespace') and prop.value.namespace is None: prop.value.namespace = objectName.namespace yield inst.path logger.log_debug('CIMProvider2 MI_referenceNames returning') def MI_invokeMethod(self, env, objectName, methodName, inputParams): """Invoke an extrinsic method. Implements the InvokeMethod WBEM operation by calling the method on a derived class called cim_method_, where is the name of the CIM method, in all lower case. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) objectName -- The InstanceName or ClassName of the object on which the method is invoked. methodName -- The name of the method to be invoked. inputParams -- A Dictionary where the key is the parameter name and the value is the parameter value. The return value for invokeMethod must be a tuple of size 2 where: element 0 is a tuple of size 2 where element 0 is the return data type name and element 1 is the actual data value. element 1 is a dictionary where the key is the output parameter name and the value is a tuple of size 2 where element 0 is the data type name for the output parameter and element 1 is the actual value of the output parameter. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider2 MI_invokeMethod called. method: %s:%s' \ % (objectName.classname,methodName)) lmethName = "cim_method_%s" % methodName.lower() if hasattr(self, lmethName) : method = getattr(self, lmethName) new_inputs = dict([('param_%s' % k.lower(), v) \ for k, v in inputParams.items()]) try: (rval, outs) = method(env=env, object_name=objectName, **new_inputs) except TypeError, e: raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, str(e)) def add_type(v): if isinstance(v, pywbem.CIMParameter): return (v.type, v.value) lv = v if type(v) == list and len(v) > 0: lv = v[0] if isinstance(lv, pywbem.CIMClass): tp = 'class' elif isinstance(lv, pywbem.CIMInstance): tp = 'instance' elif isinstance(lv, pywbem.CIMInstanceName): tp = 'reference' elif v is None or (type(v) == list and len(v) == 0): assert(None == 'This should not happen') else: tp = pywbem.cimtype(v) return (tp, v) louts = {} for op in outs: louts[op.name] = (op.type, op.value) rval = add_type(rval) rval = (rval, louts) else: raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_FOUND, "%s:%s"%(objectName.classname, methodName)) logger.log_debug('CIMProvider2 MI_invokeMethod returning') return rval def filter_instance(self, inst, plist): """Remove properties from an instance that aren't in the PropertyList inst -- The CIMInstance plist -- The property List, or None. The list items must be all lowercase. """ if plist is not None: for pname in inst.properties.keys(): if pname.lower() not in plist and pname: if inst.path is not None and pname in inst.path.keybindings: continue del inst.properties[pname] def authorize_filter (env, filter, ns, classes, owner): """Allow or disallow an indication subscription request. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- The WQL select statement namespace -- The namepace where the indication is registered for classes -- The classpath of the indication registered for owner -- The name of the principal (cimom user) """ pass def activate_filter (env, filter, ns, classes, first_activation): """Activate an indication subscription. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- The WQL select statement namespace -- The namepace where the indication is registered for classes -- The classpath of the indication registered for first_activation -- boolean - whether first activation """ pass def deactivate_filter(env, filter, ns, classes, last_activation): """Deactivate an indication subscription. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- The WQL select statement ns -- The namepace where the indication is registered for classes -- The classpath of the indication registered for last_activation -- boolean - whether last activation """ pass def enable_indications(env): """Enable indications. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) """ pass def disable_indications(env): """Disable indications. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) """ pass def codegen (cc): """Generate a Python Provider template. Parameters: cc - A CIMClass to generate code for. Returns a two-tuple containing the Python provider code stubs, and the provider registration MOF. """ import inspect def format_desc (obj, indent): linelen = 75 - indent if isinstance(obj, basestring): raw = obj else: try: raw = obj.qualifiers['description'].value except KeyError: return '' txt = '' beg = 0 end = 0 while beg < len(raw): beg = end end += linelen while beg < len(raw) and raw[beg].isspace(): beg = beg+1 while end < len(raw) and end > beg and not raw[end].isspace(): end = end-1 if beg == end: # a long URL while end < len(raw) and not raw[end].isspace(): end+= 1 line = raw[beg:end] line = line.replace('\n',' ') line = line.replace('\r','') txt += '\n%s%s'% (''.ljust(indent), line) return txt ################# def map_value(obj, val): rv = str(val) if 'ValueMap' not in obj.qualifiers: return rv if 'Values' not in obj.qualifiers: return rv vals = [str(x) for x in obj.qualifiers['Values'].value] maps = [str(x) for x in obj.qualifiers['ValueMap'].value] d = dict(zip(maps, vals)) try: tmp = d[str(val)] rv = '' for ch in tmp: rv+= ch.isalnum() and ch or '_' except KeyError: pass return rv ################# def type_hint (obj, method_name=None): if hasattr(obj, 'type'): tx = obj.type if 'embeddedinstance' in obj.qualifiers: tx = "pywbem.CIMInstance(classname='%s', ...)" % \ obj.qualifiers['embeddedinstance'].value elif tx == 'reference': tx = "pywbem.CIMInstanceName(classname='%s', ...)" % \ obj.reference_class else: tx = obj.return_type if hasattr(obj, 'value') and obj.value is not None: defval = str(obj.value) else: defval = '' if not tx.startswith('pywbem.'): if tx == 'boolean': tx = 'bool(%s)' % defval elif tx == 'datetime': tx = 'pywbem.CIMDateTime()' elif tx == 'string': tx = "''" else: tx = 'pywbem.%s(%s)' % (tx.capitalize(), defval) if 'valuemap' in obj.qualifiers: if defval: defval = map_value(obj, defval) else: defval = '' tx = 'self.Values.%s%s.%s' % \ (method_name and '%s.'%method_name or '', obj.name, defval) if hasattr(obj, 'is_array') and obj.is_array: tx = '[%s,]' % tx return tx ################# def type_str (obj, method_name=None): if hasattr(obj, 'type'): tx = obj.type if 'embeddedinstance' in obj.qualifiers: return "pywbem.CIMInstance(classname='%s', ...)" % \ obj.qualifiers['embeddedinstance'].value elif tx == 'reference': return "REF (pywbem.CIMInstanceName(classname='%s', ...)" % \ obj.reference_class else: tx = obj.return_type if tx == 'boolean': tx = 'bool' elif tx == 'datetime': tx = 'pywbem.CIMDateTime' elif tx == 'string': tx = 'unicode' else: tx = 'pywbem.%s' % tx.capitalize() if hasattr(obj, 'is_array') and obj.is_array: tx = '[%s,]' % tx if 'valuemap' in obj.qualifiers: tx+= ' self.Values.%s%s' % \ (method_name and '%s.'%method_name or '',obj.name) return tx ################# def is_required (obj): if 'required' in obj.qualifiers and obj.qualifiers['required'].value: return '(Required)' return '' ################# def build_val_map(obj): vm = obj.qualifiers['valuemap'].value if 'values' in obj.qualifiers: vals = obj.qualifiers['values'].value else: vals = vm tmap = zip(vals,vm) map = [] for t in tmap: nname = '' for ch in t[0]: if ch.isalnum(): nname+= ch else: nname+= '_' if hasattr(obj, 'return_type'): tp = obj.return_type else: tp = obj.type if tp == 'string': val = "'%s'" % t[1] else: try: int(t[1]) val = 'pywbem.%s(%s)' % (tp.capitalize(), t[1]) except ValueError: val = t[1] nname = "# "+nname map.append((nname,val)) return map valuemaps = {} for obj in cc.properties.values() + cc.methods.values(): if 'valuemap' in obj.qualifiers: valuemaps[obj.name] = {'':build_val_map(obj)} for meth in cc.methods.values(): for parm in meth.parameters.values(): if 'valuemap' in parm.qualifiers: if meth.name not in valuemaps: valuemaps[meth.name] = {} valuemaps[meth.name][parm.name] = build_val_map(parm) mappings = {'classname':cc.classname, 'classname_l':cc.classname.lower()} isAssoc = 'association' in cc.qualifiers isIndication = 'indication' in cc.qualifiers code = '''"""Python Provider for %(classname)s Instruments the CIM class %(classname)s """ import pywbem from pywbem.cim_provider2 import CIMProvider2 class %(classname)s(CIMProvider2): """Instrument the CIM class %(classname)s \n''' % mappings code+= format_desc(cc, 4) code+= ''' """''' args = inspect.getargspec(CIMProvider2.get_instance)[0] args = ', '.join(args) code+= ''' def __init__ (self, env): logger = env.get_logger() logger.log_debug('Initializing provider %%s from %%s' \\ %% (self.__class__.__name__, __file__)) def get_instance(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.get_instance()' \\ %% self.__class__.__name__) ''' % (args, CIMProvider2.get_instance.__doc__ ) keyProps = [p for p in cc.properties.values() \ if 'key' in p.qualifiers] if not keyProps and 'association' in cc.qualifiers: # SFCB has problems with qualifiers on REF properties. # http://sourceforge.net/tracker/index.php?func=detail&aid=2104565&group_id=128809&atid=712784 keyProps = [p for p in cc.properties.values() \ if p.type == 'reference'] for prop in keyProps: prop.qualifiers['KEY'] = True code+= ''' # TODO fetch system resource matching the following keys:''' for kp in keyProps: code+= ''' # model['%s']''' % kp.name code+= '\n' props = cc.properties.values() props.sort() for prop in props: if 'key' in prop.qualifiers: continue line = "#model['%s'] = %s # TODO %s" % \ (prop.name, type_hint(prop), is_required(prop)) code+= ''' %s''' % line args = inspect.getargspec(CIMProvider2.enum_instances)[0] args = ', '.join(args) code+= ''' return model def enum_instances(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.enum_instances()' \\ %% self.__class__.__name__) ''' % (args, CIMProvider2.enum_instances.__doc__) keydict = dict([(str(kp.name), None) for kp in keyProps]) code+= ''' # Prime model.path with knowledge of the keys, so key values on # the CIMInstanceName (model.path) will automatically be set when # we set property values on the model. model.pa%s ''' % format_desc('th.update('+str(keydict)+')', 12).strip() code+= ''' while False: # TODO more instances? # TODO fetch system resource # Key properties''' for kp in keyProps: if kp.name == 'CreationClassName': line = "model['%s'] = '%s'" % (kp.name, cc.classname) else: line = "#model['%s'] = %s # TODO (type = %s)" % \ (kp.name, type_hint(kp), type_str(kp)) code+=''' %s''' % line code+=''' if keys_only: yield model else: try: yield self.get_instance(env, model) except pywbem.CIMError, (num, msg): if num not in (pywbem.CIM_ERR_NOT_FOUND, pywbem.CIM_ERR_ACCESS_DENIED): raise\n''' args = inspect.getargspec(CIMProvider2.set_instance)[0] args = ', '.join(args) code+= ''' def set_instance(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.set_instance()' \\ %% self.__class__.__name__) # TODO create or modify the instance raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement return instance''' % (args, CIMProvider2.set_instance.__doc__) args = inspect.getargspec(CIMProvider2.delete_instance)[0] args = ', '.join(args) code+= ''' def delete_instance(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.delete_instance()' \\ %% self.__class__.__name__) # TODO delete the resource raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement ''' % (args, CIMProvider2.delete_instance.__doc__) for method in cc.methods.values(): inParms = [ p for p in method.parameters.values() if \ 'in' in p.qualifiers and p.qualifiers['in'].value ] outParms = [ p for p in method.parameters.values() if \ 'out' in p.qualifiers and p.qualifiers['out'].value ] code+= ''' def cim_method_%s(self, env, object_name''' % method.name.lower() for p in inParms: if 'required' in p.qualifiers and p.qualifiers['required']: code+= ''',\n%sparam_%s''' % (''.rjust(len(method.name)+20), p.name.lower()) for p in inParms: if 'required' not in p.qualifiers or not p.qualifiers['required']: code+= ''',\n%sparam_%s=None'''%\ (''.rjust(len(method.name)+20), p.name.lower()) code+= '''): """Implements %s.%s()\n''' % (cc.classname, method.name) code+= format_desc(method, 8) code+= ''' Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName specifying the object on which the method %s() should be invoked.'''\ % method.name for p in inParms: code+= ''' param_%s -- The input parameter %s (type %s) %s''' \ % (p.name.lower(), p.name, type_str(p, method.name), is_required(p)) code+= format_desc(p, 12) code+=''' Returns a two-tuple containing the return value (type %s) and a list of CIMParameter objects representing the output parameters Output parameters:''' % type_str(method) if not outParms: code+= ' none' else: for p in outParms: code+=''' %s -- (type %s) %s''' % (p.name, type_str(p, method.name), is_required(p)) code+= format_desc(p, 12) code+=''' Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_NOT_FOUND (the target CIM Class or instance does not exist in the specified namespace) CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor the invocation request) CIM_ERR_FAILED (some other unspecified error occurred) """ logger = env.get_logger() logger.log_debug('Entering %%s.cim_method_%s()' \\ %% self.__class__.__name__) # TODO do something raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented out_params = []''' % method.name.lower() for p in outParms: code+=''' #out_params+= [pywbem.CIMParameter('%s', type='%s', # value=%s)] # TODO''' % (p.name.lower(), p.type, type_hint(p, method.name)) code+=''' #rval = # TODO (type %s) return (rval, out_params) ''' % type_str(method) if isAssoc: args = inspect.getargspec(CIMProvider2.references)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def references(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.references()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle()''' % \ (args, CIMProvider2.references.__doc__) refprops = [] for prop in cc.properties.values(): if prop.reference_class is not None: refprops.append((prop.name, prop.reference_class)) code+= '''\n # If you want to get references for free, implemented in terms # of enum_instances, just leave the code below unaltered.''' for i, refprop in enumerate(refprops): if i == 0: code+= ''' if ch.is_subclass(object_name.namespace, sub=object_name.classname, super='%s')''' % refprop[1] else: code+= ''' or \\ ch.is_subclass(object_name.namespace, sub=object_name.classname, super='%s')''' % refprop[1] code+=''': return self.simple_refs(env, object_name, model, result_class_name, role, result_role, keys_only) ''' code+=''' # If you are doing simple refs with the code above, remove the # remainder of this method. Or, remove the stuff above and # implement references below. You need to pick either the # above approach or the below, and delete the other. Otherwise # you'll get a SyntaxError on the first yield below. # Prime model.path with knowledge of the keys, so key values on # the CIMInstanceName (model.path) will automatically be set when # we set property values on the model. model.pa%s # This is a common pattern. YMMV''' % \ format_desc('th.update('+str(keydict)+')', 12).strip() for refprop in refprops: code+= ''' if (not role or role.lower() == '%(refpropnamel)s') and \\ ch.is_subclass(object_name.namespace, sub=object_name.classname, super='%(rolecname)s'): model['%(refpropname)s'] = object_name yield model # TODO: Add other REF properties. # Yield association instances where # object_name is %(refpropnamel)s. # Only appropriate if object_name.classname # is '%(rolecname)s' or a subclass.\n''' \ % {'refpropname':refprop[0], 'refpropnamel':refprop[0].lower(), 'rolecname':refprop[1]} if valuemaps: code+= ''' class Values(object):''' for group, maps in valuemaps.items(): code+= ''' class %s(object):''' % group if '' in maps: for value, vm in maps['']: if value in maps: value = value+'_' code+= ''' %s = %s''' % (value, vm) for pname, vms in maps.items(): if pname == '': continue code+= ''' class %s(object):''' % pname for value, vm in vms: code+= ''' %s = %s''' % (value, vm) code+= '\n' code+= ''' ## end of class %(classname)sProvider ## get_providers() for associating CIM Class Name to python provider class name def get_providers(env): %(classname_l)s_prov = %(classname)s(env) return {'%(classname)s': %(classname_l)s_prov} ''' % mappings if isIndication: code+= ''' ## Indication support methods... ## Use these methods if this class will deliver indications. ## Remove these methods if this class will not deliver indications.''' args = inspect.getargspec(CIMProvider2.authorize_filter)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def authorize_filter(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.authorize_filter()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle() #raise pywbem.CIMError(pywbem.CIM_ERR_***) to indicate failure #otherwise just fall through for success''' % \ (args, CIMProvider2.authorize_filter.__doc__ or "Doc Goes Here") args = inspect.getargspec(CIMProvider2.enable_indications)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def enable_indications(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.enable_indications()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle() #raise pywbem.CIMError(pywbem.CIM_ERR_***) to indicate failure #otherwise just fall through for success''' % \ (args, CIMProvider2.enable_indications.__doc__ or "Doc Goes Here") args = inspect.getargspec(CIMProvider2.disable_indications)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def disable_indications(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.disable_indications()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle() #raise pywbem.CIMError(pywbem.CIM_ERR_***) to indicate failure #otherwise just fall through for success''' % \ (args, CIMProvider2.disable_indications.__doc__ or "Doc Goes Here") args = inspect.getargspec(CIMProvider2.activate_filter)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def activate_filter(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.activate_filter()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle() #raise pywbem.CIMError(pywbem.CIM_ERR_***) to indicate failure #otherwise just fall through for success''' % \ (args, CIMProvider2.activate_filter.__doc__ or "Doc Goes Here") args = inspect.getargspec(CIMProvider2.deactivate_filter)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def deactivate_filter(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.deactivate_filter()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle() #raise pywbem.CIMError(pywbem.CIM_ERR_***) to indicate failure #otherwise just fall through for success''' % \ (args, CIMProvider2.deactivate_filter.__doc__ or "Doc Goes Here") code+= ''' ## End of Indication Support Methods''' owtypes = ['1', 'Instance'] pegtypes = ['2', 'Instance'] sfcbtypes = 'instance' if isAssoc: owtypes[0]+= ',3' owtypes[1]+= ', Associator' pegtypes[0]+= ',3' pegtypes[1]+= ', Associator' sfcbtypes+= ' association' if cc.methods: owtypes[0]+= ',6' owtypes[1]+= ', Method' pegtypes[0]+= ',5' pegtypes[1]+= ', Method' sfcbtypes+= ' method' omitted = ''' // OpenWBEM Provider registration for %(classname)s instance of OpenWBEM_PyProviderRegistration { InstanceID = ""; // TODO NamespaceNames = {"root/cimv2"}; // TODO ClassName = "%(classname)s"; ProviderTypes = {%(owtypeNums)s}; // %(owtypeStrs)s ModulePath = "/usr/lib/pycim/%(classname)sProvider.py"; // TODO }; ''' mof =''' // SFCB Provider registration for %(classname)s [%(classname)s] provider: %(classname)s location: pyCmpiProvider type: %(sfcbtypes)s namespace: root/cimv2 // TODO // Pegasus Provider registration for %(classname)s instance of PG_ProviderModule { Name = "pyCmpiProvider_%(classname)s"; InterfaceType = "CMPI"; InterfaceVersion = "2.0.0"; Location = "pyCmpiProvider"; UserContext = 2; // Requestor Vendor = "TODO"; // TODO Version = "1.0"; }; instance of PG_Provider { Name = "%(classname)s"; ProviderModuleName = "pyCmpiProvider_%(classname)s"; }; instance of PG_ProviderCapabilities { CapabilityID = "%(classname)s"; ProviderModuleName = "pyCmpiProvider_%(classname)s"; ProviderName = "%(classname)s"; ClassName = "%(classname)s"; Namespaces = {"root/cimv2"}; // TODO ProviderType = {%(pegtypeNum)s}; // %(pegtypeStr)s };\n''' % {'classname': cc.classname, 'owtypeNums': owtypes[0], 'owtypeStrs': owtypes[1], 'pegtypeNum': pegtypes[0], 'pegtypeStr': pegtypes[1], 'sfcbtypes' : sfcbtypes} return code, mof class ProviderProxy(object): """Wraps a provider module, and routes requests into the module """ def __init__ (self, env, provid): self.env = env if isinstance(provid, types.ModuleType): self.provmod = provid self.provid = provid.__name__ self.filename = provid.__file__ else: logger = env.get_logger() logger.log_debug('Loading python provider at %s' %provid) self.provid = provid self._load_provider_source() self._init_provider(env) def _init_provider (self, env): self.provregs = {} if hasattr(self.provmod, 'init'): self.provmod.init(env) if hasattr(self.provmod, 'get_providers'): self.provregs = pywbem.NocaseDict(self.provmod.get_providers(env)) def _load_provider_source (self): # odd chars in a module name tend to break things self.provider_module_name = 'pyprovider_' for ch in self.provid: self.provider_module_name+= ch.isalnum() and ch or '_' # let providers import other providers in the same directory provdir = dirname(self.provid) if provdir not in sys.path: sys.path.append(provdir) try: self.provmod = sys.modules[self.provider_module_name] print 'Provider %s already loaded, found in sys.modules' \ % self.provmod except KeyError: try: # use full path in module name for uniqueness. print 'Loading provider %s from source' % self.provid path = os.path.dirname(self.provid) basename = os.path.basename(self.provid)[:-3] fn = imp.find_module(basename, [path]) try: g_mod_lock.acquire() imp.acquire_lock() self.provmod = imp.load_module( self.provider_module_name, *fn) self.provmod.provmod_timestamp = \ os.path.getmtime(self.provid) finally: imp.release_lock() g_mod_lock.release() fn[0].close() except IOError, arg: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Error loading provider %s: %s" % (self.provid, arg)) self.filename = self.provmod.__file__ def _get_callable (self, classname, cname): """Return a function or method object appropriate to fulfill a request classname -- The CIM class name associated with the request. cname -- The function or method name to look for. """ callable = None if classname in self.provregs: provClass = self.provregs[classname] if hasattr(provClass, cname): callable = getattr(provClass, cname) elif hasattr(self.provmod, cname): callable = getattr(self.provmod, cname) if callable is None: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "No provider registered for %s or no callable for %s:%s on provider %s"%(classname, classname, cname, self.provid)) return callable def _reload_if_necessary (self, env): """Check timestamp of loaded python provider module, and if it has changed since load, then reload the provider module. """ try: mod = sys.modules[self.provider_module_name] except KeyError: mod = None if (mod is None or \ mod.provmod_timestamp != os.path.getmtime(self.provid)): print "Need to reload provider at %s" %self.provid #first unload the module if self.provmod and hasattr(self.provmod, "shutdown"): self.provmod.shutdown(env) #now reload and reinit module try: del sys.modules[self.provider_module_name] except KeyError: pass try: self._load_provider_source() self._init_provider(env) except IOError, arg: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Error loading provider %s: %s" % (provid, arg)) ############################################################################## # enumInstanceNames ############################################################################## def MI_enumInstanceNames (self, env, objPath): logger = env.get_logger() logger.log_debug('ProviderProxy MI_enumInstanceNames called...') self._reload_if_necessary(env) return self._get_callable(objPath.classname, 'MI_enumInstanceNames')(env, objPath) ############################################################################## # enumInstances ############################################################################## def MI_enumInstances(self, env, objPath, propertyList): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_enumInstances called...') self._reload_if_necessary(env) return self._get_callable(objPath.classname, 'MI_enumInstances') \ (env, objPath, propertyList) ############################################################################## # getInstance ############################################################################## def MI_getInstance(self, env, instanceName, propertyList): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_getInstance called...') self._reload_if_necessary(env) rval = self._get_callable(instanceName.classname, 'MI_getInstance') \ (env, instanceName, propertyList) logger.log_debug('CIMProvider2 MI_getInstance returning') return rval ############################################################################## # createInstance ############################################################################## def MI_createInstance(self, env, instance): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_createInstance called...') self._reload_if_necessary(env) rval = self._get_callable(instance.classname, 'MI_createInstance') \ (env, instance) logger.log_debug('CIMProvider2 MI_createInstance returning') return rval ############################################################################## # modifyInstance ############################################################################## def MI_modifyInstance(self, env, modifiedInstance, propertyList): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_modifyInstance called...') self._reload_if_necessary(env) self._get_callable(modifiedInstance.classname, 'MI_modifyInstance') \ (env, modifiedInstance, propertyList) logger.log_debug('CIMProvider2 MI_modifyInstance returning') ############################################################################## # deleteInstance ############################################################################## def MI_deleteInstance(self, env, instanceName): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_deleteInstance called...') self._reload_if_necessary(env) self._get_callable(instanceName.classname, 'MI_deleteInstance') \ (env, instanceName) logger.log_debug('CIMProvider2 MI_deleteInstance returning') ############################################################################## # associators ############################################################################## def MI_associators(self, env, objectName, assocClassName, resultClassName, role, resultRole, propertyList): # NOTE: This should honor the parameters resultClassName, role, resultRole, # and propertyList logger = env.get_logger() logger.log_debug('CIMProvider2 MI_associators called. assocClass: %s' % (assocClassName)) self._reload_if_necessary(env) cname = assocClassName if not cname and hasattr(self.provmod, 'MI_associators'): for i in self.provmod.MI_associators( env, objectName, assocClassName, resultClassName, role, resultRole, propertyList): yield i return lcnames = [] if cname: lcnames = [cname] else: lcnames = self.provregs.keys() for lcname in lcnames: fn = self._get_callable(lcname, 'MI_associators') for i in fn(env, objectName, lcname, resultClassName, role, resultRole, propertyList): yield i logger.log_debug('CIMProvider2 MI_associators returning') ############################################################################## # associatorNames ############################################################################## def MI_associatorNames(self, env, objectName, assocClassName, resultClassName, role, resultRole): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_associatorNames called. assocClass: %s' % (assocClassName)) self._reload_if_necessary(env) cname = assocClassName if not cname and hasattr(self.provmod, 'MI_associatorNames'): for i in self.provmod.MI_associatorNames( env, objectName, assocClassName, resultClassName, role, resultRole): yield i return lcnames = [] if cname: lcnames = [cname] else: lcnames = self.provregs.keys() for lcname in lcnames: fn = self._get_callable(lcname, 'MI_associatorNames') for i in fn(env, objectName, lcname, resultClassName, role, resultRole): yield i logger.log_debug('CIMProvider2 MI_associatorNames returning') ############################################################################## # references ############################################################################## def MI_references(self, env, objectName, resultClassName, role, propertyList): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_references called. resultClass: %s' % (resultClassName)) self._reload_if_necessary(env) cname = resultClassName if not cname and hasattr(self.provmod, 'MI_references'): for i in self.provmod.MI_references(env, objectName, resultClassName, role, propertyList): yield i return lcnames = [] if cname: lcnames = [cname] else: lcnames = self.provregs.keys() for lcname in lcnames: fn = self._get_callable(lcname, 'MI_references') for i in fn(env, objectName, lcname, role, propertyList): yield i logger.log_debug('CIMProvider2 MI_references returning') ############################################################################## # referenceNames ############################################################################## def MI_referenceNames(self, env, objectName, resultClassName, role): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_referenceNames <1> called. resultClass: %s' % (resultClassName)) self._reload_if_necessary(env) cname = resultClassName if not cname and hasattr(self.provmod, 'MI_referenceNames'): for i in self.provmod.MI_referenceNames(env, objectName, resultClassName, role): yield i return lcnames = [] if cname: lcnames = [cname] else: lcnames = self.provregs.keys() for lcname in lcnames: fn = self._get_callable(lcname, 'MI_referenceNames') for i in fn(env, objectName, lcname, role): yield i logger.log_debug('CIMProvider2 MI_referenceNames returning') ############################################################################## # invokeMethod # inputParam is a Dictionary where the key is the parameter name # and the value is the parameter value # The return value for invokeMethod must be a tuple of size 2 where # element 0 is a tuple of size 2 where element 0 is the return data type name # and element 1 is the actual data value # element 1 is a dictionary where the key is the output parameter name # and the value is a tuple of size 2 where element 0 is the data type name # for the output parameter and element 1 is the actual value of the # output parameter. ############################################################################## def MI_invokeMethod(self, env, objectName, methodName, inputParams): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_invokeMethod called. method: %s:%s' \ % (objectName.classname,methodName)) self._reload_if_necessary(env) rval = self._get_callable(objectName.classname, 'MI_invokeMethod') \ (env, objectName, methodName, inputParams) logger.log_debug('CIMProvider2 MI_invokeMethod returning') return rval ############################################################################## def MI_authorizeFilter (self, env, filter, classname, classPath, owner): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_authorizeFilter called') self._reload_if_necessary(env) if hasattr(self.provmod, 'authorize_filter'): self.provmod.authorize_filter(env, filter, classname, classPath, owner) elif hasattr(self.provmod, 'MI_authorizeFilter'): self.provmod.MI_authorizeFilter(env, filter, classname, classPath, owner) else: # if not instrumented in provider, assume success logger.log_debug("Provider %s has no support for authorize filter"%self.provid) logger.log_debug('CIMProvider2 MI_authorizeFilter returning') return ############################################################################## def MI_activateFilter (self, env, filter, namespace, classes, firstActivation): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_activateFilter called') self._reload_if_necessary(env) if hasattr(self.provmod, 'activate_filter'): self.provmod.activate_filter(env, filter, namespace, classes, firstActivation) elif hasattr(self.provmod, 'MI_activateFilter'): self.provmod.MI_activateFilter(env, filter, namespace, classes, firstActivation) else: # if not instrumented in provider, assume success logger.log_debug("Provider %s has no support for activate filter"%self.provid) logger.log_debug('CIMProvider2 MI_activateFilter returning') return ############################################################################## def MI_deActivateFilter(self, env, filter, namespace, classes, lastActivation): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_deActivateFilter called') self._reload_if_necessary(env) if hasattr(self.provmod, 'deactivate_filter'): self.provmod.deactivate_filter(env, filter, namespace, classes, lastActivation) elif hasattr(self.provmod, 'MI_deActivateFilter'): self.provmod.MI_deActivateFilter(env, filter, namespace, classes, lastActivation) else: # if not instrumented in provider, assume success logger.log_debug("Provider %s has no support for deactivate filter"%self.provid) logger.log_debug('CIMProvider2 MI_deActivateFilter returning') return ############################################################################## def MI_enableIndications(self, env): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_enableIndications called') self._reload_if_necessary(env) if hasattr(self.provmod, 'enable_indications'): self.provmod.enable_indications(env) elif hasattr(self.provmod, 'MI_enableIndications'): self.provmod.MI_enableIndications(env) else: # if not instrumented in provider, assume success logger.log_debug("Provider %s has no support for enable indications"%self.provid) logger.log_debug('CIMProvider2 MI_enableIndications returning') return ############################################################################## def MI_disableIndications(self, env): logger = env.get_logger() logger.log_debug('CIMProvider2 MI_disableIndications called') self._reload_if_necessary(env) if hasattr(self.provmod, 'disable_indications'): self.provmod.disable_indications(env) elif hasattr(self.provmod, 'MI_disableIndications'): self.provmod.MI_disableIndications(env) else: # if not instrumented in provider, assume success logger.log_debug("Provider %s has no support for disable indications"%self.provid) logger.log_debug('CIMProvider2 MI_disableIndications returning') return ############################################################################## def MI_shutdown (self, env): modname = self.provmod.__name__ if hasattr(self.provmod, "shutdown"): self.provmod.shutdown(env) self.provmod = None del sys.modules[modname] #TODO concurrency problems here?? ############################################################################## def MI_canunload(self, env): if hasattr(self.provmod, "canunload"): return self.provmod.canunload else: return True ############################################################################## def MI_consumeIndication(self, env, destinationPath, indicationInstance): logger = env.get_logger() logger.log_debug('ProviderProxy MI_consumeIndication called') self._reload_if_necessary(env) if hasattr(self.provmod, 'consume_indication'): self.provmod.consume_indication(env, destinationPath, indicationInstance) elif hasattr(self.provmod, 'MI_consumeIndication'): self.provmod.MI_consumeIndication(env, destinationPath, indicationInstance) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for consume indication" % \ self.provid) logger.log_debug('ProviderProxy MI_consumeIndication returning') ############################################################################## def MI_handleIndication(self, env, ns, handlerInstance, indicationInstance): logger = env.get_logger() logger.log_debug('ProviderProxy MI_handleIndication called') self._reload_if_necessary(env) if hasattr(self.provmod, 'handle_indication'): self.provmod.handle_indication(env, ns, handlerInstance, indicationInstance) elif hasattr(self.provmod, 'MI_handleIndication'): self.provmod.MI_handleIndication(env, ns, handlerInstance, indicationInstance) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for handle indication"%self.provid) logger.log_debug('ProviderProxy MI_handleIndication returning') pywbem-0.7.0/yacc.py0000644000175000001440000032616511104133574013175 0ustar bartusers#----------------------------------------------------------------------------- # ply: yacc.py # # Author(s): David M. Beazley (dave@dabeaz.com) # # Copyright (C) 2001-2008, David M. Beazley # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # See the file COPYING for a complete copy of the LGPL. # # # This implements an LR parser that is constructed from grammar rules defined # as Python functions. The grammer is specified by supplying the BNF inside # Python documentation strings. The inspiration for this technique was borrowed # from John Aycock's Spark parsing system. PLY might be viewed as cross between # Spark and the GNU bison utility. # # The current implementation is only somewhat object-oriented. The # LR parser itself is defined in terms of an object (which allows multiple # parsers to co-exist). However, most of the variables used during table # construction are defined in terms of global variables. Users shouldn't # notice unless they are trying to define multiple parsers at the same # time using threads (in which case they should have their head examined). # # This implementation supports both SLR and LALR(1) parsing. LALR(1) # support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), # using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, # Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced # by the more efficient DeRemer and Pennello algorithm. # # :::::::: WARNING ::::::: # # Construction of LR parsing tables is fairly complicated and expensive. # To make this module run fast, a *LOT* of work has been put into # optimization---often at the expensive of readability and what might # consider to be good Python "coding style." Modify the code at your # own risk! # ---------------------------------------------------------------------------- __version__ = "2.5" __tabversion__ = "2.4" # Table version #----------------------------------------------------------------------------- # === User configurable parameters === # # Change these to modify the default behavior of yacc (if you wish) #----------------------------------------------------------------------------- yaccdebug = 1 # Debugging mode. If set, yacc generates a # a 'parser.out' file in the current directory debug_file = 'parser.out' # Default name of the debugging file tab_module = 'parsetab' # Default name of the table module default_lr = 'LALR' # Default LR table generation method error_count = 3 # Number of symbols that must be shifted to leave recovery mode yaccdevel = 0 # Set to True if developing yacc. This turns off optimized # implementations of certain functions. import re, types, sys, cStringIO, os.path try: from hashlib import md5 except ImportError: from md5 import new as md5 # Exception raised for yacc-related errors class YaccError(Exception): pass # Exception raised for errors raised in production rules class SyntaxError(Exception): pass # Available instance types. This is used when parsers are defined by a class. # it's a little funky because I want to preserve backwards compatibility # with Python 2.0 where types.ObjectType is undefined. try: _INSTANCETYPE = (types.InstanceType, types.ObjectType) except AttributeError: _INSTANCETYPE = types.InstanceType class object: pass # Note: needed if no new-style classes present #----------------------------------------------------------------------------- # === LR Parsing Engine === # # The following classes are used for the LR parser itself. These are not # used during table construction and are independent of the actual LR # table generation algorithm #----------------------------------------------------------------------------- # This class is used to hold non-terminal grammar symbols during parsing. # It normally has the following attributes set: # .type = Grammar symbol type # .value = Symbol value # .lineno = Starting line number # .endlineno = Ending line number (optional, set automatically) # .lexpos = Starting lex position # .endlexpos = Ending lex position (optional, set automatically) class YaccSymbol: def __str__(self): return self.type def __repr__(self): return str(self) # This class is a wrapper around the objects actually passed to each # grammar rule. Index lookup and assignment actually assign the # .value attribute of the underlying YaccSymbol object. # The lineno() method returns the line number of a given # item (or 0 if not defined). The linespan() method returns # a tuple of (startline,endline) representing the range of lines # for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) # representing the range of positional information for a symbol. class YaccProduction: def __init__(self,s,stack=None): self.slice = s self.stack = stack self.lexer = None self.parser= None def __getitem__(self,n): if n >= 0: return self.slice[n].value else: return self.stack[n].value def __setitem__(self,n,v): self.slice[n].value = v def __getslice__(self,i,j): return [s.value for s in self.slice[i:j]] def __len__(self): return len(self.slice) def lineno(self,n): return getattr(self.slice[n],"lineno",0) def linespan(self,n): startline = getattr(self.slice[n],"lineno",0) endline = getattr(self.slice[n],"endlineno",startline) return startline,endline def lexpos(self,n): return getattr(self.slice[n],"lexpos",0) def lexspan(self,n): startpos = getattr(self.slice[n],"lexpos",0) endpos = getattr(self.slice[n],"endlexpos",startpos) return startpos,endpos def error(self): raise SyntaxError # The LR Parsing engine. This is defined as a class so that multiple parsers # can exist in the same process. A user never instantiates this directly. # Instead, the global yacc() function should be used to create a suitable Parser # object. class Parser: def __init__(self,magic=None): # This is a hack to keep users from trying to instantiate a Parser # object directly. if magic != "xyzzy": raise YaccError, "Can't directly instantiate Parser. Use yacc() instead." # Reset internal state self.productions = None # List of productions self.errorfunc = None # Error handling function self.action = { } # LR Action table self.goto = { } # LR goto table self.require = { } # Attribute require table self.method = "Unknown LR" # Table construction method used def errok(self): self.errorok = 1 def restart(self): del self.statestack[:] del self.symstack[:] sym = YaccSymbol() sym.type = '$end' self.symstack.append(sym) self.statestack.append(0) def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): if debug or yaccdevel: return self.parsedebug(input,lexer,debug,tracking,tokenfunc) elif tracking: return self.parseopt(input,lexer,debug,tracking,tokenfunc) else: return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # parsedebug(). # # This is the debugging enabled version of parse(). All changes made to the # parsing engine should be made here. For the non-debugging version, # copy this code to a method parseopt() and delete all of the sections # enclosed in: # # #--! DEBUG # statements # #--! DEBUG # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def parsedebug(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): lookahead = None # Current lookahead symbol lookaheadstack = [ ] # Stack of lookahead symbols actions = self.action # Local reference to action table (to avoid lookup on self.) goto = self.goto # Local reference to goto table (to avoid lookup on self.) prod = self.productions # Local reference to production list (to avoid lookup on self.) pslice = YaccProduction(None) # Production object passed to grammar rules errorcount = 0 # Used during error recovery endsym = "$end" # End symbol # If no lexer was given, we will try to use the lex module if not lexer: import lex lexer = lex.lexer # Set up the lexer and parser objects on pslice pslice.lexer = lexer pslice.parser = self # If input was supplied, pass to lexer if input is not None: lexer.input(input) if tokenfunc is None: # Tokenize function get_token = lexer.token else: get_token = tokenfunc # Set up the state and symbol stacks statestack = [ ] # Stack of parsing states self.statestack = statestack symstack = [ ] # Stack of grammar symbols self.symstack = symstack pslice.stack = symstack # Put in the production errtoken = None # Err token # The start state is assumed to be (0,$end) statestack.append(0) sym = YaccSymbol() sym.type = endsym symstack.append(sym) state = 0 while 1: # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer # --! DEBUG if debug > 1: print 'state', state # --! DEBUG if not lookahead: if not lookaheadstack: lookahead = get_token() # Get the next token else: lookahead = lookaheadstack.pop() if not lookahead: lookahead = YaccSymbol() lookahead.type = endsym # --! DEBUG if debug: errorlead = ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip() # --! DEBUG # Check the action table ltype = lookahead.type t = actions[state].get(ltype) # --! DEBUG if debug > 1: print 'action', t # --! DEBUG if t is not None: if t > 0: # shift a symbol on the stack if ltype is endsym: # Error, end of input sys.stderr.write("yacc: Parse error. EOF\n") return statestack.append(t) state = t # --! DEBUG if debug > 1: sys.stderr.write("%-60s shift state %s\n" % (errorlead, t)) # --! DEBUG symstack.append(lookahead) lookahead = None # Decrease error count on successful shift if errorcount: errorcount -=1 continue if t < 0: # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.len # Get production function sym = YaccSymbol() sym.type = pname # Production name sym.value = None # --! DEBUG if debug > 1: sys.stderr.write("%-60s reduce %d\n" % (errorlead, -t)) # --! DEBUG if plen: targ = symstack[-plen-1:] targ[0] = sym # --! TRACKING if tracking: t1 = targ[1] sym.lineno = t1.lineno sym.lexpos = t1.lexpos t1 = targ[-1] sym.endlineno = getattr(t1,"endlineno",t1.lineno) sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) # --! TRACKING # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object p.func(pslice) del symstack[-plen:] del statestack[-plen:] symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) symstack.pop() statestack.pop() state = statestack[-1] sym.type = 'error' lookahead = sym errorcount = error_count self.errorok = 0 continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else: # --! TRACKING if tracking: sym.lineno = lexer.lineno sym.lexpos = lexer.lexpos # --! TRACKING targ = [ sym ] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # above as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object p.func(pslice) symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) symstack.pop() statestack.pop() state = statestack[-1] sym.type = 'error' lookahead = sym errorcount = error_count self.errorok = 0 continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if t == 0: n = symstack[-1] return getattr(n,"value",None) if t == None: # --! DEBUG if debug: sys.stderr.write(errorlead + "\n") # --! DEBUG # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 or self.errorok: errorcount = error_count self.errorok = 0 errtoken = lookahead if errtoken.type is endsym: errtoken = None # End of file! if self.errorfunc: global errok,token,restart errok = self.errok # Set some special functions available in error recovery token = get_token restart = self.restart tok = self.errorfunc(errtoken) del errok, token, restart # Delete special functions if self.errorok: # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = None continue else: if errtoken: if hasattr(errtoken,"lineno"): lineno = lookahead.lineno else: lineno = 0 if lineno: sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) else: sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) else: sys.stderr.write("yacc: Parse error in input. EOF\n") return else: errorcount = error_count # case 1: the statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if len(statestack) <= 1 and lookahead.type is not endsym: lookahead = None errtoken = None state = 0 # Nuke the pushback stack del lookaheadstack[:] continue # case 2: the statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type is endsym: # Whoa. We're really hosed here. Bail out return if lookahead.type != 'error': sym = symstack[-1] if sym.type == 'error': # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue lookahead = None continue t = YaccSymbol() t.type = 'error' if hasattr(lookahead,"lineno"): t.lineno = lookahead.lineno t.value = lookahead lookaheadstack.append(lookahead) lookahead = t else: symstack.pop() statestack.pop() state = statestack[-1] # Potential bug fix continue # Call an error function here raise RuntimeError, "yacc: internal parser error!!!\n" # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # parseopt(). # # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. # Edit the debug version above, then copy any modifications to the method # below while removing #--! DEBUG sections. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): lookahead = None # Current lookahead symbol lookaheadstack = [ ] # Stack of lookahead symbols actions = self.action # Local reference to action table (to avoid lookup on self.) goto = self.goto # Local reference to goto table (to avoid lookup on self.) prod = self.productions # Local reference to production list (to avoid lookup on self.) pslice = YaccProduction(None) # Production object passed to grammar rules errorcount = 0 # Used during error recovery # If no lexer was given, we will try to use the lex module if not lexer: import lex lexer = lex.lexer # Set up the lexer and parser objects on pslice pslice.lexer = lexer pslice.parser = self # If input was supplied, pass to lexer if input is not None: lexer.input(input) if tokenfunc is None: # Tokenize function get_token = lexer.token else: get_token = tokenfunc # Set up the state and symbol stacks statestack = [ ] # Stack of parsing states self.statestack = statestack symstack = [ ] # Stack of grammar symbols self.symstack = symstack pslice.stack = symstack # Put in the production errtoken = None # Err token # The start state is assumed to be (0,$end) statestack.append(0) sym = YaccSymbol() sym.type = '$end' symstack.append(sym) state = 0 while 1: # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer if not lookahead: if not lookaheadstack: lookahead = get_token() # Get the next token else: lookahead = lookaheadstack.pop() if not lookahead: lookahead = YaccSymbol() lookahead.type = '$end' # Check the action table ltype = lookahead.type t = actions[state].get(ltype) if t is not None: if t > 0: # shift a symbol on the stack if ltype == '$end': # Error, end of input sys.stderr.write("yacc: Parse error. EOF\n") return statestack.append(t) state = t symstack.append(lookahead) lookahead = None # Decrease error count on successful shift if errorcount: errorcount -=1 continue if t < 0: # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.len # Get production function sym = YaccSymbol() sym.type = pname # Production name sym.value = None if plen: targ = symstack[-plen-1:] targ[0] = sym # --! TRACKING if tracking: t1 = targ[1] sym.lineno = t1.lineno sym.lexpos = t1.lexpos t1 = targ[-1] sym.endlineno = getattr(t1,"endlineno",t1.lineno) sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) # --! TRACKING # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object p.func(pslice) del symstack[-plen:] del statestack[-plen:] symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) symstack.pop() statestack.pop() state = statestack[-1] sym.type = 'error' lookahead = sym errorcount = error_count self.errorok = 0 continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else: # --! TRACKING if tracking: sym.lineno = lexer.lineno sym.lexpos = lexer.lexpos # --! TRACKING targ = [ sym ] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # above as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object p.func(pslice) symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) symstack.pop() statestack.pop() state = statestack[-1] sym.type = 'error' lookahead = sym errorcount = error_count self.errorok = 0 continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if t == 0: n = symstack[-1] return getattr(n,"value",None) if t == None: # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 or self.errorok: errorcount = error_count self.errorok = 0 errtoken = lookahead if errtoken.type == '$end': errtoken = None # End of file! if self.errorfunc: global errok,token,restart errok = self.errok # Set some special functions available in error recovery token = get_token restart = self.restart tok = self.errorfunc(errtoken) del errok, token, restart # Delete special functions if self.errorok: # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = None continue else: if errtoken: if hasattr(errtoken,"lineno"): lineno = lookahead.lineno else: lineno = 0 if lineno: sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) else: sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) else: sys.stderr.write("yacc: Parse error in input. EOF\n") return else: errorcount = error_count # case 1: the statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if len(statestack) <= 1 and lookahead.type != '$end': lookahead = None errtoken = None state = 0 # Nuke the pushback stack del lookaheadstack[:] continue # case 2: the statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type == '$end': # Whoa. We're really hosed here. Bail out return if lookahead.type != 'error': sym = symstack[-1] if sym.type == 'error': # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue lookahead = None continue t = YaccSymbol() t.type = 'error' if hasattr(lookahead,"lineno"): t.lineno = lookahead.lineno t.value = lookahead lookaheadstack.append(lookahead) lookahead = t else: symstack.pop() statestack.pop() state = statestack[-1] # Potential bug fix continue # Call an error function here raise RuntimeError, "yacc: internal parser error!!!\n" # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # parseopt_notrack(). # # Optimized version of parseopt() with line number tracking removed. # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove # code in the #--! TRACKING sections # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): lookahead = None # Current lookahead symbol lookaheadstack = [ ] # Stack of lookahead symbols actions = self.action # Local reference to action table (to avoid lookup on self.) goto = self.goto # Local reference to goto table (to avoid lookup on self.) prod = self.productions # Local reference to production list (to avoid lookup on self.) pslice = YaccProduction(None) # Production object passed to grammar rules errorcount = 0 # Used during error recovery # If no lexer was given, we will try to use the lex module if not lexer: import lex lexer = lex.lexer # Set up the lexer and parser objects on pslice pslice.lexer = lexer pslice.parser = self # If input was supplied, pass to lexer if input is not None: lexer.input(input) if tokenfunc is None: # Tokenize function get_token = lexer.token else: get_token = tokenfunc # Set up the state and symbol stacks statestack = [ ] # Stack of parsing states self.statestack = statestack symstack = [ ] # Stack of grammar symbols self.symstack = symstack pslice.stack = symstack # Put in the production errtoken = None # Err token # The start state is assumed to be (0,$end) statestack.append(0) sym = YaccSymbol() sym.type = '$end' symstack.append(sym) state = 0 while 1: # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer if not lookahead: if not lookaheadstack: lookahead = get_token() # Get the next token else: lookahead = lookaheadstack.pop() if not lookahead: lookahead = YaccSymbol() lookahead.type = '$end' # Check the action table ltype = lookahead.type t = actions[state].get(ltype) if t is not None: if t > 0: # shift a symbol on the stack if ltype == '$end': # Error, end of input sys.stderr.write("yacc: Parse error. EOF\n") return statestack.append(t) state = t symstack.append(lookahead) lookahead = None # Decrease error count on successful shift if errorcount: errorcount -=1 continue if t < 0: # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.len # Get production function sym = YaccSymbol() sym.type = pname # Production name sym.value = None if plen: targ = symstack[-plen-1:] targ[0] = sym # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object p.func(pslice) del symstack[-plen:] del statestack[-plen:] symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) symstack.pop() statestack.pop() state = statestack[-1] sym.type = 'error' lookahead = sym errorcount = error_count self.errorok = 0 continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else: targ = [ sym ] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # above as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ try: # Call the grammar rule with our special slice object p.func(pslice) symstack.append(sym) state = goto[statestack[-1]][pname] statestack.append(state) except SyntaxError: # If an error was set. Enter error recovery state lookaheadstack.append(lookahead) symstack.pop() statestack.pop() state = statestack[-1] sym.type = 'error' lookahead = sym errorcount = error_count self.errorok = 0 continue # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if t == 0: n = symstack[-1] return getattr(n,"value",None) if t == None: # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 or self.errorok: errorcount = error_count self.errorok = 0 errtoken = lookahead if errtoken.type == '$end': errtoken = None # End of file! if self.errorfunc: global errok,token,restart errok = self.errok # Set some special functions available in error recovery token = get_token restart = self.restart tok = self.errorfunc(errtoken) del errok, token, restart # Delete special functions if self.errorok: # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = None continue else: if errtoken: if hasattr(errtoken,"lineno"): lineno = lookahead.lineno else: lineno = 0 if lineno: sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) else: sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) else: sys.stderr.write("yacc: Parse error in input. EOF\n") return else: errorcount = error_count # case 1: the statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if len(statestack) <= 1 and lookahead.type != '$end': lookahead = None errtoken = None state = 0 # Nuke the pushback stack del lookaheadstack[:] continue # case 2: the statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type == '$end': # Whoa. We're really hosed here. Bail out return if lookahead.type != 'error': sym = symstack[-1] if sym.type == 'error': # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue lookahead = None continue t = YaccSymbol() t.type = 'error' if hasattr(lookahead,"lineno"): t.lineno = lookahead.lineno t.value = lookahead lookaheadstack.append(lookahead) lookahead = t else: symstack.pop() statestack.pop() state = statestack[-1] # Potential bug fix continue # Call an error function here raise RuntimeError, "yacc: internal parser error!!!\n" # ----------------------------------------------------------------------------- # === Parser Construction === # # The following functions and variables are used to implement the yacc() function # itself. This is pretty hairy stuff involving lots of error checking, # construction of LR items, kernels, and so forth. Although a lot of # this work is done using global variables, the resulting Parser object # is completely self contained--meaning that it is safe to repeatedly # call yacc() with different grammars in the same application. # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # validate_file() # # This function checks to see if there are duplicated p_rulename() functions # in the parser module file. Without this function, it is really easy for # users to make mistakes by cutting and pasting code fragments (and it's a real # bugger to try and figure out why the resulting parser doesn't work). Therefore, # we just do a little regular expression pattern matching of def statements # to try and detect duplicates. # ----------------------------------------------------------------------------- def validate_file(filename): base,ext = os.path.splitext(filename) if ext != '.py': return 1 # No idea. Assume it's okay. try: f = open(filename) lines = f.readlines() f.close() except IOError: return 1 # Oh well # Match def p_funcname( fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') counthash = { } linen = 1 noerror = 1 for l in lines: m = fre.match(l) if m: name = m.group(1) prev = counthash.get(name) if not prev: counthash[name] = linen else: sys.stderr.write("%s:%d: Function %s redefined. Previously defined on line %d\n" % (filename,linen,name,prev)) noerror = 0 linen += 1 return noerror # This function looks for functions that might be grammar rules, but which don't have the proper p_suffix. def validate_dict(d): for n,v in d.items(): if n[0:2] == 'p_' and type(v) in (types.FunctionType, types.MethodType): continue if n[0:2] == 't_': continue if n[0:2] == 'p_': sys.stderr.write("yacc: Warning. '%s' not defined as a function\n" % n) if 1 and isinstance(v,types.FunctionType) and v.func_code.co_argcount == 1: try: doc = v.__doc__.split(" ") if doc[1] == ':': sys.stderr.write("%s:%d: Warning. Possible grammar rule '%s' defined without p_ prefix.\n" % (v.func_code.co_filename, v.func_code.co_firstlineno,n)) except StandardError: pass # ----------------------------------------------------------------------------- # === GRAMMAR FUNCTIONS === # # The following global variables and functions are used to store, manipulate, # and verify the grammar rules specified by the user. # ----------------------------------------------------------------------------- # Initialize all of the global variables used during grammar construction def initialize_vars(): global Productions, Prodnames, Prodmap, Terminals global Nonterminals, First, Follow, Precedence, UsedPrecedence, LRitems global Errorfunc, Signature, Requires Productions = [None] # A list of all of the productions. The first # entry is always reserved for the purpose of # building an augmented grammar Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all # productions of that nonterminal. Prodmap = { } # A dictionary that is only used to detect duplicate # productions. Terminals = { } # A dictionary mapping the names of terminal symbols to a # list of the rules where they are used. Nonterminals = { } # A dictionary mapping names of nonterminals to a list # of rule numbers where they are used. First = { } # A dictionary of precomputed FIRST(x) symbols Follow = { } # A dictionary of precomputed FOLLOW(x) symbols Precedence = { } # Precedence rules for each terminal. Contains tuples of the # form ('right',level) or ('nonassoc', level) or ('left',level) UsedPrecedence = { } # Precedence rules that were actually used by the grammer. # This is only used to provide error checking and to generate # a warning about unused precedence rules. LRitems = [ ] # A list of all LR items for the grammar. These are the # productions with the "dot" like E -> E . PLUS E Errorfunc = None # User defined error handler Signature = md5() # Digital signature of the grammar rules, precedence # and other information. Used to determined when a # parsing table needs to be regenerated. Signature.update(__tabversion__) Requires = { } # Requires list # File objects used when creating the parser.out debugging file global _vf, _vfc _vf = cStringIO.StringIO() _vfc = cStringIO.StringIO() # ----------------------------------------------------------------------------- # class Production: # # This class stores the raw information about a single production or grammar rule. # It has a few required attributes: # # name - Name of the production (nonterminal) # prod - A list of symbols making up its production # number - Production number. # # In addition, a few additional attributes are used to help with debugging or # optimization of table generation. # # file - File where production action is defined. # lineno - Line number where action is defined # func - Action function # prec - Precedence level # lr_next - Next LR item. Example, if we are ' E -> E . PLUS E' # then lr_next refers to 'E -> E PLUS . E' # lr_index - LR item index (location of the ".") in the prod list. # lookaheads - LALR lookahead symbols for this item # len - Length of the production (number of symbols on right hand side) # ----------------------------------------------------------------------------- class Production: def __init__(self,**kw): for k,v in kw.items(): setattr(self,k,v) self.lr_index = -1 self.lr0_added = 0 # Flag indicating whether or not added to LR0 closure self.lr1_added = 0 # Flag indicating whether or not added to LR1 self.usyms = [ ] self.lookaheads = { } self.lk_added = { } self.setnumbers = [ ] def __str__(self): if self.prod: s = "%s -> %s" % (self.name," ".join(self.prod)) else: s = "%s -> " % self.name return s def __repr__(self): return str(self) # Compute lr_items from the production def lr_item(self,n): if n > len(self.prod): return None p = Production() p.name = self.name p.prod = list(self.prod) p.number = self.number p.lr_index = n p.lookaheads = { } p.setnumbers = self.setnumbers p.prod.insert(n,".") p.prod = tuple(p.prod) p.len = len(p.prod) p.usyms = self.usyms # Precompute list of productions immediately following try: p.lrafter = Prodnames[p.prod[n+1]] except (IndexError,KeyError),e: p.lrafter = [] try: p.lrbefore = p.prod[n-1] except IndexError: p.lrbefore = None return p class MiniProduction: pass # regex matching identifiers _is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') # ----------------------------------------------------------------------------- # add_production() # # Given an action function, this function assembles a production rule. # The production rule is assumed to be found in the function's docstring. # This rule has the general syntax: # # name1 ::= production1 # | production2 # | production3 # ... # | productionn # name2 ::= production1 # | production2 # ... # ----------------------------------------------------------------------------- def add_production(f,file,line,prodname,syms): if Terminals.has_key(prodname): sys.stderr.write("%s:%d: Illegal rule name '%s'. Already defined as a token.\n" % (file,line,prodname)) return -1 if prodname == 'error': sys.stderr.write("%s:%d: Illegal rule name '%s'. error is a reserved word.\n" % (file,line,prodname)) return -1 if not _is_identifier.match(prodname): sys.stderr.write("%s:%d: Illegal rule name '%s'\n" % (file,line,prodname)) return -1 for x in range(len(syms)): s = syms[x] if s[0] in "'\"": try: c = eval(s) if (len(c) > 1): sys.stderr.write("%s:%d: Literal token %s in rule '%s' may only be a single character\n" % (file,line,s, prodname)) return -1 if not Terminals.has_key(c): Terminals[c] = [] syms[x] = c continue except SyntaxError: pass if not _is_identifier.match(s) and s != '%prec': sys.stderr.write("%s:%d: Illegal name '%s' in rule '%s'\n" % (file,line,s, prodname)) return -1 # See if the rule is already in the rulemap map = "%s -> %s" % (prodname,syms) if Prodmap.has_key(map): m = Prodmap[map] sys.stderr.write("%s:%d: Duplicate rule %s.\n" % (file,line, m)) sys.stderr.write("%s:%d: Previous definition at %s:%d\n" % (file,line, m.file, m.line)) return -1 p = Production() p.name = prodname p.prod = syms p.file = file p.line = line p.func = f p.number = len(Productions) Productions.append(p) Prodmap[map] = p if not Nonterminals.has_key(prodname): Nonterminals[prodname] = [ ] # Add all terminals to Terminals i = 0 while i < len(p.prod): t = p.prod[i] if t == '%prec': try: precname = p.prod[i+1] except IndexError: sys.stderr.write("%s:%d: Syntax error. Nothing follows %%prec.\n" % (p.file,p.line)) return -1 prec = Precedence.get(precname,None) if not prec: sys.stderr.write("%s:%d: Nothing known about the precedence of '%s'\n" % (p.file,p.line,precname)) return -1 else: p.prec = prec UsedPrecedence[precname] = 1 del p.prod[i] del p.prod[i] continue if Terminals.has_key(t): Terminals[t].append(p.number) # Is a terminal. We'll assign a precedence to p based on this if not hasattr(p,"prec"): p.prec = Precedence.get(t,('right',0)) else: if not Nonterminals.has_key(t): Nonterminals[t] = [ ] Nonterminals[t].append(p.number) i += 1 if not hasattr(p,"prec"): p.prec = ('right',0) # Set final length of productions p.len = len(p.prod) p.prod = tuple(p.prod) # Calculate unique syms in the production p.usyms = [ ] for s in p.prod: if s not in p.usyms: p.usyms.append(s) # Add to the global productions list try: Prodnames[p.name].append(p) except KeyError: Prodnames[p.name] = [ p ] return 0 # Given a raw rule function, this function rips out its doc string # and adds rules to the grammar def add_function(f): line = f.func_code.co_firstlineno file = f.func_code.co_filename error = 0 if isinstance(f,types.MethodType): reqdargs = 2 else: reqdargs = 1 if f.func_code.co_argcount > reqdargs: sys.stderr.write("%s:%d: Rule '%s' has too many arguments.\n" % (file,line,f.__name__)) return -1 if f.func_code.co_argcount < reqdargs: sys.stderr.write("%s:%d: Rule '%s' requires an argument.\n" % (file,line,f.__name__)) return -1 if f.__doc__: # Split the doc string into lines pstrings = f.__doc__.splitlines() lastp = None dline = line for ps in pstrings: dline += 1 p = ps.split() if not p: continue try: if p[0] == '|': # This is a continuation of a previous rule if not lastp: sys.stderr.write("%s:%d: Misplaced '|'.\n" % (file,dline)) return -1 prodname = lastp if len(p) > 1: syms = p[1:] else: syms = [ ] else: prodname = p[0] lastp = prodname assign = p[1] if len(p) > 2: syms = p[2:] else: syms = [ ] if assign != ':' and assign != '::=': sys.stderr.write("%s:%d: Syntax error. Expected ':'\n" % (file,dline)) return -1 e = add_production(f,file,dline,prodname,syms) error += e except StandardError: sys.stderr.write("%s:%d: Syntax error in rule '%s'\n" % (file,dline,ps)) error -= 1 else: sys.stderr.write("%s:%d: No documentation string specified in function '%s'\n" % (file,line,f.__name__)) return error # Cycle checking code (Michael Dyck) def compute_reachable(): ''' Find each symbol that can be reached from the start symbol. Print a warning for any nonterminals that can't be reached. (Unused terminals have already had their warning.) ''' Reachable = { } for s in Terminals.keys() + Nonterminals.keys(): Reachable[s] = 0 mark_reachable_from( Productions[0].prod[0], Reachable ) for s in Nonterminals.keys(): if not Reachable[s]: sys.stderr.write("yacc: Symbol '%s' is unreachable.\n" % s) def mark_reachable_from(s, Reachable): ''' Mark all symbols that are reachable from symbol s. ''' if Reachable[s]: # We've already reached symbol s. return Reachable[s] = 1 for p in Prodnames.get(s,[]): for r in p.prod: mark_reachable_from(r, Reachable) # ----------------------------------------------------------------------------- # compute_terminates() # # This function looks at the various parsing rules and tries to detect # infinite recursion cycles (grammar rules where there is no possible way # to derive a string of only terminals). # ----------------------------------------------------------------------------- def compute_terminates(): ''' Raise an error for any symbols that don't terminate. ''' Terminates = {} # Terminals: for t in Terminals.keys(): Terminates[t] = 1 Terminates['$end'] = 1 # Nonterminals: # Initialize to false: for n in Nonterminals.keys(): Terminates[n] = 0 # Then propagate termination until no change: while 1: some_change = 0 for (n,pl) in Prodnames.items(): # Nonterminal n terminates iff any of its productions terminates. for p in pl: # Production p terminates iff all of its rhs symbols terminate. for s in p.prod: if not Terminates[s]: # The symbol s does not terminate, # so production p does not terminate. p_terminates = 0 break else: # didn't break from the loop, # so every symbol s terminates # so production p terminates. p_terminates = 1 if p_terminates: # symbol n terminates! if not Terminates[n]: Terminates[n] = 1 some_change = 1 # Don't need to consider any more productions for this n. break if not some_change: break some_error = 0 for (s,terminates) in Terminates.items(): if not terminates: if not Prodnames.has_key(s) and not Terminals.has_key(s) and s != 'error': # s is used-but-not-defined, and we've already warned of that, # so it would be overkill to say that it's also non-terminating. pass else: sys.stderr.write("yacc: Infinite recursion detected for symbol '%s'.\n" % s) some_error = 1 return some_error # ----------------------------------------------------------------------------- # verify_productions() # # This function examines all of the supplied rules to see if they seem valid. # ----------------------------------------------------------------------------- def verify_productions(cycle_check=1): error = 0 for p in Productions: if not p: continue for s in p.prod: if not Prodnames.has_key(s) and not Terminals.has_key(s) and s != 'error': sys.stderr.write("%s:%d: Symbol '%s' used, but not defined as a token or a rule.\n" % (p.file,p.line,s)) error = 1 continue unused_tok = 0 # Now verify all of the tokens if yaccdebug: _vf.write("Unused terminals:\n\n") for s,v in Terminals.items(): if s != 'error' and not v: sys.stderr.write("yacc: Warning. Token '%s' defined, but not used.\n" % s) if yaccdebug: _vf.write(" %s\n"% s) unused_tok += 1 # Print out all of the productions if yaccdebug: _vf.write("\nGrammar\n\n") for i in range(1,len(Productions)): _vf.write("Rule %-5d %s\n" % (i, Productions[i])) unused_prod = 0 # Verify the use of all productions for s,v in Nonterminals.items(): if not v: p = Prodnames[s][0] sys.stderr.write("%s:%d: Warning. Rule '%s' defined, but not used.\n" % (p.file,p.line, s)) unused_prod += 1 if unused_tok == 1: sys.stderr.write("yacc: Warning. There is 1 unused token.\n") if unused_tok > 1: sys.stderr.write("yacc: Warning. There are %d unused tokens.\n" % unused_tok) if unused_prod == 1: sys.stderr.write("yacc: Warning. There is 1 unused rule.\n") if unused_prod > 1: sys.stderr.write("yacc: Warning. There are %d unused rules.\n" % unused_prod) if yaccdebug: _vf.write("\nTerminals, with rules where they appear\n\n") ks = Terminals.keys() ks.sort() for k in ks: _vf.write("%-20s : %s\n" % (k, " ".join([str(s) for s in Terminals[k]]))) _vf.write("\nNonterminals, with rules where they appear\n\n") ks = Nonterminals.keys() ks.sort() for k in ks: _vf.write("%-20s : %s\n" % (k, " ".join([str(s) for s in Nonterminals[k]]))) if (cycle_check): compute_reachable() error += compute_terminates() # error += check_cycles() return error # ----------------------------------------------------------------------------- # build_lritems() # # This function walks the list of productions and builds a complete set of the # LR items. The LR items are stored in two ways: First, they are uniquely # numbered and placed in the list _lritems. Second, a linked list of LR items # is built for each production. For example: # # E -> E PLUS E # # Creates the list # # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] # ----------------------------------------------------------------------------- def build_lritems(): for p in Productions: lastlri = p lri = p.lr_item(0) i = 0 while 1: lri = p.lr_item(i) lastlri.lr_next = lri if not lri: break lri.lr_num = len(LRitems) LRitems.append(lri) lastlri = lri i += 1 # In order for the rest of the parser generator to work, we need to # guarantee that no more lritems are generated. Therefore, we nuke # the p.lr_item method. (Only used in debugging) # Production.lr_item = None # ----------------------------------------------------------------------------- # add_precedence() # # Given a list of precedence rules, add to the precedence table. # ----------------------------------------------------------------------------- def add_precedence(plist): plevel = 0 error = 0 for p in plist: plevel += 1 try: prec = p[0] terms = p[1:] if prec != 'left' and prec != 'right' and prec != 'nonassoc': sys.stderr.write("yacc: Invalid precedence '%s'\n" % prec) return -1 for t in terms: if Precedence.has_key(t): sys.stderr.write("yacc: Precedence already specified for terminal '%s'\n" % t) error += 1 continue Precedence[t] = (prec,plevel) except: sys.stderr.write("yacc: Invalid precedence table.\n") error += 1 return error # ----------------------------------------------------------------------------- # check_precedence() # # Checks the use of the Precedence tables. This makes sure all of the symbols # are terminals or were used with %prec # ----------------------------------------------------------------------------- def check_precedence(): error = 0 for precname in Precedence.keys(): if not (Terminals.has_key(precname) or UsedPrecedence.has_key(precname)): sys.stderr.write("yacc: Precedence rule '%s' defined for unknown symbol '%s'\n" % (Precedence[precname][0],precname)) error += 1 return error # ----------------------------------------------------------------------------- # augment_grammar() # # Compute the augmented grammar. This is just a rule S' -> start where start # is the starting symbol. # ----------------------------------------------------------------------------- def augment_grammar(start=None): if not start: start = Productions[1].name Productions[0] = Production(name="S'",prod=[start],number=0,len=1,prec=('right',0),func=None) Productions[0].usyms = [ start ] Nonterminals[start].append(0) # ------------------------------------------------------------------------- # first() # # Compute the value of FIRST1(beta) where beta is a tuple of symbols. # # During execution of compute_first1, the result may be incomplete. # Afterward (e.g., when called from compute_follow()), it will be complete. # ------------------------------------------------------------------------- def first(beta): # We are computing First(x1,x2,x3,...,xn) result = [ ] for x in beta: x_produces_empty = 0 # Add all the non- symbols of First[x] to the result. for f in First[x]: if f == '': x_produces_empty = 1 else: if f not in result: result.append(f) if x_produces_empty: # We have to consider the next x in beta, # i.e. stay in the loop. pass else: # We don't have to consider any further symbols in beta. break else: # There was no 'break' from the loop, # so x_produces_empty was true for all x in beta, # so beta produces empty as well. result.append('') return result # FOLLOW(x) # Given a non-terminal. This function computes the set of all symbols # that might follow it. Dragon book, p. 189. def compute_follow(start=None): # Add '$end' to the follow list of the start symbol for k in Nonterminals.keys(): Follow[k] = [ ] if not start: start = Productions[1].name Follow[start] = [ '$end' ] while 1: didadd = 0 for p in Productions[1:]: # Here is the production set for i in range(len(p.prod)): B = p.prod[i] if Nonterminals.has_key(B): # Okay. We got a non-terminal in a production fst = first(p.prod[i+1:]) hasempty = 0 for f in fst: if f != '' and f not in Follow[B]: Follow[B].append(f) didadd = 1 if f == '': hasempty = 1 if hasempty or i == (len(p.prod)-1): # Add elements of follow(a) to follow(b) for f in Follow[p.name]: if f not in Follow[B]: Follow[B].append(f) didadd = 1 if not didadd: break if 0 and yaccdebug: _vf.write('\nFollow:\n') for k in Nonterminals.keys(): _vf.write("%-20s : %s\n" % (k, " ".join([str(s) for s in Follow[k]]))) # ------------------------------------------------------------------------- # compute_first1() # # Compute the value of FIRST1(X) for all symbols # ------------------------------------------------------------------------- def compute_first1(): # Terminals: for t in Terminals.keys(): First[t] = [t] First['$end'] = ['$end'] First['#'] = ['#'] # what's this for? # Nonterminals: # Initialize to the empty set: for n in Nonterminals.keys(): First[n] = [] # Then propagate symbols until no change: while 1: some_change = 0 for n in Nonterminals.keys(): for p in Prodnames[n]: for f in first(p.prod): if f not in First[n]: First[n].append( f ) some_change = 1 if not some_change: break if 0 and yaccdebug: _vf.write('\nFirst:\n') for k in Nonterminals.keys(): _vf.write("%-20s : %s\n" % (k, " ".join([str(s) for s in First[k]]))) # ----------------------------------------------------------------------------- # === SLR Generation === # # The following functions are used to construct SLR (Simple LR) parsing tables # as described on p.221-229 of the dragon book. # ----------------------------------------------------------------------------- # Global variables for the LR parsing engine def lr_init_vars(): global _lr_action, _lr_goto, _lr_method global _lr_goto_cache, _lr0_cidhash _lr_action = { } # Action table _lr_goto = { } # Goto table _lr_method = "Unknown" # LR method used _lr_goto_cache = { } _lr0_cidhash = { } # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. # prodlist is a list of productions. _add_count = 0 # Counter used to detect cycles def lr0_closure(I): global _add_count _add_count += 1 prodlist = Productions # Add everything in I to J J = I[:] didadd = 1 while didadd: didadd = 0 for j in J: for x in j.lrafter: if x.lr0_added == _add_count: continue # Add B --> .G to J J.append(x.lr_next) x.lr0_added = _add_count didadd = 1 return J # Compute the LR(0) goto function goto(I,X) where I is a set # of LR(0) items and X is a grammar symbol. This function is written # in a way that guarantees uniqueness of the generated goto sets # (i.e. the same goto set will never be returned as two different Python # objects). With uniqueness, we can later do fast set comparisons using # id(obj) instead of element-wise comparison. def lr0_goto(I,x): # First we look for a previously cached entry g = _lr_goto_cache.get((id(I),x),None) if g: return g # Now we generate the goto set in a way that guarantees uniqueness # of the result s = _lr_goto_cache.get(x,None) if not s: s = { } _lr_goto_cache[x] = s gs = [ ] for p in I: n = p.lr_next if n and n.lrbefore == x: s1 = s.get(id(n),None) if not s1: s1 = { } s[id(n)] = s1 gs.append(n) s = s1 g = s.get('$end',None) if not g: if gs: g = lr0_closure(gs) s['$end'] = g else: s['$end'] = gs _lr_goto_cache[(id(I),x)] = g return g _lr0_cidhash = { } # Compute the LR(0) sets of item function def lr0_items(): C = [ lr0_closure([Productions[0].lr_next]) ] i = 0 for I in C: _lr0_cidhash[id(I)] = i i += 1 # Loop over the items in C and each grammar symbols i = 0 while i < len(C): I = C[i] i += 1 # Collect all of the symbols that could possibly be in the goto(I,X) sets asyms = { } for ii in I: for s in ii.usyms: asyms[s] = None for x in asyms.keys(): g = lr0_goto(I,x) if not g: continue if _lr0_cidhash.has_key(id(g)): continue _lr0_cidhash[id(g)] = len(C) C.append(g) return C # ----------------------------------------------------------------------------- # ==== LALR(1) Parsing ==== # # LALR(1) parsing is almost exactly the same as SLR except that instead of # relying upon Follow() sets when performing reductions, a more selective # lookahead set that incorporates the state of the LR(0) machine is utilized. # Thus, we mainly just have to focus on calculating the lookahead sets. # # The method used here is due to DeRemer and Pennelo (1982). # # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) # Lookahead Sets", ACM Transactions on Programming Languages and Systems, # Vol. 4, No. 4, Oct. 1982, pp. 615-649 # # Further details can also be found in: # # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", # McGraw-Hill Book Company, (1985). # # Note: This implementation is a complete replacement of the LALR(1) # implementation in PLY-1.x releases. That version was based on # a less efficient algorithm and it had bugs in its implementation. # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # compute_nullable_nonterminals() # # Creates a dictionary containing all of the non-terminals that might produce # an empty production. # ----------------------------------------------------------------------------- def compute_nullable_nonterminals(): nullable = {} num_nullable = 0 while 1: for p in Productions[1:]: if p.len == 0: nullable[p.name] = 1 continue for t in p.prod: if not nullable.has_key(t): break else: nullable[p.name] = 1 if len(nullable) == num_nullable: break num_nullable = len(nullable) return nullable # ----------------------------------------------------------------------------- # find_nonterminal_trans(C) # # Given a set of LR(0) items, this functions finds all of the non-terminal # transitions. These are transitions in which a dot appears immediately before # a non-terminal. Returns a list of tuples of the form (state,N) where state # is the state number and N is the nonterminal symbol. # # The input C is the set of LR(0) items. # ----------------------------------------------------------------------------- def find_nonterminal_transitions(C): trans = [] for state in range(len(C)): for p in C[state]: if p.lr_index < p.len - 1: t = (state,p.prod[p.lr_index+1]) if Nonterminals.has_key(t[1]): if t not in trans: trans.append(t) state = state + 1 return trans # ----------------------------------------------------------------------------- # dr_relation() # # Computes the DR(p,A) relationships for non-terminal transitions. The input # is a tuple (state,N) where state is a number and N is a nonterminal symbol. # # Returns a list of terminals. # ----------------------------------------------------------------------------- def dr_relation(C,trans,nullable): dr_set = { } state,N = trans terms = [] g = lr0_goto(C[state],N) for p in g: if p.lr_index < p.len - 1: a = p.prod[p.lr_index+1] if Terminals.has_key(a): if a not in terms: terms.append(a) # This extra bit is to handle the start state if state == 0 and N == Productions[0].prod[0]: terms.append('$end') return terms # ----------------------------------------------------------------------------- # reads_relation() # # Computes the READS() relation (p,A) READS (t,C). # ----------------------------------------------------------------------------- def reads_relation(C, trans, empty): # Look for empty transitions rel = [] state, N = trans g = lr0_goto(C[state],N) j = _lr0_cidhash.get(id(g),-1) for p in g: if p.lr_index < p.len - 1: a = p.prod[p.lr_index + 1] if empty.has_key(a): rel.append((j,a)) return rel # ----------------------------------------------------------------------------- # compute_lookback_includes() # # Determines the lookback and includes relations # # LOOKBACK: # # This relation is determined by running the LR(0) state machine forward. # For example, starting with a production "N : . A B C", we run it forward # to obtain "N : A B C ." We then build a relationship between this final # state and the starting state. These relationships are stored in a dictionary # lookdict. # # INCLUDES: # # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). # # This relation is used to determine non-terminal transitions that occur # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) # if the following holds: # # B -> LAT, where T -> epsilon and p' -L-> p # # L is essentially a prefix (which may be empty), T is a suffix that must be # able to derive an empty string. State p' must lead to state p with the string L. # # ----------------------------------------------------------------------------- def compute_lookback_includes(C,trans,nullable): lookdict = {} # Dictionary of lookback relations includedict = {} # Dictionary of include relations # Make a dictionary of non-terminal transitions dtrans = {} for t in trans: dtrans[t] = 1 # Loop over all transitions and compute lookbacks and includes for state,N in trans: lookb = [] includes = [] for p in C[state]: if p.name != N: continue # Okay, we have a name match. We now follow the production all the way # through the state machine until we get the . on the right hand side lr_index = p.lr_index j = state while lr_index < p.len - 1: lr_index = lr_index + 1 t = p.prod[lr_index] # Check to see if this symbol and state are a non-terminal transition if dtrans.has_key((j,t)): # Yes. Okay, there is some chance that this is an includes relation # the only way to know for certain is whether the rest of the # production derives empty li = lr_index + 1 while li < p.len: if Terminals.has_key(p.prod[li]): break # No forget it if not nullable.has_key(p.prod[li]): break li = li + 1 else: # Appears to be a relation between (j,t) and (state,N) includes.append((j,t)) g = lr0_goto(C[j],t) # Go to next set j = _lr0_cidhash.get(id(g),-1) # Go to next state # When we get here, j is the final state, now we have to locate the production for r in C[j]: if r.name != p.name: continue if r.len != p.len: continue i = 0 # This look is comparing a production ". A B C" with "A B C ." while i < r.lr_index: if r.prod[i] != p.prod[i+1]: break i = i + 1 else: lookb.append((j,r)) for i in includes: if not includedict.has_key(i): includedict[i] = [] includedict[i].append((state,N)) lookdict[(state,N)] = lookb return lookdict,includedict # ----------------------------------------------------------------------------- # digraph() # traverse() # # The following two functions are used to compute set valued functions # of the form: # # F(x) = F'(x) U U{F(y) | x R y} # # This is used to compute the values of Read() sets as well as FOLLOW sets # in LALR(1) generation. # # Inputs: X - An input set # R - A relation # FP - Set-valued function # ------------------------------------------------------------------------------ def digraph(X,R,FP): N = { } for x in X: N[x] = 0 stack = [] F = { } for x in X: if N[x] == 0: traverse(x,N,stack,F,X,R,FP) return F def traverse(x,N,stack,F,X,R,FP): stack.append(x) d = len(stack) N[x] = d F[x] = FP(x) # F(X) <- F'(x) rel = R(x) # Get y's related to x for y in rel: if N[y] == 0: traverse(y,N,stack,F,X,R,FP) N[x] = min(N[x],N[y]) for a in F.get(y,[]): if a not in F[x]: F[x].append(a) if N[x] == d: N[stack[-1]] = sys.maxint F[stack[-1]] = F[x] element = stack.pop() while element != x: N[stack[-1]] = sys.maxint F[stack[-1]] = F[x] element = stack.pop() # ----------------------------------------------------------------------------- # compute_read_sets() # # Given a set of LR(0) items, this function computes the read sets. # # Inputs: C = Set of LR(0) items # ntrans = Set of nonterminal transitions # nullable = Set of empty transitions # # Returns a set containing the read sets # ----------------------------------------------------------------------------- def compute_read_sets(C, ntrans, nullable): FP = lambda x: dr_relation(C,x,nullable) R = lambda x: reads_relation(C,x,nullable) F = digraph(ntrans,R,FP) return F # ----------------------------------------------------------------------------- # compute_follow_sets() # # Given a set of LR(0) items, a set of non-terminal transitions, a readset, # and an include set, this function computes the follow sets # # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} # # Inputs: # ntrans = Set of nonterminal transitions # readsets = Readset (previously computed) # inclsets = Include sets (previously computed) # # Returns a set containing the follow sets # ----------------------------------------------------------------------------- def compute_follow_sets(ntrans,readsets,inclsets): FP = lambda x: readsets[x] R = lambda x: inclsets.get(x,[]) F = digraph(ntrans,R,FP) return F # ----------------------------------------------------------------------------- # add_lookaheads() # # Attaches the lookahead symbols to grammar rules. # # Inputs: lookbacks - Set of lookback relations # followset - Computed follow set # # This function directly attaches the lookaheads to productions contained # in the lookbacks set # ----------------------------------------------------------------------------- def add_lookaheads(lookbacks,followset): for trans,lb in lookbacks.items(): # Loop over productions in lookback for state,p in lb: if not p.lookaheads.has_key(state): p.lookaheads[state] = [] f = followset.get(trans,[]) for a in f: if a not in p.lookaheads[state]: p.lookaheads[state].append(a) # ----------------------------------------------------------------------------- # add_lalr_lookaheads() # # This function does all of the work of adding lookahead information for use # with LALR parsing # ----------------------------------------------------------------------------- def add_lalr_lookaheads(C): # Determine all of the nullable nonterminals nullable = compute_nullable_nonterminals() # Find all non-terminal transitions trans = find_nonterminal_transitions(C) # Compute read sets readsets = compute_read_sets(C,trans,nullable) # Compute lookback/includes relations lookd, included = compute_lookback_includes(C,trans,nullable) # Compute LALR FOLLOW sets followsets = compute_follow_sets(trans,readsets,included) # Add all of the lookaheads add_lookaheads(lookd,followsets) # ----------------------------------------------------------------------------- # lr_parse_table() # # This function constructs the parse tables for SLR or LALR # ----------------------------------------------------------------------------- def lr_parse_table(method): global _lr_method goto = _lr_goto # Goto array action = _lr_action # Action array actionp = { } # Action production array (temporary) _lr_method = method n_srconflict = 0 n_rrconflict = 0 if yaccdebug: sys.stderr.write("yacc: Generating %s parsing table...\n" % method) _vf.write("\n\nParsing method: %s\n\n" % method) # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items # This determines the number of states C = lr0_items() if method == 'LALR': add_lalr_lookaheads(C) # Build the parser table, state by state st = 0 for I in C: # Loop over each production in I actlist = [ ] # List of actions st_action = { } st_actionp = { } st_goto = { } if yaccdebug: _vf.write("\nstate %d\n\n" % st) for p in I: _vf.write(" (%d) %s\n" % (p.number, str(p))) _vf.write("\n") for p in I: try: if p.len == p.lr_index + 1: if p.name == "S'": # Start symbol. Accept! st_action["$end"] = 0 st_actionp["$end"] = p else: # We are at the end of a production. Reduce! if method == 'LALR': laheads = p.lookaheads[st] else: laheads = Follow[p.name] for a in laheads: actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) r = st_action.get(a,None) if r is not None: # Whoa. Have a shift/reduce or reduce/reduce conflict if r > 0: # Need to decide on shift or reduce here # By default we favor shifting. Need to add # some precedence rules here. sprec,slevel = Productions[st_actionp[a].number].prec rprec,rlevel = Precedence.get(a,('right',0)) if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): # We really need to reduce here. st_action[a] = -p.number st_actionp[a] = p if not slevel and not rlevel: _vfc.write("shift/reduce conflict in state %d resolved as reduce.\n" % st) _vf.write(" ! shift/reduce conflict for %s resolved as reduce.\n" % a) n_srconflict += 1 elif (slevel == rlevel) and (rprec == 'nonassoc'): st_action[a] = None else: # Hmmm. Guess we'll keep the shift if not rlevel: _vfc.write("shift/reduce conflict in state %d resolved as shift.\n" % st) _vf.write(" ! shift/reduce conflict for %s resolved as shift.\n" % a) n_srconflict +=1 elif r < 0: # Reduce/reduce conflict. In this case, we favor the rule # that was defined first in the grammar file oldp = Productions[-r] pp = Productions[p.number] if oldp.line > pp.line: st_action[a] = -p.number st_actionp[a] = p # sys.stderr.write("Reduce/reduce conflict in state %d\n" % st) n_rrconflict += 1 _vfc.write("reduce/reduce conflict in state %d resolved using rule %d (%s).\n" % (st, st_actionp[a].number, st_actionp[a])) _vf.write(" ! reduce/reduce conflict for %s resolved using rule %d (%s).\n" % (a,st_actionp[a].number, st_actionp[a])) else: sys.stderr.write("Unknown conflict in state %d\n" % st) else: st_action[a] = -p.number st_actionp[a] = p else: i = p.lr_index a = p.prod[i+1] # Get symbol right after the "." if Terminals.has_key(a): g = lr0_goto(I,a) j = _lr0_cidhash.get(id(g),-1) if j >= 0: # We are in a shift state actlist.append((a,p,"shift and go to state %d" % j)) r = st_action.get(a,None) if r is not None: # Whoa have a shift/reduce or shift/shift conflict if r > 0: if r != j: sys.stderr.write("Shift/shift conflict in state %d\n" % st) elif r < 0: # Do a precedence check. # - if precedence of reduce rule is higher, we reduce. # - if precedence of reduce is same and left assoc, we reduce. # - otherwise we shift rprec,rlevel = Productions[st_actionp[a].number].prec sprec,slevel = Precedence.get(a,('right',0)) if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): # We decide to shift here... highest precedence to shift st_action[a] = j st_actionp[a] = p if not rlevel: n_srconflict += 1 _vfc.write("shift/reduce conflict in state %d resolved as shift.\n" % st) _vf.write(" ! shift/reduce conflict for %s resolved as shift.\n" % a) elif (slevel == rlevel) and (rprec == 'nonassoc'): st_action[a] = None else: # Hmmm. Guess we'll keep the reduce if not slevel and not rlevel: n_srconflict +=1 _vfc.write("shift/reduce conflict in state %d resolved as reduce.\n" % st) _vf.write(" ! shift/reduce conflict for %s resolved as reduce.\n" % a) else: sys.stderr.write("Unknown conflict in state %d\n" % st) else: st_action[a] = j st_actionp[a] = p except StandardError,e: print sys.exc_info() raise YaccError, "Hosed in lr_parse_table" # Print the actions associated with each terminal if yaccdebug: _actprint = { } for a,p,m in actlist: if st_action.has_key(a): if p is st_actionp[a]: _vf.write(" %-15s %s\n" % (a,m)) _actprint[(a,m)] = 1 _vf.write("\n") for a,p,m in actlist: if st_action.has_key(a): if p is not st_actionp[a]: if not _actprint.has_key((a,m)): _vf.write(" ! %-15s [ %s ]\n" % (a,m)) _actprint[(a,m)] = 1 # Construct the goto table for this state if yaccdebug: _vf.write("\n") nkeys = { } for ii in I: for s in ii.usyms: if Nonterminals.has_key(s): nkeys[s] = None for n in nkeys.keys(): g = lr0_goto(I,n) j = _lr0_cidhash.get(id(g),-1) if j >= 0: st_goto[n] = j if yaccdebug: _vf.write(" %-30s shift and go to state %d\n" % (n,j)) action[st] = st_action actionp[st] = st_actionp goto[st] = st_goto st += 1 if yaccdebug: if n_srconflict == 1: sys.stderr.write("yacc: %d shift/reduce conflict\n" % n_srconflict) if n_srconflict > 1: sys.stderr.write("yacc: %d shift/reduce conflicts\n" % n_srconflict) if n_rrconflict == 1: sys.stderr.write("yacc: %d reduce/reduce conflict\n" % n_rrconflict) if n_rrconflict > 1: sys.stderr.write("yacc: %d reduce/reduce conflicts\n" % n_rrconflict) # ----------------------------------------------------------------------------- # ==== LR Utility functions ==== # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- # _lr_write_tables() # # This function writes the LR parsing tables to a file # ----------------------------------------------------------------------------- def lr_write_tables(modulename=tab_module,outputdir=''): if isinstance(modulename, types.ModuleType): print >>sys.stderr, "Warning module %s is inconsistent with the grammar (ignored)" % modulename return basemodulename = modulename.split(".")[-1] filename = os.path.join(outputdir,basemodulename) + ".py" try: f = open(filename,"w") f.write(""" # %s # This file is automatically generated. Do not edit. _lr_method = %s _lr_signature = %s """ % (filename, repr(_lr_method), repr(Signature.digest()))) # Change smaller to 0 to go back to original tables smaller = 1 # Factor out names to try and make smaller if smaller: items = { } for s,nd in _lr_action.items(): for name,v in nd.items(): i = items.get(name) if not i: i = ([],[]) items[name] = i i[0].append(s) i[1].append(v) f.write("\n_lr_action_items = {") for k,v in items.items(): f.write("%r:([" % k) for i in v[0]: f.write("%r," % i) f.write("],[") for i in v[1]: f.write("%r," % i) f.write("]),") f.write("}\n") f.write(""" _lr_action = { } for _k, _v in _lr_action_items.items(): for _x,_y in zip(_v[0],_v[1]): if not _lr_action.has_key(_x): _lr_action[_x] = { } _lr_action[_x][_k] = _y del _lr_action_items """) else: f.write("\n_lr_action = { "); for k,v in _lr_action.items(): f.write("(%r,%r):%r," % (k[0],k[1],v)) f.write("}\n"); if smaller: # Factor out names to try and make smaller items = { } for s,nd in _lr_goto.items(): for name,v in nd.items(): i = items.get(name) if not i: i = ([],[]) items[name] = i i[0].append(s) i[1].append(v) f.write("\n_lr_goto_items = {") for k,v in items.items(): f.write("%r:([" % k) for i in v[0]: f.write("%r," % i) f.write("],[") for i in v[1]: f.write("%r," % i) f.write("]),") f.write("}\n") f.write(""" _lr_goto = { } for _k, _v in _lr_goto_items.items(): for _x,_y in zip(_v[0],_v[1]): if not _lr_goto.has_key(_x): _lr_goto[_x] = { } _lr_goto[_x][_k] = _y del _lr_goto_items """) else: f.write("\n_lr_goto = { "); for k,v in _lr_goto.items(): f.write("(%r,%r):%r," % (k[0],k[1],v)) f.write("}\n"); # Write production table f.write("_lr_productions = [\n") for p in Productions: if p: if (p.func): f.write(" (%r,%d,%r,%r,%d),\n" % (p.name, p.len, p.func.__name__,p.file,p.line)) else: f.write(" (%r,%d,None,None,None),\n" % (p.name, p.len)) else: f.write(" None,\n") f.write("]\n") f.close() except IOError,e: print >>sys.stderr, "Unable to create '%s'" % filename print >>sys.stderr, e return def lr_read_tables(module=tab_module,optimize=0): global _lr_action, _lr_goto, _lr_productions, _lr_method try: if isinstance(module,types.ModuleType): parsetab = module else: exec "import %s as parsetab" % module if (optimize) or (Signature.digest() == parsetab._lr_signature): _lr_action = parsetab._lr_action _lr_goto = parsetab._lr_goto _lr_productions = parsetab._lr_productions _lr_method = parsetab._lr_method return 1 else: return 0 except (ImportError,AttributeError): return 0 # ----------------------------------------------------------------------------- # yacc(module) # # Build the parser module # ----------------------------------------------------------------------------- def yacc(method=default_lr, debug=yaccdebug, module=None, tabmodule=tab_module, start=None, check_recursion=1, optimize=0,write_tables=1,debugfile=debug_file,outputdir=''): global yaccdebug yaccdebug = debug initialize_vars() files = { } error = 0 # Add parsing method to signature Signature.update(method) # If a "module" parameter was supplied, extract its dictionary. # Note: a module may in fact be an instance as well. if module: # User supplied a module object. if isinstance(module, types.ModuleType): ldict = module.__dict__ elif isinstance(module, _INSTANCETYPE): _items = [(k,getattr(module,k)) for k in dir(module)] ldict = { } for i in _items: ldict[i[0]] = i[1] else: raise ValueError,"Expected a module" else: # No module given. We might be able to get information from the caller. # Throw an exception and unwind the traceback to get the globals try: raise RuntimeError except RuntimeError: e,b,t = sys.exc_info() f = t.tb_frame f = f.f_back # Walk out to our calling function if f.f_globals is f.f_locals: # Collect global and local variations from caller ldict = f.f_globals else: ldict = f.f_globals.copy() ldict.update(f.f_locals) # Add starting symbol to signature if not start: start = ldict.get("start",None) if start: Signature.update(start) # Look for error handler ef = ldict.get('p_error',None) if ef: if isinstance(ef,types.FunctionType): ismethod = 0 elif isinstance(ef, types.MethodType): ismethod = 1 else: raise YaccError,"'p_error' defined, but is not a function or method." eline = ef.func_code.co_firstlineno efile = ef.func_code.co_filename files[efile] = None if (ef.func_code.co_argcount != 1+ismethod): raise YaccError,"%s:%d: p_error() requires 1 argument." % (efile,eline) global Errorfunc Errorfunc = ef else: print >>sys.stderr, "yacc: Warning. no p_error() function is defined." # If running in optimized mode. We're going to read tables instead if (optimize and lr_read_tables(tabmodule,1)): # Read parse table del Productions[:] for p in _lr_productions: if not p: Productions.append(None) else: m = MiniProduction() m.name = p[0] m.len = p[1] m.file = p[3] m.line = p[4] if p[2]: m.func = ldict[p[2]] Productions.append(m) else: # Get the tokens map if (module and isinstance(module,_INSTANCETYPE)): tokens = getattr(module,"tokens",None) else: tokens = ldict.get("tokens",None) if not tokens: raise YaccError,"module does not define a list 'tokens'" if not (isinstance(tokens,types.ListType) or isinstance(tokens,types.TupleType)): raise YaccError,"tokens must be a list or tuple." # Check to see if a requires dictionary is defined. requires = ldict.get("require",None) if requires: if not (isinstance(requires,types.DictType)): raise YaccError,"require must be a dictionary." for r,v in requires.items(): try: if not (isinstance(v,types.ListType)): raise TypeError v1 = [x.split(".") for x in v] Requires[r] = v1 except StandardError: print >>sys.stderr, "Invalid specification for rule '%s' in require. Expected a list of strings" % r # Build the dictionary of terminals. We a record a 0 in the # dictionary to track whether or not a terminal is actually # used in the grammar if 'error' in tokens: print >>sys.stderr, "yacc: Illegal token 'error'. Is a reserved word." raise YaccError,"Illegal token name" for n in tokens: if Terminals.has_key(n): print >>sys.stderr, "yacc: Warning. Token '%s' multiply defined." % n Terminals[n] = [ ] Terminals['error'] = [ ] # Get the precedence map (if any) prec = ldict.get("precedence",None) if prec: if not (isinstance(prec,types.ListType) or isinstance(prec,types.TupleType)): raise YaccError,"precedence must be a list or tuple." add_precedence(prec) Signature.update(repr(prec)) for n in tokens: if not Precedence.has_key(n): Precedence[n] = ('right',0) # Default, right associative, 0 precedence # Get the list of built-in functions with p_ prefix symbols = [ldict[f] for f in ldict.keys() if (type(ldict[f]) in (types.FunctionType, types.MethodType) and ldict[f].__name__[:2] == 'p_' and ldict[f].__name__ != 'p_error')] # Check for non-empty symbols if len(symbols) == 0: raise YaccError,"no rules of the form p_rulename are defined." # Sort the symbols by line number symbols.sort(lambda x,y: cmp(x.func_code.co_firstlineno,y.func_code.co_firstlineno)) # Add all of the symbols to the grammar for f in symbols: if (add_function(f)) < 0: error += 1 else: files[f.func_code.co_filename] = None # Make a signature of the docstrings for f in symbols: if f.__doc__: Signature.update(f.__doc__) lr_init_vars() if error: raise YaccError,"Unable to construct parser." if not lr_read_tables(tabmodule): # Validate files for filename in files.keys(): if not validate_file(filename): error = 1 # Validate dictionary validate_dict(ldict) if start and not Prodnames.has_key(start): raise YaccError,"Bad starting symbol '%s'" % start augment_grammar(start) error = verify_productions(cycle_check=check_recursion) otherfunc = [ldict[f] for f in ldict.keys() if (type(f) in (types.FunctionType,types.MethodType) and ldict[f].__name__[:2] != 'p_')] # Check precedence rules if check_precedence(): error = 1 if error: raise YaccError,"Unable to construct parser." build_lritems() compute_first1() compute_follow(start) if method in ['SLR','LALR']: lr_parse_table(method) else: raise YaccError, "Unknown parsing method '%s'" % method if write_tables: lr_write_tables(tabmodule,outputdir) if yaccdebug: try: f = open(os.path.join(outputdir,debugfile),"w") f.write(_vfc.getvalue()) f.write("\n\n") f.write(_vf.getvalue()) f.close() except IOError,e: print >>sys.stderr, "yacc: can't create '%s'" % debugfile,e # Made it here. Create a parser object and set up its internal state. # Set global parse() method to bound method of parser object. p = Parser("xyzzy") p.productions = Productions p.errorfunc = Errorfunc p.action = _lr_action p.goto = _lr_goto p.method = _lr_method p.require = Requires global parse parse = p.parse global parser parser = p # Clean up all of the globals we created if (not optimize): yacc_cleanup() return p # yacc_cleanup function. Delete all of the global variables # used during table construction def yacc_cleanup(): global _lr_action, _lr_goto, _lr_method, _lr_goto_cache del _lr_action, _lr_goto, _lr_method, _lr_goto_cache global Productions, Prodnames, Prodmap, Terminals global Nonterminals, First, Follow, Precedence, UsedPrecedence, LRitems global Errorfunc, Signature, Requires del Productions, Prodnames, Prodmap, Terminals del Nonterminals, First, Follow, Precedence, UsedPrecedence, LRitems del Errorfunc, Signature, Requires global _vf, _vfc del _vf, _vfc # Stub that raises an error if parsing is attempted without first calling yacc() def parse(*args,**kwargs): raise YaccError, "yacc: No parser built with yacc()" pywbem-0.7.0/setup.py0000644000175000001440000000303011120514317013371 0ustar bartusers'''A pure-Python library for performing operations using the WBEM management protocol.''' # # (C) Copyright 2004 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter from distutils.core import setup, Extension import sys, string, os import shutil import mof_compiler mof_compiler._build() args = {'name': 'pywbem', 'author': 'Tim Potter', 'author_email': 'tpot@hp.com', 'description': 'Python WBEM client library', 'long_description': __doc__, 'platforms': ['any'], 'url': 'http://pywbem.sf.net/', 'version': '0.7.0', 'license': 'LGPLv2', 'packages': ['pywbem'], # Make packages in root dir appear in pywbem module 'package_dir': {'pywbem': ''}, # Make extensions in root dir appear in pywbem module 'ext_package': 'pywbem', } setup(**args) pywbem-0.7.0/moflextab.py0000644000175000001440000000513310757070147014235 0ustar bartusers# moflextab.py. This file automatically created by PLY (version 2.3). Don't edit! _lextokens = {'DT_UINT8': None, 'DISABLEOVERRIDE': None, 'PRAGMA': None, 'DT_SINT64': None, 'octalValue': None, 'hexValue': None, 'stringValue': None, 'decimalValue': None, 'NULL': None, 'PARAMETER': None, 'charValue': None, 'DT_DATETIME': None, 'REFERENCE': None, 'RESTRICTED': None, 'TOSUBCLASS': None, 'DT_REAL64': None, 'TRANSLATABLE': None, 'ENABLEOVERRIDE': None, 'DT_UINT32': None, 'ASSOCIATION': None, 'SCHEMA': None, 'DT_BOOL': None, 'floatValue': None, 'METHOD': None, 'DT_SINT16': None, 'AS': None, 'DT_SINT32': None, 'binaryValue': None, 'DT_STR': None, 'SCOPE': None, 'FLAVOR': None, 'DT_CHAR16': None, 'TRUE': None, 'DT_REAL32': None, 'DT_SINT8': None, 'FALSE': None, 'QUALIFIER': None, 'DT_UINT64': None, 'OF': None, 'REF': None, 'CLASS': None, 'INSTANCE': None, 'IDENTIFIER': None, 'PROPERTY': None, 'INDICATION': None, 'DT_UINT16': None, 'ANY': None} _lexreflags = 0 _lexliterals = '#(){};[],$:=' _lexstateinfo = {'INITIAL': 'inclusive'} _lexstatere = {'INITIAL': [('(?P//.*)|(?P/\\*(.|\\n)*?\\*/)|(?P([a-zA-Z_]|(([\\xC2-\\xDF][\\x80-\\xBF])|(\\xE0[\\xA0-\\xBF][\\x80-\\xBF])|([\\xE1-\\xEC][\\x80-\\xBF][\\x80-\\xBF])|(\\xED[\\x80-\\x9F][\\x80-\\xBF])|([\\xEE-\\xEF][\\x80-\\xBF][\\x80-\\xBF])|(\\xF0[\\x90-\\xBF][\\x80-\\xBF][\\x80-\\xBF])|([\\xF1-\\xF3][\\x80-\\xBF][\\x80-\\xBF][\\x80-\\xBF])|(\\xF4[\\x80-\\x8F][\\x80-\\xBF][\\x80-\\xBF])))([0-9a-zA-Z_]|(([\\xC2-\\xDF][\\x80-\\xBF])|(\\xE0[\\xA0-\\xBF][\\x80-\\xBF])|([\\xE1-\\xEC][\\x80-\\xBF][\\x80-\\xBF])|(\\xED[\\x80-\\x9F][\\x80-\\xBF])|([\\xEE-\\xEF][\\x80-\\xBF][\\x80-\\xBF])|(\\xF0[\\x90-\\xBF][\\x80-\\xBF][\\x80-\\xBF])|([\\xF1-\\xF3][\\x80-\\xBF][\\x80-\\xBF][\\x80-\\xBF])|(\\xF4[\\x80-\\x8F][\\x80-\\xBF][\\x80-\\xBF])))*)|(?P\\n+)|(?P"([^"\\\\\\n\\r]|([\\\\](([bfnrt\'"\\\\])|(x[0-9a-fA-F]{1,4}))))*")|(?P[+-]?[0-9]*\\.[0-9]+([eE][+-]?[0-9]+)?)|(?P[+-]?0[xX][0-9a-fA-F]+)|(?P[+-]?([1-9][0-9]*|0))|(?P[+-]?[01]+[bB])|(?P[+-]?0[0-7]+)', [None, ('t_COMMENT', 'COMMENT'), ('t_MCOMMENT', 'MCOMMENT'), None, ('t_IDENTIFIER', 'IDENTIFIER'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_newline', 'newline'), (None, 'stringValue'), None, None, None, None, None, (None, 'floatValue'), None, (None, 'hexValue'), (None, 'decimalValue'), None, (None, 'binaryValue'), (None, 'octalValue')])]} _lexstateignore = {'INITIAL': ' \r\t'} _lexstateerrorf = {'INITIAL': 't_error'} pywbem-0.7.0/cim_http.py0000644000175000001440000002725611104440777014072 0ustar bartusers# # (C) Copyright 2003-2005 Hewlett-Packard Development Company, L.P. # (C) Copyright 2006-2007 Novell, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter # Author: Martin Pool # Author: Bart Whiteley ''' This module implements CIM operations over HTTP. This module should not know anything about the fact that the data being transferred is XML. It is up to the caller to format the input data and interpret the result. ''' import sys, string, re, os, socket, pwd from stat import S_ISSOCK import cim_obj from types import StringTypes class Error(Exception): """This exception is raised when a transport error occurs.""" pass class AuthError(Error): """This exception is raised when an authentication error (401) occurs.""" pass def parse_url(url): """Return a tuple of (host, port, ssl) from the URL parameter. The returned port defaults to 5988 if not specified. SSL supports defaults to False if not specified.""" host = url # Defaults port = 5988 ssl = False if re.match("https", url): # Set SSL if specified ssl = True port = 5989 m = re.search("^https?://", url) # Eat protocol name if m: host = url[len(m.group(0)):] s = string.split(host, ":") # Set port number if len(s) != 1: host = s[0] port = int(s[1]) return host, port, ssl def wbem_request(url, data, creds, headers = [], debug = 0, x509 = None, verify_callback = None): """Send XML data over HTTP to the specified url. Return the response in XML. Uses Python's build-in httplib. x509 may be a dictionary containing the location of the SSL certificate and key files.""" import httplib, base64, urllib class HTTPBaseConnection: def send(self, str): """ Same as httplib.HTTPConnection.send(), except we don't check for sigpipe and close the connection. If the connection gets closed, getresponse() fails. """ if self.sock is None: if self.auto_open: self.connect() else: raise httplib.NotConnected() if self.debuglevel > 0: print "send:", repr(str) self.sock.sendall(str) class HTTPConnection(HTTPBaseConnection, httplib.HTTPConnection): def __init__(self, host, port=None, strict=None): httplib.HTTPConnection.__init__(self, host, port, strict) class HTTPSConnection(HTTPBaseConnection, httplib.HTTPSConnection): def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None): httplib.HTTPSConnection.__init__(self, host, port, key_file, cert_file, strict) class FileHTTPConnection(HTTPBaseConnection, httplib.HTTPConnection): def __init__(self, uds_path): httplib.HTTPConnection.__init__(self, 'localhost') self.uds_path = uds_path def connect(self): self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(self.uds_path) host, port, ssl = parse_url(url) key_file = None cert_file = None if ssl: if x509 is not None: cert_file = x509.get('cert_file') key_file = x509.get('key_file') if verify_callback is not None: try: from OpenSSL import SSL ctx = SSL.Context(SSL.SSLv3_METHOD) ctx.set_verify(SSL.VERIFY_PEER, verify_callback) # Add the key and certificate to the session if cert_file is not None and key_file is not None: ctx.use_certificate_file(cert_file) ctx.use_privatekey_file(key_file) s = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) s.connect((host, port)) s.do_handshake() s.shutdown() s.close() except socket.error, arg: raise Error("Socket error: %s" % (arg,)) except socket.sslerror, arg: raise Error("SSL error: %s" % (arg,)) numTries = 0 localAuthHeader = None tryLimit = 5 data = '\n' + data local = False if ssl: h = HTTPSConnection(host, port = port, key_file = key_file, cert_file = cert_file) else: if url.startswith('http'): h = HTTPConnection(host, port = port) else: if url.startswith('file:'): url = url[5:] try: s = os.stat(url) if S_ISSOCK(s.st_mode): h = FileHTTPConnection(url) local = True else: raise Error('Invalid URL') except OSError: raise Error('Invalid URL') locallogin = None if host in ('localhost', '127.0.0.1'): local = True if local: uid = os.getuid() try: locallogin = pwd.getpwuid(uid)[0] except KeyError: locallogin = None while numTries < tryLimit: numTries = numTries + 1 h.putrequest('POST', '/cimom') h.putheader('Content-type', 'application/xml; charset="utf-8"') h.putheader('Content-length', len(data)) if localAuthHeader is not None: h.putheader(*localAuthHeader) elif creds is not None: h.putheader('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (creds[0], creds[1])).replace('\n','')) elif locallogin is not None: h.putheader('PegasusAuthorization', 'Local "%s"' % locallogin) for hdr in headers: s = map(lambda x: string.strip(x), string.split(hdr, ":", 1)) h.putheader(urllib.quote(s[0]), urllib.quote(s[1])) try: # See RFC 2616 section 8.2.2 # An http server is allowed to send back an error (presumably # a 401), and close the connection without reading the entire # request. A server may do this to protect itself from a DoS # attack. # # If the server closes the connection during our h.send(), we # will either get a socket exception 104 (TCP RESET), or a # socket exception 32 (broken pipe). In either case, thanks # to our fixed HTTPConnection classes, we'll still be able to # retrieve the response so that we can read and respond to the # authentication challenge. h.endheaders() try: h.send(data) except socket.error, arg: if arg[0] != 104 and arg[0] != 32: raise response = h.getresponse() body = response.read() if response.status != 200: if response.status == 401: if numTries >= tryLimit: raise AuthError(response.reason) if not local: raise AuthError(response.reason) authChal = response.getheader('WWW-Authenticate', '') if 'openwbem' in response.getheader('Server', ''): if 'OWLocal' not in authChal: localAuthHeader = ('Authorization', 'OWLocal uid="%d"' % uid) continue else: try: nonceIdx = authChal.index('nonce=') nonceBegin = authChal.index('"', nonceIdx) nonceEnd = authChal.index('"', nonceBegin+1) nonce = authChal[nonceBegin+1:nonceEnd] cookieIdx = authChal.index('cookiefile=') cookieBegin = authChal.index('"', cookieIdx) cookieEnd = authChal.index('"', cookieBegin+1) cookieFile = authChal[cookieBegin+1:cookieEnd] f = open(cookieFile, 'r') cookie = f.read().strip() f.close() localAuthHeader = ('Authorization', 'OWLocal nonce="%s", cookie="%s"' % \ (nonce, cookie)) continue except: localAuthHeader = None continue elif 'Local' in authChal: try: beg = authChal.index('"') + 1 end = authChal.rindex('"') if end > beg: file = authChal[beg:end] fo = open(file, 'r') cookie = fo.read().strip() fo.close() localAuthHeader = ('PegasusAuthorization', 'Local "%s:%s:%s"' % \ (locallogin, file, cookie)) continue except ValueError: pass raise AuthError(response.reason) if response.getheader('CIMError', None) is not None and \ response.getheader('PGErrorDetail', None) is not None: import urllib raise Error( 'CIMError: %s: %s' % (response.getheader('CIMError'), urllib.unquote(response.getheader('PGErrorDetail')))) raise Error('HTTP error: %s' % response.reason) except httplib.BadStatusLine, arg: raise Error("The web server returned a bad status line: '%s'" % arg) except socket.error, arg: raise Error("Socket error: %s" % (arg,)) except socket.sslerror, arg: raise Error("SSL error: %s" % (arg,)) break return body def get_object_header(obj): """Return the HTTP header required to make a CIM operation request using the given object. Return None if the object does not need to have a header.""" # Local namespacepath if isinstance(obj, StringTypes): return 'CIMObject: %s' % obj # CIMLocalClassPath if isinstance(obj, cim_obj.CIMClassName): return 'CIMObject: %s:%s' % (obj.namespace, obj.classname) # CIMInstanceName with namespace if isinstance(obj, cim_obj.CIMInstanceName) and obj.namespace is not None: return 'CIMObject: %s' % obj raise TypeError('Don\'t know how to generate HTTP headers for %s' % obj) pywbem-0.7.0/cimxml_parse.py0000644000175000001440000006653211102360476014741 0ustar bartusers# # (C) Copyright 2006, 2007 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter from xml.dom.pulldom import * from pywbem import * class ParseError(Exception): """This exception is raised when there is a validation error detected by the parser.""" pass # # Helper functions # def get_required_attribute(node, attr): """Return an attribute by name. Throw an exception if not present.""" if not node.hasAttribute(attr): raise ParseError( 'Expecting %s attribute in element %s' % (attr, node.tagName)) return node.getAttribute(attr) def get_attribute(node, attr): """Return an attribute by name, or None if not present.""" if node.hasAttribute(attr): return node.getAttribute(attr) return None def get_end_event(parser, tagName): """Check that the next event is the end of a particular tag.""" (event, node) = parser.next() if event != END_ELEMENT or node.tagName != tagName: raise ParseError( 'Expecting %s end tag, got %s %s' % (tagName, event, node.tagName)) def is_start(event, node, tagName): """Return true if (event, node) is a start event for tagname.""" return event == START_ELEMENT and node.tagName == tagName def is_end(event, node, tagName): """Return true if (event, node) is an end event for tagname.""" return event == END_ELEMENT and node.tagName == tagName # # # # # # # # # # # # # # # # # # # # def parse_value(parser, event, node): value = '' (next_event, next_node) = parser.next() if next_event == CHARACTERS: value = next_node.nodeValue (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'VALUE'): raise ParseError('Expecting end VALUE') return value # def parse_value_array(parser, event, node): value_array = [] (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'VALUE'): value_array.append(parse_value(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_end(next_event, next_node, 'VALUE.ARRAY'): break if is_start(next_event, next_node, 'VALUE'): value_array.append(parse_value(parser, next_event, next_node)) else: raise ParseError('Expecting VALUE element') return value_array # def parse_value_reference(parser, event, node): (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'CLASSPATH'): result = parse_classpath(parser, next_event, next_node) elif is_start(next_event, next_node, 'LOCALCLASSPATH'): result = parse_localclasspath(parser, next_event, next_node) elif is_start(next_event, next_node, 'CLASSNAME'): result = parse_classname(parser, next_event, next_node) elif is_start(next_event, next_node, 'INSTANCEPATH'): result = parse_instancepath(parser, next_event, next_node) elif is_start(next_event, next_node, 'LOCALINSTANCEPATH'): result = parse_localinstancepath(parser, next_event, next_node) elif is_start(next_event, next_node, 'INSTANCENAME'): result = parse_instancename(parser, next_event, next_node) else: raise ParseError('Expecting (CLASSPATH | LOCALCLASSPATH | CLASSNAME ' '| INSTANCEPATH | LOCALINSTANCEPATH | INSTANCENAME)') get_end_event(parser, 'VALUE.REFERENCE') return result # # # # # # # # # # # def parse_namespacepath(parser, event, node): (next_event, next_node) = parser.next() if not is_start(next_event, next_node, 'HOST'): raise ParseError('Expecting HOST') host = parse_host(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_start(next_event, next_node, 'LOCALNAMESPACEPATH'): raise ParseError('Expecting LOCALNAMESPACEPATH') namespacepath = parse_localnamespacepath(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'NAMESPACEPATH'): raise ParseError('Expecting end NAMESPACEPATH') return (host, namespacepath) # def parse_localnamespacepath(parser, event, node): (next_event, next_node) = parser.next() namespaces = [] if not is_start(next_event, next_node, 'NAMESPACE'): print next_event, next_node raise ParseError('Expecting NAMESPACE') namespaces.append(parse_namespace(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_end(next_event, next_node, 'LOCALNAMESPACEPATH'): break if is_start(next_event, next_node, 'NAMESPACE'): namespaces.append(parse_namespace(parser, next_event, next_node)) else: raise ParseError('Expecting NAMESPACE') return string.join(namespaces, '/') # def parse_host(parser, event, node): host = '' (next_event, next_node) = parser.next() if next_event == CHARACTERS: host = next_node.nodeValue (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'HOST'): raise ParseError('Expecting end HOST') return host # # def parse_namespace(parser, event, node): name = get_required_attribute(node, 'NAME') (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'NAMESPACE'): raise ParseError('Expecting end NAMESPACE') return name # # # # # def parse_instancepath(parser, event, node): (next_event, next_node) = parser.next() if not is_start(next_event, next_node, 'NAMESPACEPATH'): raise ParseError('Expecting NAMESPACEPATH') host, namespacepath = parse_namespacepath(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_start(next_event, next_node, 'INSTANCENAME'): print next_event, next_node raise ParseError('Expecting INSTANCENAME') instancename = parse_instancename(parser, next_event, next_node) instancename.host = host instancename.namespace = namespacepath return instancename # def parse_localinstancepath(parser, event, node): (next_event, next_node) = parser.next() if not is_start(next_event, next_node, 'LOCALNAMESPACEPATH'): raise ParseError('Expecting LOCALNAMESPACEPATH') namespacepath = parse_localnamespacepath(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_start(next_event, next_node, 'INSTANCENAME'): raise ParseError('Expecting INSTANCENAME') instancename = parse_instancename(parser, next_event, next_node) instancename.namespace = namespacepath return instancename # # def parse_instancename(parser, event, node): classname = get_required_attribute(node, 'CLASSNAME') keybindings = [] (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'KEYBINDING'): keybindings.append(parse_keybinding(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_end(next_event, next_node, 'INSTANCENAME'): break if is_start(next_event, next_node, 'KEYBINDING'): keybindings.append( parse_keybinding(parser, next_event, next_node)) else: raise ParseError('Expecting KEYBINDING element') if is_end(next_event, next_node, 'INSTANCENAME'): pass elif is_start(next_event, next_node, 'KEYVALUE'): keybindings.append(('', parse_keyvalue(parser, next_event, next_node))) elif is_start(next_event, next_node, 'VALUE.REFERENCE'): keybindings.append( parse_value_reference(parser, next_event, next_node)) else: raise ParseError( 'Expecting KEYBINDING* | KEYVALUE? | VALUE.REFERENCE') return CIMInstanceName(classname, keybindings) # # # def parse_keybinding(parser, event, node): name = get_required_attribute(node, 'NAME') (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'KEYVALUE'): keyvalue = parse_keyvalue(parser, next_event, next_node) result = (name, keyvalue) elif is_start(next_event, next_node, 'VALUE.REFERENCE'): value_reference = parse_value_reference(parser, next_event, next_node) result = (name, value_reference) else: raise ParseError('Expecting KEYVALUE or VALUE.REFERENCE element') get_end_event(parser, 'KEYBINDING') return result # # def parse_keyvalue(parser, event, node): valuetype = get_required_attribute(node, 'VALUETYPE') type = get_attribute(node, 'TYPE') (next_event, next_node) = parser.next() if next_event != CHARACTERS: raise ParseError('Expecting character data') value = next_node.nodeValue if valuetype == 'string': pass elif valuetype == 'boolean': # CIM-XML says "These values MUST be treated as # case-insensitive" (even though the XML definition # requires them to be lowercase.) p = value.strip().lower() if p == 'true': value = True elif p == 'false': value = False else: raise ParseError('invalid boolean value "%s"' % `p`) elif valuetype == 'numeric': try: # XXX: Use TYPE attribute to create named CIM type. # if attrs(tt).has_key('TYPE'): # return cim_obj.tocimobj(attrs(tt)['TYPE'], p.strip()) # XXX: Would like to use long() here, but that tends to cause # trouble when it's written back out as '2L' value = int(value.strip(), 0) except ValueError: raise ParseError( 'invalid numeric value "%s"' % value) else: raise ParseError('Invalid VALUETYPE') get_end_event(parser, 'KEYVALUE') return value # # # # # # # def parse_instance(parser, event, node): classname = get_required_attribute(node, 'CLASSNAME') properties = [] qualifiers = [] (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append(parse_qualifier(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'PROPERTY') or \ is_start(next_event, next_node, 'PROPERTY.ARRAY') or \ is_start(next_event, next_node, 'PROPERTY.REFERENCE') or \ is_end(next_event, next_node, 'INSTANCE'): break if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append( parse_qualifier(parser, next_event, next_node)) else: raise ParseError('Expecting QUALIFIER') while 1: if is_end(next_event, next_node, 'INSTANCE'): break if is_start(next_event, next_node, 'PROPERTY'): properties.append(parse_property(parser, next_event, next_node)) elif is_start(next_event, next_node, 'PROPERTY.ARRAY'): properties.append( parse_property_array(parser, next_event, next_node)) elif is_start(next_event, next_node, 'PROPERTY.REFERENCE'): properties.append( parse_property_reference(parser, next_event, next_node)) else: raise ParseError( 'Expecting (PROPERTY | PROPERTY.ARRAY | PROPERTY.REFERENCE)') (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'INSTANCE'): raise ParseError('Expecting end INSTANCE') return CIMInstance(classname, properties = dict([(x.name, x) for x in properties]), qualifiers = dict([(x.name, x) for x in qualifiers])) # # def parse_qualifier(parser, event, node): name = get_required_attribute(node, 'NAME') type = get_required_attribute(node, 'TYPE') propagated = get_attribute(node, 'PROPAGATED') (next_event, next_node) = parser.next() if is_end(next_event, next_node, 'QUALIFIER'): return CIMQualifier(name, None, type = type) if is_start(next_event, next_node, 'VALUE'): value = parse_value(parser, next_event, next_node) elif is_start(next_event, next_node, 'VALUE.ARRAY'): value = parse_value_array(parser, next_event, next_node) else: raise ParseError('Expecting (VALUE | VALUE.ARRAY)') result = CIMQualifier(name, tocimobj(type, value)) get_end_event(parser, 'QUALIFIER') return result # # def parse_property(parser, event, node): name = get_required_attribute(node, 'NAME') type = get_required_attribute(node, 'TYPE') class_origin = get_attribute(node, 'CLASSORIGIN') propagated = get_attribute(node, 'PROPAGATED') qualifiers = [] value = None (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append(parse_qualifier(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'VALUE'): break if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append( parse_qualifier(parser, next_event, next_node)) else: raise ParseError('Expecting QUALIFIER') if is_start(next_event, next_node, 'VALUE'): value = parse_value(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'PROPERTY'): raise ParseError('Expecting end PROPERTY') return CIMProperty(name, tocimobj(type, value), type = type, class_origin = class_origin, propagated = propagated, qualifiers = dict([(x.name, x) for x in qualifiers])) # # def parse_property_array(parser, event, node): name = get_required_attribute(node, 'NAME') type = get_required_attribute(node, 'TYPE') array_size = get_attribute(node, 'ARRAYSIZE') class_origin = get_attribute(node, 'CLASSORIGIN') propagated = get_attribute(node, 'PROPAGATED') qualifiers = [] value = None (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append(parse_qualifier(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'VALUE.ARRAY'): break if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append( parse_qualifier(parser, next_event, next_node)) else: raise ParseError('Expecting QUALIFIER') if is_start(next_event, next_node, 'VALUE.ARRAY'): value = parse_value_array(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'PROPERTY.ARRAY'): raise ParseError('Expecting end PROPERTY.ARRAY') return CIMProperty(name, tocimobj(type, value), type = type, class_origin = class_origin, propagated = propagated, is_array = True, qualifiers = dict([(x.name, x) for x in qualifiers])) # # def parse_property_reference(parser, event, node): name = get_required_attribute(node, 'NAME') class_origin = get_attribute(node, 'CLASSORIGIN') propagated = get_attribute(node, 'PROPAGATED') qualifiers = [] value = None (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append(parse_qualifier(parser, next_event, next_node)) while 1: (next_event, next_node) = parser.next() if is_start(next_event, next_node, 'VALUE.REFERENCE'): break if is_start(next_event, next_node, 'QUALIFIER'): qualifiers.append( parse_qualifier(parser, next_event, next_node)) else: raise ParseError('Expecting QUALIFIER') if is_start(next_event, next_node, 'VALUE.REFERENCE'): value = parse_value_reference(parser, next_event, next_node) (next_event, next_node) = parser.next() if not is_end(next_event, next_node, 'PROPERTY.REFERENCE'): raise ParseError('Expecting end PROPERTY.REFERENCE') return CIMProperty(name, value, class_origin = class_origin, propagated = propagated, type = 'reference', qualifiers = dict([(x.name, x) for x in qualifiers])) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def make_parser(stream_or_string): """Create a xml.dom.pulldom parser.""" if type(stream_or_string) == str or type(stream_or_string) == unicode: # XXX: the pulldom.parseString() function doesn't seem to # like operating on unicode strings! return parseString(str(stream_or_string)) else: return parse(stream_or_string) def parse_any(stream_or_string): """Parse any XML string or stream.""" parser = make_parser(stream_or_string) (event, node) = parser.next() if event != START_DOCUMENT: raise ParseError('Expecting document start') (event, node) = parser.next() if event != START_ELEMENT: raise ParseError('Expecting element start') fn_name = 'parse_%s' % node.tagName.lower().replace('.', '_') fn = globals().get(fn_name) if fn is None: raise ParseError('No parser for element %s' % node.tagName) return fn(parser, event, node) # Test harness if __name__ == '__main__': import sys print parse_any(sys.stdin) pywbem-0.7.0/mofparsetab.py0000644000175000001440000020620510757070147014562 0ustar bartusers # mofparsetab.py # This file is automatically generated. Do not edit. _lr_method = 'LALR' _lr_signature = '\xaf\x88\x12 # Martin Pool # Bart Whiteley # This is meant to be safe for import *; ie the only global names # should be ones that all clients can see. import sys, string from types import StringTypes from xml.dom import minidom import cim_obj, cim_xml, cim_http, cim_types from cim_obj import CIMClassName, CIMInstanceName, CIMInstance, CIMClass from datetime import datetime, timedelta from tupletree import dom_to_tupletree, xml_to_tupletree from tupleparse import parse_cim """CIM-XML/HTTP operations. The WBEMConnection class opens a connection to a remote WBEM server. Across this you can run various CIM operations. Each method of this object corresponds fairly directly to a single CIM method call. """ DEFAULT_NAMESPACE = 'root/cimv2' # TODO: Many methods have more parameters that aren't set yet. # helper functions for validating arguments def _check_classname(val): if not isinstance(val, StringTypes): raise ValueError("string expected for classname, not %s" % `val`) class CIMError(Exception): """Raised when something bad happens. The associated value is a tuple of (error_code, description). An error code of zero indicates an XML parsing error in PyWBEM.""" class WBEMConnection(object): """Class representing a client's connection to a WBEM server. At the moment there is no persistent TCP connection; the connectedness is only conceptual. After creating a connection, various methods may be called on the object, which causes a remote call to the server. All these operations take regular Python or cim_types values for parameters, and return the same. The caller should not need to know about the XML encoding. (It should be possible to use a different transport below this layer without disturbing any clients.) The connection remembers the XML for the last request and last reply. This may be useful in debugging: if a problem occurs, you can examine the last_request and last_reply fields of the connection. These are the prettified request and response; the real request is sent without indents so as not to corrupt whitespace. The caller may also register callback functions which are passed the request before it is sent, and the reply before it is unpacked. verify_callback is used to verify the server certificate. It is passed to OpenSSL.SSL.set_verify, and is called during the SSL handshake. verify_callback should take five arguments: A Connection object, an X509 object, and three integer variables, which are in turn potential error number, error depth and return code. verify_callback should return True if verification passes and False otherwise. The value of the x509 argument is used only when the url contains 'https'. x509 must be a dictionary containing the keys 'cert_file' and 'key_file'. The value of 'cert_file' must consist of the filename of an certificate and the value of 'key_file' must consist of a filename containing the private key belonging to the public key that is part of the certificate in cert_file. """ def __init__(self, url, creds = None, default_namespace = DEFAULT_NAMESPACE, x509 = None, verify_callback = None): self.url = url self.creds = creds self.x509 = x509 self.verify_callback = verify_callback self.last_request = self.last_reply = '' self.default_namespace = default_namespace self.debug = False def __repr__(self): if self.creds is None: user = 'anonymous' else: user = 'user=%s' % `self.creds[0]` return "%s(%s, %s, namespace=%s)" % (self.__class__.__name__, `self.url`, user, `self.default_namespace`) def imethodcall(self, methodname, namespace, **params): """Make an intrinsic method call. Returns a tupletree with a IRETURNVALUE element at the root. A CIMError exception is thrown if there was an error parsing the call response, or an ERROR element was returned. The parameters are automatically converted to the right CIM_XML objects. In general clients should call one of the method-specific methods of the connection, such as EnumerateInstanceNames, etc.""" # Create HTTP headers headers = ['CIMOperation: MethodCall', 'CIMMethod: %s' % methodname, cim_http.get_object_header(namespace)] # Create parameter list plist = map(lambda x: cim_xml.IPARAMVALUE(x[0], cim_obj.tocimxml(x[1])), params.items()) # Build XML request req_xml = cim_xml.CIM( cim_xml.MESSAGE( cim_xml.SIMPLEREQ( cim_xml.IMETHODCALL( methodname, cim_xml.LOCALNAMESPACEPATH( [cim_xml.NAMESPACE(ns) for ns in string.split(namespace, '/')]), plist)), '1001', '1.0'), '2.0', '2.0') if self.debug: self.last_raw_request = req_xml.toxml() self.last_request = req_xml.toprettyxml(indent=' ') self.last_reply = None self.last_raw_reply = None # Get XML response try: resp_xml = cim_http.wbem_request(self.url, req_xml.toxml(), self.creds, headers, x509 = self.x509, verify_callback = self.verify_callback) except cim_http.AuthError: raise except cim_http.Error, arg: # Convert cim_http exceptions to CIMError exceptions raise CIMError(0, str(arg)) ## TODO: Perhaps only compute this if it's required? Should not be ## all that expensive. reply_dom = minidom.parseString(resp_xml) if self.debug: self.last_reply = reply_dom.toprettyxml(indent=' ') self.last_raw_reply = resp_xml # Parse response tt = parse_cim(dom_to_tupletree(reply_dom)) if tt[0] != 'CIM': raise CIMError(0, 'Expecting CIM element, got %s' % tt[0]) tt = tt[2] if tt[0] != 'MESSAGE': raise CIMError(0, 'Expecting MESSAGE element, got %s' % tt[0]) tt = tt[2] if len(tt) != 1 or tt[0][0] != 'SIMPLERSP': raise CIMError(0, 'Expecting one SIMPLERSP element') tt = tt[0][2] if tt[0] != 'IMETHODRESPONSE': raise CIMError( 0, 'Expecting IMETHODRESPONSE element, got %s' % tt[0]) if tt[1]['NAME'] != methodname: raise CIMError(0, 'Expecting attribute NAME=%s, got %s' % (methodname, tt[1]['NAME'])) tt = tt[2] # At this point we either have a IRETURNVALUE, ERROR element # or None if there was no child nodes of the IMETHODRESPONSE # element. if tt is None: return None if tt[0] == 'ERROR': code = int(tt[1]['CODE']) if tt[1].has_key('DESCRIPTION'): raise CIMError(code, tt[1]['DESCRIPTION']) raise CIMError(code, 'Error code %s' % tt[1]['CODE']) if tt[0] != 'IRETURNVALUE': raise CIMError(0, 'Expecting IRETURNVALUE element, got %s' % tt[0]) return tt def methodcall(self, methodname, localobject, **params): """Make an extrinsic method call. Returns a tupletree with a RETURNVALUE element at the root. A CIMError exception is thrown if there was an error parsing the call response, or an ERROR element was returned. The parameters are automatically converted to the right CIM_XML objects.""" # METHODCALL only takes a LOCALCLASSPATH or LOCALINSTANCEPATH if hasattr(localobject, 'host') and localobject.host is not None: localobject = localobject.copy() localobject.host = None # Create HTTP headers headers = ['CIMOperation: MethodCall', 'CIMMethod: %s' % methodname, cim_http.get_object_header(localobject)] # Create parameter list def paramtype(obj): """Return a string to be used as the CIMTYPE for a parameter.""" if isinstance(obj, cim_types.CIMType): return obj.cimtype elif type(obj) == bool: return 'boolean' elif isinstance(obj, StringTypes): return 'string' elif isinstance(obj, (datetime, timedelta)): return 'datetime' elif isinstance(obj, (CIMClassName, CIMInstanceName)): return 'reference' elif isinstance(obj, (CIMClass, CIMInstance)): return 'string' elif isinstance(obj, list): if obj: return paramtype(obj[0]) else: return None raise TypeError('Unsupported parameter type "%s"' % type(obj)) def paramvalue(obj): """Return a cim_xml node to be used as the value for a parameter.""" if isinstance(obj, (datetime, timedelta)): obj = cim_types.CIMDateTime(obj) if isinstance(obj, (cim_types.CIMType, bool, StringTypes)): return cim_xml.VALUE(cim_types.atomic_to_cim_xml(obj)) if isinstance(obj, (CIMClassName, CIMInstanceName)): return cim_xml.VALUE_REFERENCE(obj.tocimxml()) if isinstance(obj, (CIMClass, CIMInstance)): return cim_xml.VALUE(obj.tocimxml().toxml()) if isinstance(obj, list): if obj and isinstance(obj[0], (CIMClassName, CIMInstanceName)): return cim_xml.VALUE_REFARRAY([paramvalue(x) for x in obj]) return cim_xml.VALUE_ARRAY([paramvalue(x) for x in obj]) raise TypeError('Unsupported parameter type "%s"' % type(obj)) def is_embedded(obj): """Determine if an object requires an EmbeddedObject attribute""" if isinstance(obj,list) and obj: return is_embedded(obj[0]) elif isinstance(obj, CIMClass): return 'object' elif isinstance(obj, CIMInstance): return 'instance' return None plist = [cim_xml.PARAMVALUE(x[0], paramvalue(x[1]), paramtype(x[1]), embedded_object=is_embedded(x[1])) for x in params.items()] # Build XML request req_xml = cim_xml.CIM( cim_xml.MESSAGE( cim_xml.SIMPLEREQ( cim_xml.METHODCALL( methodname, localobject.tocimxml(), plist)), '1001', '1.0'), '2.0', '2.0') if self.debug: self.last_request = req_xml.toprettyxml(indent=' ') # Get XML response try: resp_xml = cim_http.wbem_request(self.url, req_xml.toxml(), self.creds, headers, x509 = self.x509, verify_callback = self.verify_callback) except cim_http.Error, arg: # Convert cim_http exceptions to CIMError exceptions raise CIMError(0, str(arg)) if self.debug: self.last_reply = resp_xml tt = parse_cim(xml_to_tupletree(resp_xml)) if tt[0] != 'CIM': raise CIMError(0, 'Expecting CIM element, got %s' % tt[0]) tt = tt[2] if tt[0] != 'MESSAGE': raise CIMError(0, 'Expecting MESSAGE element, got %s' % tt[0]) tt = tt[2] if len(tt) != 1 or tt[0][0] != 'SIMPLERSP': raise CIMError(0, 'Expecting one SIMPLERSP element') tt = tt[0][2] if tt[0] != 'METHODRESPONSE': raise CIMError( 0, 'Expecting METHODRESPONSE element, got %s' % tt[0]) if tt[1]['NAME'] != methodname: raise CIMError(0, 'Expecting attribute NAME=%s, got %s' % (methodname, tt[1]['NAME'])) tt = tt[2] # At this point we have an optional RETURNVALUE and zero or # more PARAMVALUE elements representing output parameters. if len(tt) > 0 and tt[0][0] == 'ERROR': code = int(tt[0][1]['CODE']) if tt[0][1].has_key('DESCRIPTION'): raise CIMError(code, tt[0][1]['DESCRIPTION']) raise CIMError(code, 'Error code %s' % tt[0][1]['CODE']) return tt # # Instance provider API # def EnumerateInstanceNames(self, ClassName, namespace = None, **params): """Enumerate instance names of a given classname. Returns a list of CIMInstanceName objects.""" if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'EnumerateInstanceNames', namespace, ClassName = CIMClassName(ClassName), **params) names = [] if result is not None: names = result[2] [setattr(n, 'namespace', namespace) for n in names] return names def EnumerateInstances(self, ClassName, namespace = None, **params): """Enumerate instances of a given classname. Returns a list of CIMInstance objects with paths.""" if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'EnumerateInstances', namespace, ClassName = CIMClassName(ClassName), **params) instances = [] if result is not None: instances = result[2] [setattr(i.path, 'namespace', namespace) for i in instances] return instances def GetInstance(self, InstanceName, **params): """Fetch an instance given by instancename. Returns a CIMInstance object.""" # Strip off host and namespace to make this a "local" object iname = InstanceName.copy() iname.host = None iname.namespace = None if InstanceName.namespace is None: namespace = self.default_namespace else: namespace = InstanceName.namespace result = self.imethodcall( 'GetInstance', namespace, InstanceName = iname, **params) instance = result[2][0] instance.path = InstanceName instance.path.namespace = namespace return instance def DeleteInstance(self, InstanceName, **params): """Delete the instance given by instancename.""" # Strip off host and namespace to make this a "local" object iname = InstanceName.copy() iname.host = None iname.namespace = None if InstanceName.namespace is None: namespace = self.default_namespace else: namespace = InstanceName.namespace self.imethodcall( 'DeleteInstance', namespace, InstanceName = iname, **params) def CreateInstance(self, NewInstance, **params): """Create an instance. Returns the name for the instance.""" # Take namespace path from object parameter if NewInstance.path is not None and \ NewInstance.path.namespace is not None: namespace = NewInstance.path.namespace else: namespace = self.default_namespace # Strip off path to avoid producing a VALUE.NAMEDINSTANCE # element instead of an INSTANCE element. instance = NewInstance.copy() instance.path = None result = self.imethodcall( 'CreateInstance', namespace, NewInstance = instance, **params) name = result[2][0] name.namespace = namespace return name def ModifyInstance(self, ModifiedInstance, **params): """Modify properties of a named instance.""" # Must pass a named CIMInstance here (i.e path attribute set) if ModifiedInstance.path is None: raise ValueError( 'ModifiedInstance parameter must have path attribute set') # Take namespace path from object parameter if ModifiedInstance.path.namespace is None: namespace = self.default_namespace else: namespace = ModifiedInstance.path.namespace instance = ModifiedInstance.copy() instance.path.namespace = None self.imethodcall( 'ModifyInstance', namespace, ModifiedInstance = instance, **params) def ExecQuery(self, QueryLanguage, Query, namespace = None): if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'ExecQuery', namespace, QueryLanguage = QueryLanguage, Query = Query) instances = [] if result is not None: instances = [tt[2] for tt in result[2]] [setattr(i.path, 'namespace', namespace) for i in instances] return instances # # Schema management API # def _map_classname_param(self, params): """Convert string ClassName parameter to a CIMClassName.""" if params.has_key('ClassName') and \ isinstance(params['ClassName'], StringTypes): params['ClassName'] = cim_obj.CIMClassName(params['ClassName']) return params def EnumerateClassNames(self, namespace = None, **params): """Return a list of CIM class names. Names are returned as strings.""" params = self._map_classname_param(params) if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'EnumerateClassNames', namespace, **params) if result is None: return [] else: return map(lambda x: x.classname, result[2]) def EnumerateClasses(self, namespace = None, **params): """Return a list of CIM class objects.""" params = self._map_classname_param(params) if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'EnumerateClasses', namespace, **params) if result is None: return [] return result[2] def GetClass(self, ClassName, namespace = None, **params): """Return a CIMClass representing the named class.""" params = self._map_classname_param(params) if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'GetClass', namespace, ClassName = CIMClassName(ClassName), **params) return result[2][0] def DeleteClass(self, ClassName, namespace = None, **params): """Delete a class by class name.""" # UNSUPPORTED (but actually works) params = self._map_classname_param(params) if namespace is None: namespace = self.default_namespace self.imethodcall( 'DeleteClass', namespace, ClassName = CIMClassName(ClassName), **params) def ModifyClass(self, ModifiedClass, namespace = None, **params): """Modify a CIM class.""" # UNSUPPORTED if namespace is None: namespace = self.default_namespace self.imethodcall( 'ModifyClass', namespace, ModifiedClass = ModifiedClass, **params) def CreateClass(self, NewClass, namespace = None, **params): """Create a CIM class.""" # UNSUPPORTED if namespace is None: namespace = self.default_namespace self.imethodcall( 'CreateClass', namespace, NewClass = NewClass, **params) # # Association provider API # def _add_objectname_param(self, params, object): """Add an object name (either a class name or an instance name) to a dictionary of parameter names.""" if isinstance(object, (CIMClassName, CIMInstanceName)): params['ObjectName'] = object.copy() params['ObjectName'].namespace = None elif isinstance(object, StringTypes): params['ObjectName'] = CIMClassName(object) else: raise ValueError('Expecting a classname, CIMClassName or ' 'CIMInstanceName object') return params def _map_association_params(self, params): """Convert various convenience parameters and types into their correct form for passing to the imethodcall() function.""" # ResultClass and Role parameters that are strings should be # mapped to CIMClassName objects. if params.has_key('ResultClass') and \ isinstance(params['ResultClass'], StringTypes): params['ResultClass'] = cim_obj.CIMClassName(params['ResultClass']) if params.has_key('AssocClass') and \ isinstance(params['AssocClass'], StringTypes): params['AssocClass'] = cim_obj.CIMClassName(params['AssocClass']) return params def Associators(self, ObjectName, **params): """Enumerate CIM classes or instances that are associated to a particular source CIM Object. Pass a keyword parameter of 'ClassName' to return associators for a CIM class, pass 'InstanceName' to return the associators for a CIM instance.""" params = self._map_association_params(params) params = self._add_objectname_param(params, ObjectName) namespace = self.default_namespace if isinstance(ObjectName, CIMInstanceName) and \ ObjectName.namespace is not None: namespace = ObjectName.namespace result = self.imethodcall( 'Associators', namespace, **params) if result is None: return [] return map(lambda x: x[2], result[2]) def AssociatorNames(self, ObjectName, **params): """Enumerate the names of CIM classes or instances names that are associated to a particular source CIM Object. Pass a keyword parameter of 'ClassName' to return associator names for a CIM class, pass 'InstanceName' to return the associator names for a CIM instance. Returns a list of CIMInstanceName objects with the host and namespace attributes set.""" params = self._map_association_params(params) params = self._add_objectname_param(params, ObjectName) namespace = self.default_namespace if isinstance(ObjectName, CIMInstanceName) and \ ObjectName.namespace is not None: namespace = ObjectName.namespace result = self.imethodcall( 'AssociatorNames', namespace, **params) if result is None: return [] return map(lambda x: x[2], result[2]) def References(self, ObjectName, **params): """Enumerate the association objects that refer to a particular target CIM class or instance. Pass a keyword parameter of 'ClassName' to return associators for a CIM class, pass 'InstanceName' to return the associators for a CIM instance.""" params = self._map_association_params(params) params = self._add_objectname_param(params, ObjectName) namespace = self.default_namespace if isinstance(ObjectName, CIMInstanceName) and \ ObjectName.namespace is not None: namespace = ObjectName.namespace result = self.imethodcall( 'References', namespace, **params) if result is None: return [] return map(lambda x: x[2], result[2]) def ReferenceNames(self, ObjectName, **params): """Enumerate the name of association objects that refer to a particular target CIM class or instance. Pass a keyword parameter of 'ClassName' to return associators for a CIM class, pass 'InstanceName' to return the associators for a CIM instance.""" params = self._map_association_params(params) params = self._add_objectname_param(params, ObjectName) namespace = self.default_namespace if isinstance(ObjectName, CIMInstanceName) and \ ObjectName.namespace is not None: namespace = ObjectName.namespace result = self.imethodcall( 'ReferenceNames', namespace, **params) if result is None: return [] return map(lambda x: x[2], result[2]) # # Method provider API # def InvokeMethod(self, MethodName, ObjectName, **params): # Convert string to CIMClassName obj = ObjectName if isinstance(obj, StringTypes): obj = CIMClassName(obj, namespace = self.default_namespace) if isinstance(obj, CIMInstanceName) and obj.namespace is None: obj = ObjectName.copy() obj.namespace = self.default_namespace # Make the method call result = self.methodcall(MethodName, obj, **params) # Convert optional RETURNVALUE into a Python object returnvalue = None if len(result) > 0 and result[0][0] == 'RETURNVALUE': returnvalue = cim_obj.tocimobj(result[0][1]['PARAMTYPE'], result[0][2]) result = result[1:] # Convert zero or more PARAMVALUE elements into dictionary output_params = {} for p in result: if p[1] == 'reference': output_params[p[0]] = p[2] else: output_params[p[0]] = cim_obj.tocimobj(p[1], p[2]) return returnvalue, output_params # # Qualifiers API # def EnumerateQualifiers(self, namespace = None, **params): """Enumerate qualifier declarations. Returns a list of CIMQualifier objects.""" if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'EnumerateQualifiers', namespace, **params) qualifiers = [] if result is not None: names = result[2] else: names = [] return names def GetQualifier(self, QualifierName, namespace = None, **params): """Retrieve a qualifier by name. Returns a CIMQualifier object.""" if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'GetQualifier', namespace, QualifierName = QualifierName, **params) if result is not None: names = result[2][0] return names def SetQualifier(self, QualifierDeclaration, namespace = None, **params): """Set a qualifier.""" if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'SetQualifier', namespace, QualifierDeclaration = QualifierDeclaration, **params) def DeleteQualifier(self, QualifierName, namespace = None, **params): """Delete a qualifier by name.""" if namespace is None: namespace = self.default_namespace result = self.imethodcall( 'DeleteQualifier', namespace, QualifierName = QualifierName, **params) def is_subclass(ch, ns, super, sub): """Determine if one class is a subclass of another Keyword Arguments: ch -- A CIMOMHandle. Either a pycimmb.CIMOMHandle or a pywbem.WBEMConnection. ns -- Namespace. super -- A string containing the super class name. sub -- The subclass. This can either be a string or a pywbem.CIMClass. """ lsuper = super.lower() if isinstance(sub, CIMClass): subname = sub.classname subclass = sub else: subname = sub subclass = None if subname.lower() == lsuper: return True if subclass is None: subclass = ch.GetClass(subname, ns, LocalOnly=True, IncludeQualifiers=False, PropertyList=[], IncludeClassOrigin=False) while subclass.superclass is not None: if subclass.superclass.lower() == lsuper: return True subclass = ch.GetClass(subclass.superclass, ns, LocalOnly=True, IncludeQualifiers=False, PropertyList=[], IncludeClassOrigin=False) return False def PegasusUDSConnection(creds = None, **kwargs): return WBEMConnection('/var/run/tog-pegasus/cimxml.socket', creds, **kwargs) def SFCBUDSConnection(creds = None, **kwargs): return WBEMConnection('/tmp/sfcbHttpSocket', creds, **kwargs) def OpenWBEMUDSConnection(creds = None, **kwargs): return WBEMConnection('/tmp/OW@LCL@APIIPC_72859_Xq47Bf_P9r761-5_J-7_Q', creds, **kwargs) pywbem-0.7.0/cim_xml.py0000644000175000001440000012650711102360476013705 0ustar bartusers# # (C) Copyright 2003-2006 Hewlett-Packard Development Company, L.P. # (C) Copyright 2006-2007 Novell, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter # Bart Whiteley """ Functions to create XML documens and elements conforming to the DMTF standard DSP0201, Representation of CIM in XML, v2.2. http://www.dmtf.org/standards/wbem/DSP201.html http://www.dmtf.org/standards/published_documents/DSP201.pdf Elements generated by this module should conform to version 2.2 of the DTD: http://www.dmtf.org/standards/wbem/CIM_DTD_V22.dtd There should be one class for each element described in the DTD. The constructors take builtin Python types, or other cim_xml classes where child elements are required. Every class is a subclass of the Element class and so shares the same attributes and methods, and can be used with the built-in Python XML handling routines. In particular you can call the toxml() and toprettyxml() methods to generate XML. Note that converting using toprettyxml() inserts whitespace which may corrupt the data in the XML (!!) so you should only do this when displaying to humans who can ignore it, and never for computers. XML always passes through all non-markup whitespace. """ import sys, xml.dom.minidom from xml.dom.minidom import Document, Element def Text(data): """Grr. The API for the minidom text node function has changed in Python 2.3. This function allows the code to run under older versions of the intepreter.""" if sys.version_info[0] == 2 and sys.version_info[1] >= 3: t = xml.dom.minidom.Text() t.data = data return t return xml.dom.minidom.Text(data) class CIMElement(Element): """A base class that has a few bonus helper methods.""" def setName(self, name): """Set the NAME attribute of the element.""" self.setAttribute('NAME', name) def setOptionalAttribute(self, name, value): """Set an attribute if the value parameter is not None.""" if value is not None: self.setAttribute(name, value) def appendOptionalChild(self, child): """Append a child element which can be None.""" if child is not None: self.appendChild(child) def appendChildren(self, children): """Append a list or tuple of children.""" [self.appendChild(child) for child in children] # Root element class CIM(CIMElement): """ The CIM element is the root element of every XML Document that is valid with respect to this schema. Each document takes one of two forms; it either contains a single MESSAGE element defining a CIM message (to be used in the HTTP mapping), or it contains a DECLARATION element used to declare a set of CIM objects. """ def __init__(self, data, cim_version, dtd_version): Element.__init__(self, 'CIM') self.setAttribute('CIMVERSION', cim_version) self.setAttribute('DTDVERSION', dtd_version) self.appendChild(data) # Object declaration elements class DECLARATION(CIMElement): """ The DECLARATION element defines a set of one or more declarations of CIM objects. These are partitioned into logical declaration subsets. """ def __init__(self, data): Element.__init__(self, 'DECLARATION') self.appendChildren(data) class DECLGROUP(CIMElement): """ The DECLGROUP element defines a logical set of CIM Class, Instance and Qualifier declarations. It MAY optionally include a NAMESPACEPATH or LOCALNAMESPACEPATH element which, if present, defines the common namespace in which all objects within the group are declared. """ def __init__(self, data): Element.__init__(self, 'DECLGROUP') self.appendChildren(data) class DECLGROUP_WITHNAME(CIMElement): """ The DECLGROUP.WITHNAME element defines a logical set of CIM Class, Instance and Qualifier declarations. It MAY optionally include a NAMESPACEPATH or LOCALNAMESPACEPATH element which, if present, defines the common namespace in which all objects within the group are declared. """ def __init__(self, data): Element.__init__(self, 'DECLGROUP.WITHNAME') self.appendChildren(data) class DECLGROUP_WITHPATH(CIMElement): """ The DECLGROUP.WITHPATH element defines a logical set of CIM Class and Instance declarations. Each object is declared with its own independent naming and location information. """ def __init__(self, data): Element.__init__(self, 'DECLGROUP.WITHPATH') self.appendChildren(data) class QUALIFIER_DECLARATION(CIMElement): """ The QUALIFIER.DECLARATION element defines a single CIM Qualifier declaration. """ def __init__(self, name, type, value, is_array = None, array_size = None, qualifier_scopes = {}, overridable = None, tosubclass = None, toinstance = None, translatable = None): Element.__init__(self, 'QUALIFIER.DECLARATION') self.setName(name) self.setAttribute('TYPE', type) if is_array is not None: self.setOptionalAttribute('ISARRAY', str(is_array).lower()) if array_size is not None: self.setAttribute('ARRAYSIZE', str(array_size)) if overridable is not None: self.setAttribute('OVERRIDABLE', str(overridable).lower()) if tosubclass is not None: self.setAttribute('TOSUBCLASS', str(tosubclass).lower()) if toinstance is not None: self.setAttribute('TOINSTANCE', str(toinstance).lower()) if translatable is not None: self.setAttribute('TRANSLATABLE', str(translatable).lower()) if qualifier_scopes: scope = SCOPE(qualifier_scopes) self.appendOptionalChild(scope) if value is not None: if is_array: xval = VALUE_ARRAY(value) else: xval = VALUE(value) self.appendOptionalChild(xval) class SCOPE(CIMElement): """ The SCOPE element defines the scope of a QUALIFIER.DECLARATION in the case that there are restrictions on the scope of the Qualifier declaration. """ def __init__(self, scopes={}): Element.__init__(self, 'SCOPE') if 'any' in scopes and scopes['any']: scopes = {'CLASS': True, 'ASSOCIATION': True, 'REFERENCE': True, 'PROPERTY': True, 'METHOD': True, 'PARAMETER': True, 'INDICATION': True} for k, v in scopes.items(): self.setOptionalAttribute(k, str(v).lower()) # Object value elements class VALUE(CIMElement): """ The VALUE element is used to define a single (non-array and non-reference) CIM Property value, CIM Qualifier value, or a CIM Method Parameter value. """ def __init__(self, pcdata): Element.__init__(self, 'VALUE') if pcdata is not None: self.appendChild(Text(pcdata)) class VALUE_ARRAY(CIMElement): """ The VALUE.ARRAY element is used to represent the value of a CIM Property or Qualifier that has an array type. """ def __init__(self, values): Element.__init__(self, 'VALUE.ARRAY') self.appendChildren(values) class VALUE_REFERENCE(CIMElement): """ The VALUE.REFERENCE element is used to define a single CIM reference Property value. """ def __init__(self, data): Element.__init__(self, 'VALUE.REFERENCE') self.appendChild(data) class VALUE_REFARRAY(CIMElement): """ The VALUE.REFARRAY element is used to represent the value of an array of CIM references. """ def __init__(self, data): Element.__init__(self, 'VALUE.REFARRAY') self.appendChildren(data) class VALUE_OBJECT(CIMElement): """ The VALUE.OBJECT element is used to define a value which is comprised of a single CIM Class or Instance definition. """ def __init__(self, data): Element.__init__(self, 'VALUE.OBJECT') self.appendChild(data) class VALUE_NAMEDINSTANCE(CIMElement): """ The VALUE.NAMEDINSTANCE element is used to define a value which is comprised of a single named CIM Instance definition. """ def __init__(self, instancename, instance): Element.__init__(self, 'VALUE.NAMEDINSTANCE') self.appendChild(instancename) self.appendChild(instance) class VALUE_NAMEDOBJECT(CIMElement): """ The VALUE.NAMEDOBJECT element is used to define a value which is comprised of a single named CIM Class or Instance definition. """ def __init__(self, data): Element.__init__(self, 'VALUE.NAMEDOBJECT') if type(data) == tuple or type(data) == list: self.appendChildren(data) else: self.appendChild(data) class VALUE_OBJECTWITHLOCALPATH(CIMElement): """ The VALUE.OBJECTWITHLOCALPATH element is used to define a value which is comprised of a single CIM Object (Class or Instance) definition with additional information that defines the local path to that Object. """ def __init__(self, data1, data2): Element.__init__(self, 'VALUE.OBJECTWITHLOCALPATH') self.appendChild(data1) self.appendChild(data2) class VALUE_OBJECTWITHPATH(CIMElement): """ The VALUE.OBJECTWITHPATH element is used to define a value which is comprised of a single CIM Object (Class or Instance) definition with additional information that defines the absolute path to that Object. """ def __init__(self, data1, data2): Element.__init__(self, 'VALUE.OBJECTWITHPATH') self.appendChild(data1) self.appendChild(data2) class VALUE_NULL(CIMElement): """ The VALUE.NULL element is used to represent a TABLECELL that has no assigned value. """ def __init__(self): Element.__init__(self, 'VALUE.NULL') # Object naming and location elements class NAMESPACEPATH(CIMElement): """ The NAMESPACEPATH element is used to define a Namespace Path. It consists of a HOST element and a LOCALNAMESPACE element. """ def __init__(self, host, localnamespacepath): Element.__init__(self, 'NAMESPACEPATH') self.appendChild(host) self.appendChild(localnamespacepath) class LOCALNAMESPACEPATH(CIMElement): """ The LOCALNAMESPACEPATH element is used to define a local Namespace path (one without a Host component). It consists of one or more NAMESPACE elements (one for each namespace in the path). """ def __init__(self, namespaces): Element.__init__(self, 'LOCALNAMESPACEPATH') self.appendChildren(namespaces) class HOST(CIMElement): """ The HOST element is used to define a single Host. The element content MUST specify a legal value for a hostname in accordance with the CIM specification. """ def __init__(self, pcdata): Element.__init__(self, 'HOST') self.appendChild(Text(pcdata)) class NAMESPACE(CIMElement): """ The NAMESPACE element is used to define a single Namespace component of a Namespace path. """ def __init__(self, name): Element.__init__(self, 'NAMESPACE') self.setName(name) class CLASSPATH(CIMElement): """ The CLASSPATH element defines the absolute path to a CIM Class. It is formed from a namespace path and Class name. """ def __init__(self, namespacepath, classname): Element.__init__(self, 'CLASSPATH') self.appendChild(namespacepath) self.appendChild(classname) class LOCALCLASSPATH(CIMElement): """ The LOCALCLASSPATH element defines the a local path to a CIM Class. It is formed from a local namespace path and Class name. """ def __init__(self, localnamespacepath, classname): Element.__init__(self, 'LOCALCLASSPATH') self.appendChild(localnamespacepath) self.appendChild(classname) class CLASSNAME(CIMElement): """ The CLASSNAME element defines the qualifying name of a CIM Class. """ def __init__(self, classname): Element.__init__(self, 'CLASSNAME') self.setName(classname) class INSTANCEPATH(CIMElement): """ The INSTANCEPATH element defines the absolute path to a CIM Instance. It is comprised of a Namespace path and an Instance Name (model path). """ def __init__(self, namespacepath, instancename): Element.__init__(self, 'INSTANCEPATH') self.appendChild(namespacepath) self.appendChild(instancename) class LOCALINSTANCEPATH(CIMElement): """ The LOCALINSTANCEPATH element defines the local path to a CIM Instance. It is comprised of a local Namespace path and an Instance Name (model path). """ def __init__(self, localpath, instancename): Element.__init__(self, 'LOCALINSTANCEPATH') self.appendChild(localpath) self.appendChild(instancename) class INSTANCENAME(CIMElement): """ The INSTANCENAME element defines the location of a CIM Instance within a Namespace (it is referred to in the CIM Specification as a Model Path). It is comprised of a class name and a key binding information. If the Class has a single key property, then a single KEYVALUE or VALUE.REFERENCE subelement may be used to describe the (necessarily) unique key value without a key name. Alternatively a single KEYBINDING subelement may be used instead. If the Class has more than one key property, then a KEYBINDING subelement MUST appear for each key. If there are no key-bindings specified, the instance is assumed to be a singleton instance of a keyless Class. """ def __init__(self, classname, data): Element.__init__(self, 'INSTANCENAME') self.setAttribute('CLASSNAME', classname) if data is not None: if type(data) == list: self.appendChildren(data) else: self.appendChild(data) class OBJECTPATH(CIMElement): """ The OBJECTPATH element is used to define a full path to a single CIM Object (Class or Instance). """ def __init__(self, data): Element.__init__(self, 'OBJECTPATH') self.appendChild(data) class KEYBINDING(CIMElement): """ The KEYBINDING element defines a single key property value binding. """ def __init__(self, name, data): Element.__init__(self, 'KEYBINDING') self.setName(name) self.appendChild(data) class KEYVALUE(CIMElement): """ The KEYVALUE element defines a single property key value when the key property is a non-reference type. """ def __init__(self, data, value_type = None, cim_type = None): Element.__init__(self, 'KEYVALUE') if value_type is None: self.setAttribute('VALUETYPE', 'string') else: self.setAttribute('VALUETYPE', value_type) self.setOptionalAttribute('TYPE', cim_type) if data != None: self.appendChild(Text(data)) # Object definition elements class CLASS(CIMElement): """ The CLASS element defines a single CIM Class. """ def __init__(self, classname, properties = [], methods = [], qualifiers = [], superclass = None): Element.__init__(self, 'CLASS') self.setName(classname) self.setOptionalAttribute('SUPERCLASS', superclass) self.appendChildren(qualifiers + properties + methods) class INSTANCE(CIMElement): """ The INSTANCE element defines a single CIM Instance of a CIM Class. """ def __init__(self, classname, properties = [], qualifiers = [], xml_lang = None): Element.__init__(self, 'INSTANCE') self.setAttribute('CLASSNAME', classname) self.setOptionalAttribute('xml:lang', xml_lang) self.appendChildren(qualifiers + properties) class QUALIFIER(CIMElement): """ The QUALIFIER element defines a single CIM Qualifier. If the Qualifier has a non-array type, it contains a single VALUE element representing the value of the Qualifier. If the Qualifier has an array type, it contains a single VALUE.ARRAY element to represent the value. If the Qualifier has no assigned value then the VALUE element MUST be absent. """ def __init__(self, name, type, value = None, propagated = None, overridable = None, tosubclass = None, toinstance = None, translatable = None, xml_lang = None): Element.__init__(self, 'QUALIFIER') self.setName(name) self.setAttribute('TYPE', type) if propagated is not None: self.setAttribute('PROPAGATED', str(propagated).lower()) if overridable is not None: self.setAttribute('OVERRIDABLE', str(overridable).lower()) if tosubclass is not None: self.setAttribute('TOSUBCLASS', str(tosubclass).lower()) if toinstance is not None: self.setAttribute('TOINSTANCE', str(toinstance).lower()) if translatable is not None: self.setAttribute('TRANSLATABLE', str(translatable).lower()) self.setOptionalAttribute('xml:lang', xml_lang) self.appendOptionalChild(value) class PROPERTY(CIMElement): """ The PROPERTY element defines a single (non-array) CIM Property that is not a reference. It contains a single VALUE element representing the value of the Property. If the Property has no assigned value then the VALUE element MUST be absent. CIM Reference Properties are described using the PROPERTY.REFERENCE element. """ def __init__(self, name, type, value = None, class_origin = None, propagated = None, qualifiers = [], xml_lang = None, embedded_object = None): Element.__init__(self, 'PROPERTY') self.setName(name) self.setAttribute('TYPE', type) self.setOptionalAttribute('CLASSORIGIN', class_origin) if propagated is not None: self.setAttribute('PROPAGATED', str(propagated).lower()) self.setOptionalAttribute('xml:lang', xml_lang) self.setOptionalAttribute('EmbeddedObject', embedded_object) self.appendChildren(qualifiers) self.appendOptionalChild(value) class PROPERTY_ARRAY(CIMElement): """ The PROPERTY.ARRAY element defines a single CIM Property with an array type. It contains a single VALUE.ARRAY element representing the value of the Property. If the Property has no assigned value then the VALUE.ARRAY element MUST be absent. There is no element to model a Property that contains an array of references as this is not a valid Property type according to CIM. """ def __init__(self, name, type, value_array = None, array_size = None, class_origin = None, propagated = None, qualifiers = [], xml_lang = None, embedded_object = None): Element.__init__(self, 'PROPERTY.ARRAY') self.setName(name) self.setAttribute('TYPE', type) if array_size is not None: self.setAttribute('ARRAYSIZE', str(array_size)) self.setOptionalAttribute('CLASSORIGIN', class_origin) self.setOptionalAttribute('EmbeddedObject', embedded_object) if propagated is not None: self.setAttribute('PROPAGATED', str(propagated).lower()) self.setOptionalAttribute('xml:lang', xml_lang) self.appendChildren(qualifiers) self.appendOptionalChild(value_array) class PROPERTY_REFERENCE(CIMElement): """ The PROPERTY.REFERENCE element models a single CIM Property with reference semantics. In future the features of XML Linking may be used to identify linking elements within the XML Document; as XML Linking is currently only at Working Draft status no explicit dependencies have been made at this point. """ def __init__(self, name, value_reference = None, reference_class = None, class_origin = None, propagated = None, qualifiers = []): Element.__init__(self, 'PROPERTY.REFERENCE') self.setName(name) self.setOptionalAttribute('REFERENCECLASS', reference_class) self.setOptionalAttribute('CLASSORIGIN', class_origin) if propagated is not None: self.setAttribute('PROPAGATED', str(propagated).lower()); self.appendChildren(qualifiers) self.appendOptionalChild(value_reference) class METHOD(CIMElement): """ The METHOD element defines a single CIM Method. It may have Qualifiers, and zero or more parameters. The order of the PARAMETER, PARAMETER.REFERENCE, PARAMETER.ARRAY and PARAMETER.REFARRAY subelements is not significant. """ def __init__(self, name, parameters = [], return_type = None, class_origin = None, propagated = None, qualifiers = []): Element.__init__(self, 'METHOD') self.setName(name) self.setOptionalAttribute('TYPE', return_type) self.setOptionalAttribute('CLASSORIGIN', class_origin) if propagated is not None: self.setAttribute('PROPAGATED', str(propagated).lower()) self.appendChildren(qualifiers + parameters) class PARAMETER(CIMElement): """ The PARAMETER element defines a single (non-array, non-reference) Parameter to a CIM Method. The parameter MAY have zero or more Qualifiers. """ def __init__(self, name, type, qualifiers = []): Element.__init__(self, 'PARAMETER') self.setName(name) self.setAttribute('TYPE', type) self.appendChildren(qualifiers) class PARAMETER_REFERENCE(CIMElement): """ The PARAMETER.REFERENCE element defines a single reference Parameter to a CIM Method. The parameter MAY have zero or more Qualifiers. """ def __init__(self, name, reference_class = None, qualifiers = []): Element.__init__(self, 'PARAMETER.REFERENCE') self.setName(name) self.setOptionalAttribute('REFERENCECLASS', reference_class) self.appendChildren(qualifiers) class PARAMETER_ARRAY(CIMElement): """ The PARAMETER.ARRAY element defines a single Parameter to a CIM Method that has an array type. The parameter MAY have zero or more Qualifiers. """ def __init__(self, name, type, array_size = None, qualifiers = []): Element.__init__(self, 'PARAMETER.ARRAY') self.setName(name) self.setAttribute('TYPE', type) if array_size is not None: self.setAttribute('ARRAYSIZE', str(array_size)) self.appendChildren(qualifiers) class PARAMETER_REFARRAY(CIMElement): """ The PARAMETER.REFARRAY element defines a single Parameter to a CIM Method that has an array of references type. The parameter MAY have zero or more Qualifiers. """ def __init__(self, name, reference_class = None, array_size = None, qualifiers = []): Element.__init__(self, 'PARAMETER.REFARRAY') self.setName(name) self.setOptionalAttribute('REFERENCECLASS', reference_class) if array_size is not None: self.setAttribute('ARRAYSIZE', str(array_size)) self.appendChildren(qualifiers) class TABLECELL_DECLARATION(CIMElement): """ The TABLECELL.DECLARATION element describes a TABLECELL that is not a reference or an array of references. """ class TABLECELL_REFERENCE(CIMElement): """ The TABLECELL.REFERENCE element defines a TABLECELL that contains reference or reference array values. """ class TABLEROW_DECLARATION(CIMElement): """ The TABLEROW.DECLARATION element contains a definition for each TABLECELL in the TABLEROW. """ class TABLE(CIMElement): """ The TABLE element defines the result of a CIM Query. A TABLE element consists of a TABLEROW.DECLARATION followed by 0 or more rows. """ class TABLEROW(CIMElement): """ The TABLEROW element defines the values for a single row of a table. A value for each cell of the row MUST be specified. If a value has no assigned value, the VALUE.NULL element MUST be used. """ # Message elements class MESSAGE(CIMElement): """ The MESSAGE element models a single CIM message. This element is used as the basis for CIM Operation Messages and CIM Export Messages. """ def __init__(self, data, message_id, protocol_version): Element.__init__(self, 'MESSAGE') self.setAttribute('ID', message_id) self.setAttribute('PROTOCOLVERSION', protocol_version) self.appendChild(data) class MULTIREQ(CIMElement): """ The MULTIREQ element defines a Multiple CIM Operation request. It contains two or more subelements defining the SIMPLEREQ elements that make up this multiple request. """ def __init__(self, data): Element.__init__(self, 'MULTIREQ') self.appendChildren(data) class MULTIEXPREQ(CIMElement): """ The MULTIEXPREQ element defines a Multiple CIM Export request. It contains two or more subelements defining the SIMPLEEXPREQ elements that make up this multiple request. """ def __init__(self, data): Element.__init__(self, 'MULTIEXPREQ') self.appendChildren(data) class SIMPLEREQ(CIMElement): """ The SIMPLEREQ element defines a Simple CIM Operation request. It contains either a METHODCALL (extrinsic method) element or an IMETHODCALL (intrinsic method) element. """ def __init__(self, data): Element.__init__(self, 'SIMPLEREQ') self.appendChild(data) class SIMPLEEXPREQ(CIMElement): """ The SIMPLEEXPREQ element defines a Simple CIM Export request. It contains an EXPMETHODCALL (export method). """ def __init__(self, data): Element.__init__(self, 'SIMPLEEXPREQ') self.appendChild(data) class IMETHODCALL(CIMElement): """ The IMETHODCALL element defines a single intrinsic method invocation. It specifies the target local namespace, followed by zero or more IPARAMVALUE subelements as the parameter values to be passed to the method. If the RESPONSEDESTINATION element is specified, the intrinsic method call MUST be interpreted as an asynchronous method call. """ def __init__(self, name, localnamespacepath, iparamvalues = [], responsedestination = None): Element.__init__(self, 'IMETHODCALL') self.setName(name) self.appendChild(localnamespacepath) self.appendChildren(iparamvalues) self.appendOptionalChild(responsedestination) class METHODCALL(CIMElement): """ The METHODCALL element defines a single method invocation on a Class or Instance. It specifies the local path of the target Class or Instance, followed by zero or more PARAMVALUE subelements as the parameter values to be passed to the method. If the RESPONSEDESTINATION element is specified, the method call MUST be interpreted as an asynchronous method call. """ def __init__(self, name, localpath, paramvalues = [], responsedestination = None): Element.__init__(self, 'METHODCALL') self.setName(name) self.appendChild(localpath) self.appendChildren(paramvalues) self.appendOptionalChild(responsedestination) class EXPMETHODCALL(CIMElement): """ The EXPMETHODCALL element defines a single export method invocation. It specifies zero or more subelements as the parameter values to be passed to the method. """ def __init__(self, name, params = []): Element.__init__(self, 'EXPMETHODCALL') self.setName(name) self.appendChildren(params) class PARAMVALUE(CIMElement): """ The PARAMVALUE element defines a single extrinsic method named parameter value. If no subelement is present this indicates that no value has been supplied for this parameter. """ def __init__(self, name, data = None, paramtype = None, embedded_object = None): Element.__init__(self, 'PARAMVALUE') self.setName(name) self.setOptionalAttribute('PARAMTYPE', paramtype) self.setOptionalAttribute('EmbeddedObject', embedded_object) self.appendOptionalChild(data) class IPARAMVALUE(CIMElement): """ The IPARAMVALUE element defines a single intrinsic method named parameter value. If no subelement is present this indicates that no value has been supplied for this parameter. """ def __init__(self, name, data = None): Element.__init__(self, 'IPARAMVALUE') self.setName(name) self.appendOptionalChild(data) class EXPPARAMVALUE(CIMElement): """ The EXPPARAMVALUE element defines a single export method named parameter value. If no subelement is present this indicates that no value has been supplied for this parameter. """ def __init__(self, name, data = None, param_type = None): Element.__init__(self, 'EXPPARAMVALUE') self.setName(name) self.setOptionalAttribute('PARAMTYPE', param_type) self.appendOptionalChild(data) class MULTIRSP(CIMElement): """ The MULTIRSP element defines a Multiple CIM Operation response. It contains two or more subelements defining the SIMPLERSP elements that make up this multiple response. """ def __init__(self, data): Element.__init__(self, 'MULTIRSP') self.appendChildren(data) class MULTIEXPRSP(CIMElement): """ The MULTIEXPRSP element defines a Multiple CIM Export response. It contains two or more subelements defining the SIMPLEEXPRSP elements that make up this multiple response. """ def __init__(self, data): Element.__init__(self, 'MULTIEXPRSP') self.appendChildren(data) class SIMPLERSP(CIMElement): """ The SIMPLERSP element defines a Simple CIM Operation response. It contains either a METHODRESPONSE (for extrinsic methods), IMETHODRESPONSE (for intrinsic methods) or a SIMPLEREQACK subelement. """ def __init__(self, data): Element.__init__(self, 'SIMPLERSP') self.appendChild(data) class SIMPLEEXPRSP(CIMElement): """ The SIMPLEEXPRSP element defines a Simple CIM Export response. It contains either a EXPMETHODRESPONSE (for export methods) subelement. """ def __init__(self, data): Element.__init__(self, 'SIMPLEEXPRSP') self.appendChild(data) class METHODRESPONSE(CIMElement): """ The METHODRESPONSE defines the response to a single CIM extrinsic method invocation. It contains either an ERROR subelement (to report a fundamental error which prevented the method from executing), or a combination of an optional return value and zero or more out parameter values. """ def __init__(self, name, data = None): Element.__init__(self, 'METHODRESPONSE') self.setName(name) if data is not None: if type(data) == tuple or type(data) == list: self.appendChildren(data) else: self.appendChild(data) class EXPMETHODRESPONSE(CIMElement): """ The EXPMETHODRESPONSE defines the response to a single export method invocation. It contains either an ERROR subelement (to report a fundamental error which prevented the method from executing), or an optional return value. """ def __init__(self, name, data = None): Element.__init__(self, 'EXPMETHODRESPONSE') self.setName(name) self.appendOptionalChild(data) class IMETHODRESPONSE(CIMElement): """ The IMETHODRESPONSE defines the response to a single intrinsic CIM method invocation. It contains either an ERROR subelement (to report a fundamental error which prevented the method from executing), or an optional return value. """ def __init__(self, name, data = None): Element.__init__(self, 'IMETHODRESPONSE') self.setName(name) self.appendOptionalChild(data) class ERROR(CIMElement): """ The ERROR element is used to define a fundamental error which prevented a method from executing normally. It consists of a status code, an optional description and zero or more instances containing detailed information about the error. """ def __init__(self, code, description = None, instances = []): Element.__init__(self, 'ERROR') self.setAttribute('CODE', code) self.setOptionalAttribute('DESCRIPTION', description) self.appendChildren(instances) class RETURNVALUE(CIMElement): """ The RETURNVALUE element specifies the value returned from an extrinsic method call. """ def __init__(self, data, param_type = None): Element.__init__(self, 'RETURNVALUE') self.setOptionalAttribute('PARAMTYPE', param_type) self.appendChild(data) class IRETURNVALUE(CIMElement): """ The IRETURNVALUE element specifies the value returned from an intrinsic method call. """ def __init__(self, data): Element.__init__(self, 'IRETURNVALUE') self.appendOptionalChild(data) class RESPONSEDESTINATION(CIMElement): """ The RESPONSEDESTINATION element contains an instance that describes the desired destination for the response. """ def __init__(self, data): Element.__init__(self, 'RESPONSEDESTINATON') self.appendChild(data); class SIMPLEREQACK(CIMElement): """ The SIMPLEREQACK defines the acknowledgement response to a Simple CIM Operation asynchronous request. The ERROR subelement is used to report a fundamental error which prevented the asynchronous request from being initiated. """ def __init__(self, instanceid, data): Element.__init__(self, 'SIMPLEREQACK') self.setOptionalAttribute('INSTANCEID', instanceid) self.appendOptionalChild(data) pywbem-0.7.0/cim_types.py0000644000175000001440000001742211104136174014242 0ustar bartusers# # (C) Copyright 2003, 2004 Hewlett-Packard Development Company, L.P. # (C) Copyright 2006, 2007 Novell, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Tim Potter # Author: Bart Whiteley """ Subclasses of builtin Python types to remember CIM types. This is necessary as we need to remember whether an integer property is a uint8, uint16, uint32 etc, while still being able to treat it as a integer. """ from datetime import tzinfo, datetime, timedelta import re class MinutesFromUTC(tzinfo): """Fixed offset in minutes from UTC.""" def __init__(self, offset): self.__offset = timedelta(minutes = offset) def utcoffset(self, dt): return self.__offset def dst(self, dt): return timedelta(0) class CIMType(object): """Base type for all CIM types.""" class CIMDateTime(CIMType) : """A CIM DateTime.""" def __init__(self, dtarg): """Construct a new CIMDateTime Arguments: dtarg -- Can be a string in CIM datetime format, a datetime.datetime, or a datetime.timedelta. """ self.cimtype = 'datetime' self.__timedelta = None self.__datetime = None if isinstance(dtarg, basestring): date_pattern = re.compile(r'^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d{6})([+|-])(\d{3})') s = date_pattern.search(dtarg) if s is not None: g = s.groups() offset = int(g[8]) if g[7] == '-': offset = -offset self.__datetime = datetime(int(g[0]), int(g[1]), int(g[2]), int(g[3]), int(g[4]), int(g[5]), int(g[6]), MinutesFromUTC(offset)) else: tv_pattern = re.compile(r'^(\d{8})(\d{2})(\d{2})(\d{2})\.(\d{6})(:)(000)') s = tv_pattern.search(dtarg) if s is None: raise ValueError('Invalid Datetime format "%s"' % dtarg) else: g = s.groups() self.__timedelta = timedelta(days=int(g[0]), hours=int(g[1]), minutes=int(g[2]), seconds=int(g[3]), microseconds=int(g[4])) elif isinstance(dtarg, datetime): self.__datetime = dtarg; elif isinstance(dtarg, timedelta): self.__timedelta = dtarg elif isinstance(dtarg, CIMDateTime): self.__datetime = dtarg.__datetime self.__timedelta = dtarg.__timedelta else: raise ValueError('Expected datetime, timedelta, or string') @property def minutes_from_utc(self): """Return the timezone as +/- minutes from UTC""" offset = 0 if self.__datetime is not None and \ self.__datetime.utcoffset() is not None: offset = self.__datetime.utcoffset().seconds / 60 if self.__datetime.utcoffset().days == -1: offset = -(60*24 - offset) return offset @property def datetime(self): return self.__datetime @property def timedelta(self): return self.__timedelta @property def is_interval(self): return self.__timedelta is not None @staticmethod def get_local_utcoffset(): """Return minutes +/- UTC for the local timezone""" utc = datetime.utcnow() local = datetime.now() if local < utc: return - int(float((utc - local).seconds) / 60 + .5) else: return int(float((local - utc).seconds) / 60 + .5) @classmethod def now(cls, tzi=None): if tzi is None: tzi = MinutesFromUTC(cls.get_local_utcoffset()) return cls(datetime.now(tzi)) @classmethod def fromtimestamp(cls, ts, tzi=None): if tzi is None: tzi = MinutesFromUTC(cls.get_local_utcoffset()) return cls(datetime.fromtimestamp(ts, tzi)) def __str__ (self): if self.is_interval: hour = self.__timedelta.seconds / 3600 minute = (self.__timedelta.seconds - hour * 3600) / 60 second = self.__timedelta.seconds - hour * 3600 - minute * 60 return '%08d%02d%02d%02d.%06d:000' % \ (self.__timedelta.days, hour, minute, second, self.__timedelta.microseconds) else: offset = self.minutes_from_utc sign = '+' if offset < 0: sign = '-' offset = -offset return '%d%02d%02d%02d%02d%02d.%06d%s%03d' % \ (self.__datetime.year, self.__datetime.month, self.__datetime.day, self.__datetime.hour, self.__datetime.minute, self.__datetime.second, self.__datetime.microsecond, sign, offset) def __repr__ (self): return '%s(%s)'%(self.__class__.__name__, `str(self)`) def __getstate__(self): return str(self) def __setstate__(self, arg): self.__init__(arg) def __cmp__(self, other): if self is other: return 0 elif not isinstance(other, CIMDateTime): return 1 return (cmp(self.__datetime, other.__datetime) or cmp(self.__timedelta, other.__timedelta)) # CIM integer types class CIMInt(CIMType, long): pass class Uint8(CIMInt): cimtype = 'uint8' class Sint8(CIMInt): cimtype = 'sint8' class Uint16(CIMInt): cimtype = 'uint16' class Sint16(CIMInt): cimtype = 'sint16' class Uint32(CIMInt): cimtype = 'uint32' class Sint32(CIMInt): cimtype = 'sint32' class Uint64(CIMInt): cimtype = 'uint64' class Sint64(CIMInt): cimtype = 'sint64' # CIM float types class CIMFloat(CIMType, float): pass class Real32(CIMFloat): cimtype = 'real32' class Real64(CIMFloat): cimtype = 'real64' def cimtype(obj): """Return the CIM type name of an object as a string. For a list, the type is the type of the first element as CIM arrays must be homogeneous.""" if isinstance(obj, CIMType): return obj.cimtype if isinstance(obj, bool): return 'boolean' if isinstance(obj, (str, unicode)): return 'string' if isinstance(obj, list): return cimtype(obj[0]) if isinstance(obj, (datetime, timedelta)): return 'datetime' raise TypeError("Invalid CIM type for %s" % str(obj)) def atomic_to_cim_xml(obj): """Convert an atomic type to CIM external form""" if isinstance(obj, bool): if obj: return "true" else: return "false" elif isinstance(obj, CIMDateTime): return unicode(obj) elif isinstance(obj, datetime): return unicode(CIMDateTime(obj)) elif obj is None: return obj elif cimtype(obj) == 'real32': return u'%.8E' % obj elif cimtype(obj) == 'real64': return u'%.16E' % obj else: return unicode(obj) pywbem-0.7.0/cim_provider.py0000644000175000001440000023155111102360476014733 0ustar bartusers# # (C) Copyright 2003-2007 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Bart Whiteley # Jon Carey #### r"""Python CIM Providers (aka "nirvana") This module is an abstraction and utility layer between a CIMOM and Python providers. The CIMOM uses this module to load Python providers, and route requests to those providers. Python Provider Modules Python Providers are implemented as Python modules. By convention these modules are installed into /usr/lib/pycim. However, they can be anywhere. These modules are loaded on demand using load_source() from the imp module. The CIMOM's pycim interface stores the timestamp of the provider modules. If the modules change, the CIMOM reloads the modules. This is very useful while developing providers, since the latest code will always be loaded and used. A Python Provider Module will contain functions, attributes, and instances that will be accessed and manipulated by this module. Providers are often classified in the following catagories: Instance -- Instrument the retrieval, creation, modification, and deletion of CIM instances. Association -- Instrument CIM associations (CIM classes with the Association qualifier). Method -- Instrument methods as defined on CIM instances or CIM classes. Indication -- Generates indications based on indication subscriptions. Indication Consumer -- "Consumes" (or "Handles") an indication, possibly delivering it through some other means, such as email. Polled -- A polled provider is allowed to run periodically (by calling its poll function). This allows a provider to do some periodic work, without the need to create its own thread. An Instance, Association, and/or Method provider is created by defining one or more subclasses of CIMProvider within the provider module, and registering instances of the subclass(es) with CIM class names by way of the get_providers function (described below). Refer to the documentation for CIMProvider in this module. Indication, Indication Consumer, and Polled providers are defined by implementing some functions within the provider module. Provider module functions: init(env): This module function is optional. It is called immediately after the provider module is imported. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) get_providers(env): Return a dict that maps CIM class names to instances of CIMProvider subclasses. Note that multiple classes can be instrumented by the same instance of a CIMProvider subclass. The CIM class names are case-insensitive, since this dict is converted to a NocaseDict. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) For example, a Python Provider Module may contain the following: class Py_FooBarProvider(CIMProvider): ... def get_providers(env): _fbp = Py_FooBarProvider() return {'Py_Foo':_fbp, 'Py_Bar':_fbp} get_initial_polling_interval(env): Return the number of seconds before the first call to poll. If this method returns zero, then the poll method is never called. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) poll(env): Do some work, and return the number of seconds until the next poll. A polled provider's poll function will be called periodically by the CIMOM. The polled provider can use this opportunity to do some work, such as checking on some conditions, and generating indications. The poll function returns the number of seconds the CIMOM should wait before calling poll again. A return value of -1 indicates to the CIMOM that the previous poll value should be used. A return value of 0 indicates that the poll function should never be called again. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) can_unload(env): Return True if the provider can be unloaded. The CIMOM may try to unload a provider after a period of inactivity. Before unloading a provider, the CIMOM asks the provider if it can be unloaded. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) shutdown(env): Perform any cleanup tasks prior to being unloaded. The provider will shortly be unloaded, and is given an opportunity to perform any needed cleanup. The provider may be unloaded after a period of inactivity (see the documentation for can_unload), or because the CIMOM is shutting down. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) handle_indication(env, ns, handler_instance, indication_instance): Process an indication. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) ns -- The namespace where the even occurred handler_instance -- indication_instance -- The indication activate_filter (env, filter, ns, classes, first_activation): Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- namespace -- classes -- first_activation -- deactivate_filter(env, filter, ns, classes, last_activation): Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) filter -- ns -- classes -- last_activation -- Provider Environment A pycimmb.ProviderEnvironment is passed to many functions. This is a handle back into the CIMOM. You can use it for logging and for making "up-calls" to the CIMOM. For example: logger = env.get_logger() logger.log_debug('Debug Info') ch = env.get_cimom_handle() other_inst = ch.GetInstance(inst_path, LocalOnly=False, IncludeQualifiers=False, IncludeClassOrigin=False) The API of the pycimmb.CIMOMHandle resembles that of pywbem.WBEMConnection. For more information on the ProviderEnvironments, and other features provided by pycimmb, refer to the pycimmb documentation. CodeGen The codegen function can be used to generate provider stub code for a given CIM class. This is a quick way to get started writing a provider. """ import sys from os.path import dirname import pywbem from imp import load_source import types __all__ = ['CIMProvider'] def _path_equals_ignore_host(lhs, rhs): """If one object path doesn't inlcude a host, don't include the hosts in the comparison """ if lhs is rhs: return True if lhs.host is not None and rhs.host is not None and lhs.host != rhs.host: return False # need to make sure this stays in sync with CIMInstanceName.__cmp__() return not (pywbem.cmpname(rhs.classname, lhs.classname) or cmp(rhs.keybindings, lhs.keybindings) or pywbem.cmpname(rhs.namespace, lhs.namespace)) class CIMProvider(object): """Base class for CIM Providers. A derived class might normally override the following: - enum_instances - get_instance - set_instance - delete_instance - references If the provider is a "read-only" instance provider, set_instance and delete_instance need not be overridden. Only association providers need to override references. A method provider should implement a method of the form: def cim_method_(self, env, object_name, method, param_, param_, ...): Where is the name of the method from the CIM schema. needs to be all lowercase, regardless of the case of the method name in the CIM schema (CIM method names are case insensitive). Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName or pywbem.CIMClassname specifying the object on which the method is to be invoked. method -- A pywbem.CIMMethod, representing the method to execute. param_ -- Corresponds to the input parameter from the CIM schema. needs to be all lowercase, regardless of the case of the parameter name in the CIM schema (CIM parameter names are case insensitive). The method returns a two-tuple containing the return value of the method, and a dictionary containing the output parameters. Example: def cim_method_requeststatechange(self, env, object_name, method, param_requestedstate, param_timeoutperiod): # do stuff. out_params = {'job': pywbem.CIMInstanceName(...)} rval = pywbem.Uint32(0) return (rval, out_params) The methods prefixed with "MI_" correspond to the WBEM operations from http://www.dmtf.org/standards/published_documents/DSP200.html The default implementations of these methods call the methods described above. These will not normally be overridden or extended by a subclass. """ def get_instance (self, env, model, cim_class): """Return an instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) model -- A template of the pywbem.CIMInstance to be returned. The key properties are set on this instance to correspond to the instanceName that was requested. The properties of the model are already filtered according to the PropertyList from the request. Only properties present in the model need to be given values. If you prefer, you can set all of the values, and the instance will be filtered for you. cim_class -- The pywbem.CIMClass Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) """ return None def enum_instances(self, env, model, cim_class, keys_only): """Enumerate instances. The WBEM operations EnumerateInstances and EnumerateInstanceNames are both mapped to this method. This method is a python generator Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) model -- A template of the pywbem.CIMInstances to be generated. The properties of the model are already filtered according to the PropertyList from the request. Only properties present in the model need to be given values. If you prefer, you can always set all of the values, and the instance will be filtered for you. cim_class -- The pywbem.CIMClass keys_only -- A boolean. True if only the key properties should be set on the generated instances. Possible Errors: CIM_ERR_FAILED (some other unspecified error occurred) """ pass def set_instance(self, env, instance, previous_instance, cim_class): """Return a newly created or modified instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) instance -- The new pywbem.CIMInstance. If modifying an existing instance, the properties on this instance have been filtered by the PropertyList from the request. previous_instance -- The previous pywbem.CIMInstance if modifying an existing instance. None if creating a new instance. cim_class -- The pywbem.CIMClass Return the new instance. The keys must be set on the new instance. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only valid if previous_instance is None, indicating that the operation was CreateInstance) CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid if previous_instance is not None, indicating that the operation was ModifyInstance) CIM_ERR_FAILED (some other unspecified error occurred) """ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, "") def delete_instance(self, env, instance_name): """Delete an instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) instance_name -- A pywbem.CIMInstanceName specifying the instance to delete. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_NAMESPACE CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified namespace) CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) """ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, "") def references(self, env, object_name, model, assoc_class, result_class_name, role, result_role, keys_only): """Instrument Associations. All four association-related operations (Associators, AssociatorNames, References, ReferenceNames) are mapped to this method. This method is a python generator Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName that defines the source CIM Object whose associated Objects are to be returned. model -- A template pywbem.CIMInstance to serve as a model of the objects to be returned. Only properties present on this model need to be set. assoc_class -- The pywbem.CIMClass. result_class_name -- If not empty, this string acts as a filter on the returned set of Instances by mandating that each returned Instances MUST represent an association between object_name and an Instance of a Class whose name matches this parameter or a subclass. role -- If not empty, MUST be a valid Property name. It acts as a filter on the returned set of Instances by mandating that each returned Instance MUST refer to object_name via a Property whose name matches the value of this parameter. result_role -- If not empty, MUST be a valid Property name. It acts as a filter on the returned set of Instances by mandating that each returned Instance MUST represent associations of object_name to other Instances, where the other Instances play the specified result_role in the association (i.e. the name of the Property in the Association Class that refers to the Object related to object_name MUST match the value of this parameter). keys_only -- A boolean. True if only the key properties should be set on the generated instances. The following diagram may be helpful in understanding the role, result_role, and result_class_name parameters. +------------------------+ +-------------------+ | object_name.classname | | result_class_name | | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ | +------------------------+ +-------------------+ | +-----------------------------------+ | | | [Association] assoc_class | | | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~ | | +--------------+ object_name.classname REF role | | (CIMInstanceName) | result_class_name REF result_role +------+ | |(CIMInstanceName) +-----------------------------------+ Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_NAMESPACE CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_FAILED (some other unspecified error occurred) """ pass def _set_filter_results(self, value): self._filter_results = value def _get_filter_results(self): if hasattr(self, '_filter_results'): return self._filter_results return True filter_results = property(_get_filter_results, _set_filter_results, None, """Determines if the CIMProvider base class should filter results If True, the subclass of CIMProvider in the provider module does not need to filter returned results based on property_list, and in the case of association providers, role, result_role, and result_class_name. The results will be filtered by the CIMProvider base class. If False, the CIMProvider base class will do no filtering. Therefore the subclass of CIMProvider in the provider module will have to filter based on property_list, and in the case of association providers, role, result_role, and result_class_name.""") def MI_enumInstanceNames(self, env, ns, cimClass): """Return instance names of a given CIM class Implements the WBEM operation EnumerateInstanceNames in terms of the enum_instances method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_enumInstanceNames called...') provClass = False keys = pywbem.NocaseDict() [keys.__setitem__(p.name, p) for p in cimClass.properties.values()\ if 'key' in p.qualifiers] _strip_quals(keys) path = pywbem.CIMInstanceName(classname=cimClass.classname, namespace=ns) model = pywbem.CIMInstance(classname=cimClass.classname, properties=keys, path=path) gen = self.enum_instances(env=env, model=model, cim_class=cimClass, keys_only=True) try: iter(gen) except TypeError: logger.log_debug('CIMProvider MI_enumInstanceNames returning') return for inst in gen: rval = build_instance_name(inst) yield rval logger.log_debug('CIMProvider MI_enumInstanceNames returning') def MI_enumInstances(self, env, ns, propertyList, requestedCimClass, cimClass): """Return instances of a given CIM class Implements the WBEM operation EnumerateInstances in terms of the enum_instances method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_enumInstances called...') keyNames = get_keys_from_class(cimClass) plist = None if propertyList is not None: lkns = [kn.lower() for kn in keyNames] props = pywbem.NocaseDict() plist = [s.lower() for s in propertyList] pklist = plist + lkns [props.__setitem__(p.name, p) for p in cimClass.properties.values() if p.name.lower() in pklist] else: props = cimClass.properties _strip_quals(props) path = pywbem.CIMInstanceName(classname=cimClass.classname, namespace=ns) model = pywbem.CIMInstance(classname=cimClass.classname, properties=props, path=path) gen = self.enum_instances(env=env, model=model, cim_class=cimClass, keys_only=False) try: iter(gen) except TypeError: logger.log_debug('CIMProvider MI_enumInstances returning') return for inst in gen: inst.path = build_instance_name(inst, keyNames) if self.filter_results and plist is not None: inst = inst.copy() filter_instance(inst, plist) yield inst logger.log_debug('CIMProvider MI_enumInstances returning') def MI_getInstance(self, env, instanceName, propertyList, cimClass): """Return a specific CIM instance Implements the WBEM operation GetInstance in terms of the get_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_getInstance called...') keyNames = get_keys_from_class(cimClass) plist = None if propertyList is not None: lkns = [kn.lower() for kn in keyNames] props = pywbem.NocaseDict() plist = [s.lower() for s in propertyList] pklist = plist + lkns [props.__setitem__(p.name, p) for p in cimClass.properties.values() if p.name.lower() in pklist] else: props = cimClass.properties _strip_quals(props) model = pywbem.CIMInstance(classname=instanceName.classname, properties=props, path=instanceName) for k, v in instanceName.keybindings.items(): type = cimClass.properties[k].type if type != 'reference': v = val = pywbem.tocimobj(type, v) model.__setitem__(k, pywbem.CIMProperty(name=k, type=type, value=v)) rval = self.get_instance(env=env, model=model, cim_class=cimClass) if self.filter_results: filter_instance(rval, plist) logger.log_debug('CIMProvider MI_getInstance returning') if rval is None: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "") return rval def MI_createInstance(self, env, instance): """Create a CIM instance, and return its instance name Implements the WBEM operation CreateInstance in terms of the set_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_createInstance called...') rval = None ch = env.get_cimom_handle() cimClass = ch.GetClass(instance.classname, instance.path.namespace, LocalOnly=False, IncludeQualifiers=True) # CIMOM has already filled in default property values for # props with default values, if values not supplied by client. rval = self.set_instance(env=env, instance=instance, previous_instance=None, cim_class=cimClass) rval = build_instance_name(rval, cimClass) logger.log_debug('CIMProvider MI_createInstance returning') return rval def MI_modifyInstance(self, env, modifiedInstance, previousInstance, propertyList, cimClass): """Modify a CIM instance Implements the WBEM operation ModifyInstance in terms of the set_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_modifyInstance called...') if propertyList is not None: plist = [p.lower() for p in propertyList] filter_instance(modifiedInstance, plist) modifiedInstance.update(modifiedInstance.path) self.set_instance(env=env, instance=modifiedInstance, previous_instance=previousInstance, cim_class=cimClass) logger.log_debug('CIMProvider MI_modifyInstance returning') def MI_deleteInstance(self, env, instanceName): """Delete a CIM instance Implements the WBEM operation DeleteInstance in terms of the delete_instance method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_deleteInstance called...') self.delete_instance(env=env, instance_name=instanceName) logger.log_debug('CIMProvider MI_deleteInstance returning') def MI_associators(self, env, objectName, assocClassName, resultClassName, role, resultRole, propertyList): """Return instances associated to a given object. Implements the WBEM operation Associators in terms of the references method. A derived class will not normally override this method. """ # NOTE: This should honor the parameters resultClassName, role, resultRole, # and propertyList logger = env.get_logger() logger.log_debug('CIMProvider MI_associators called. assocClass: %s' % (assocClassName)) ch = env.get_cimom_handle() if not assocClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty assocClassName passed to Associators") assocClass = ch.GetClass(assocClassName, objectName.namespace, LocalOnly=False, IncludeQualifiers=True) plist = pywbem.NocaseDict() [plist.__setitem__(p.name, p) for p in assocClass.properties.values() if 'key' in p.qualifiers or p.type == 'reference'] _strip_quals(plist) model = pywbem.CIMInstance(classname=assocClass.classname, properties=plist) model.path = pywbem.CIMInstanceName(classname=assocClass.classname, namespace=objectName.namespace) for inst in self.references(env=env, object_name=objectName, model=model, assoc_class=assocClass, result_class_name=resultClassName, role=role, result_role=None, keys_only=False): for prop in inst.properties.values(): lpname = prop.name.lower() if prop.type != 'reference': continue if role and role.lower() == lpname: continue if resultRole and resultRole.lower() != lpname: continue if _path_equals_ignore_host(prop.value, objectName): continue if resultClassName and self.filter_results and \ not pywbem.is_subclass(ch, objectName.namespace, sub=prop.value.classname, super=resultClassName): continue try: if prop.value.namespace is None: prop.value.namespace = objectName.namespace inst = ch.GetInstance(prop.value, IncludeQualifiers=True, IncludeClassOrigin=True, PropertyList=propertyList) except pywbem.CIMError, (num, msg): if num == pywbem.CIM_ERR_NOT_FOUND: continue else: raise if inst.path is None: inst.path = prop.value yield inst logger.log_debug('CIMProvider MI_associators returning') def MI_associatorNames(self, env, objectName, assocClassName, resultClassName, role, resultRole): """Return instances names associated to a given object. Implements the WBEM operation AssociatorNames in terms of the references method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_associatorNames called. assocClass: %s' % (assocClassName)) ch = env.get_cimom_handle() if not assocClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty assocClassName passed to AssociatorNames") assocClass = ch.GetClass(assocClassName, objectName.namespace, LocalOnly=False, IncludeQualifiers=True) keys = pywbem.NocaseDict() [keys.__setitem__(p.name, p) for p in assocClass.properties.values() if 'key' in p.qualifiers or p.type == 'reference' ] _strip_quals(keys) model = pywbem.CIMInstance(classname=assocClass.classname, properties=keys) model.path = pywbem.CIMInstanceName(classname=assocClass.classname, namespace=objectName.namespace) for inst in self.references(env=env, object_name=objectName, model=model, assoc_class=assocClass, result_class_name=resultClassName, role=role, result_role=None, keys_only=False): for prop in inst.properties.values(): lpname = prop.name.lower() if prop.type != 'reference': continue if role and role.lower() == lpname: continue if resultRole and resultRole.lower() != lpname: continue if _path_equals_ignore_host(prop.value, objectName): continue if resultClassName and self.filter_results and \ not pywbem.is_subclass(ch, objectName.namespace, sub=prop.value.classname, super=resultClassName): continue if prop.value.namespace is None: prop.value.namespace = objectName.namespace yield prop.value logger.log_debug('CIMProvider MI_associatorNames returning') def MI_references(self, env, objectName, resultClassName, role, propertyList): """Return instances of an association class. Implements the WBEM operation References in terms of the references method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_references called. resultClass: %s' % (resultClassName)) ch = env.get_cimom_handle() if not resultClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty resultClassName passed to References") assocClass = ch.GetClass(resultClassName, objectName.namespace, LocalOnly=False, IncludeQualifiers=True) keyNames = get_keys_from_class(assocClass) plist = None if propertyList is not None: lkns = [kn.lower() for kn in keyNames] props = pywbem.NocaseDict() plist = [s.lower() for s in propertyList] pklist = plist + lkns [props.__setitem__(p.name, p) for p in \ assocClass.properties.values() \ if p.name.lower() in pklist] else: props = assocClass.properties _strip_quals(props) model = pywbem.CIMInstance(classname=assocClass.classname, properties=props) model.path = pywbem.CIMInstanceName(classname=assocClass.classname, namespace=objectName.namespace) #if role is None: # raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, # "** this shouldn't happen") if role: if role not in model.properties: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "** this shouldn't happen") model[role] = objectName for inst in self.references(env=env, object_name=objectName, model=model, assoc_class=assocClass, result_class_name='', role=role, result_role=None, keys_only=False): inst.path = build_instance_name(inst, keyNames) if self.filter_results and plist is not None: inst = inst.copy() filter_instance(inst, plist) for prop in inst.properties.values(): if hasattr(prop.value, 'namespace') and prop.value.namespace is None: prop.value.namespace = objectName.namespace yield inst logger.log_debug('CIMProvider MI_references returning') def MI_referenceNames(self, env, objectName, resultClassName, role): """Return instance names of an association class. Implements the WBEM operation ReferenceNames in terms of the references method. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_referenceNames <2> called. resultClass: %s' % (resultClassName)) ch = env.get_cimom_handle() if not resultClassName: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Empty resultClassName passed to ReferenceNames") assocClass = ch.GetClass(resultClassName, objectName.namespace, LocalOnly=False, IncludeQualifiers=True) keys = pywbem.NocaseDict() keyNames = [p.name for p in assocClass.properties.values() if 'key' in p.qualifiers] for keyName in keyNames: p = assocClass.properties[keyName] keys.__setitem__(p.name, p) _strip_quals(keys) model = pywbem.CIMInstance(classname=assocClass.classname, properties=keys) model.path = pywbem.CIMInstanceName(classname=assocClass.classname, namespace=objectName.namespace) #if role is None: # raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, # "** this shouldn't happen") if role: if role not in model.properties: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "** this shouldn't happen") model[role] = objectName for inst in self.references(env=env, object_name=objectName, model=model, assoc_class=assocClass, result_class_name='', role=role, result_role=None, keys_only=True): for prop in inst.properties.values(): if hasattr(prop.value, 'namespace') and prop.value.namespace is None: prop.value.namespace = objectName.namespace yield build_instance_name(inst, keyNames) logger.log_debug('CIMProvider MI_referenceNames returning') def MI_invokeMethod(self, env, objectName, metaMethod, inputParams): """Invoke an extrinsic method. Implements the InvokeMethod WBEM operation by calling the method on a derived class called cim_method_, where is the name of the CIM method, in all lower case. Arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) objectName -- The InstanceName or ClassName of the object on which the method is invoked. metaMethod -- The CIMMethod representing the method to be invoked. inputParams -- A Dictionary where the key is the parameter name and the value is the parameter value. The return value for invokeMethod must be a tuple of size 2 where: element 0 is a tuple of size 2 where element 0 is the return data type name and element 1 is the actual data value. element 1 is a dictionary where the key is the output parameter name and the value is a tuple of size 2 where element 0 is the data type name for the output parameter and element 1 is the actual value of the output parameter. A derived class will not normally override this method. """ logger = env.get_logger() logger.log_debug('CIMProvider MI_invokeMethod called. method: %s:%s' \ % (objectName.classname,metaMethod.name)) lmethName = "cim_method_%s" % metaMethod.name.lower() if hasattr(self, lmethName) : method = getattr(self, lmethName) new_inputs = dict([('param_%s' % k.lower(), v) \ for k, v in inputParams.items()]) (rval, outs) = method(env=env, object_name=objectName, method=metaMethod, **new_inputs) def add_type(v, _tp): lv = v if type(v) == list and len(v) > 0: lv = v[0] if isinstance(lv, pywbem.CIMClass): tp = 'class' elif isinstance(lv, pywbem.CIMInstance): tp = 'instance' elif isinstance(lv, pywbem.CIMInstanceName): tp = 'reference' elif v is None or (type(v) == list and len(v) == 0): tp = _tp else: tp = pywbem.cimtype(v) return (tp, v) for k, v in outs.items(): if hasattr(v, 'namespace') and v.namespace is None: v.namespace = objectName.namespace outs[k] = add_type(v, metaMethod.parameters[k].type) rval = add_type(rval, metaMethod.return_type) rval = (rval, outs) else: raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_FOUND, "%s:%s"%(objectName.classname, metaMethod.name)) logger.log_debug('CIMProvider MI_invokeMethod returning') return rval def filter_instance(inst, plist): """Remove properties from an instance that aren't in the PropertyList inst -- The CIMInstance plist -- The property List, or None. The list items must be all lowercase. """ if plist is not None: for pname in inst.properties.keys(): if pname.lower() not in plist: del inst.properties[pname] def get_keys_from_class(cc): """Return list of the key property names for a class """ return [prop.name for prop in cc.properties.values() \ if 'key' in prop.qualifiers] def build_instance_name(inst, obj=None): """Return an instance name from an instance, and set instance.path """ if obj is None: for _ in inst.properties.values(): inst.path.keybindings.__setitem__(_.name, _.value) return inst.path if not isinstance(obj, list): return build_instance_name(inst, get_keys_from_class(obj)) keys = {} for _ in obj: if _ not in inst.properties: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Instance of %s is missing key property %s" \ %(inst.classname, _)) keys[_] = inst[_] inst.path = pywbem.CIMInstanceName(classname=inst.classname, keybindings=keys, namespace=inst.path.namespace, host=inst.path.host) return inst.path def _strip_quals(props): for prop in props.values(): # remove all but key quals try: prop.qualifiers = pywbem.NocaseDict({'KEY': prop.qualifiers['KEY']}) except KeyError: prop.qualifiers = pywbem.NocaseDict() def codegen (cc): """Generate a Python Provider template. Parameters: cc - A CIMClass to generate code for. Returns a two-tuple containing the Python provider code stubs, and the provider registration MOF. """ import inspect def format_desc (obj, indent): linelen = 75 - indent if isinstance(obj, basestring): raw = obj else: try: raw = obj.qualifiers['description'].value except KeyError: return '' txt = '' beg = 0 end = 0 while beg < len(raw): beg = end end += linelen while beg < len(raw) and raw[beg].isspace(): beg = beg+1 while end < len(raw) and end > beg and not raw[end].isspace(): end = end-1 if beg == end: # a long URL while end < len(raw) and not raw[end].isspace(): end+= 1 line = raw[beg:end] line = line.replace('\n',' ') line = line.replace('\r','') txt += '\n%s%s'% (''.ljust(indent), line) return txt ################# def map_value(obj, val): rv = str(val) if 'ValueMap' not in obj.qualifiers: return rv if 'Values' not in obj.qualifiers: return rv vals = [str(x) for x in obj.qualifiers['Values'].value] maps = [str(x) for x in obj.qualifiers['ValueMap'].value] d = dict(zip(maps, vals)) try: tmp = d[str(val)] rv = '' for ch in tmp: rv+= ch.isalnum() and ch or '_' except KeyError: pass return rv ################# def type_hint (obj, method_name=None): if hasattr(obj, 'type'): tx = obj.type if 'embeddedinstance' in obj.qualifiers: tx = "pywbem.CIMInstance(classname='%s', ...)" % \ obj.qualifiers['embeddedinstance'].value elif tx == 'reference': tx = "pywbem.CIMInstanceName(classname='%s', ...)" % \ obj.reference_class else: tx = obj.return_type if hasattr(obj, 'value') and obj.value is not None: defval = str(obj.value) else: defval = '' if not tx.startswith('pywbem.'): if tx == 'boolean': tx = 'bool(%s)' % defval elif tx == 'datetime': tx = 'pywbem.CIMDateTime()' elif tx == 'string': tx = "''" else: tx = 'pywbem.%s(%s)' % (tx.capitalize(), defval) if 'valuemap' in obj.qualifiers: if defval: defval = map_value(obj, defval) else: defval = '' tx = 'self.Values.%s%s.%s' % \ (method_name and '%s.'%method_name or '', obj.name, defval) if hasattr(obj, 'is_array') and obj.is_array: tx = '[%s,]' % tx return tx ################# def type_str (obj, method_name=None): if hasattr(obj, 'type'): tx = obj.type if 'embeddedinstance' in obj.qualifiers: return "pywbem.CIMInstance(classname='%s', ...)" % \ obj.qualifiers['embeddedinstance'].value elif tx == 'reference': return "REF (pywbem.CIMInstanceName(classname='%s', ...)" % \ obj.reference_class else: tx = obj.return_type if tx == 'boolean': tx = 'bool' elif tx == 'datetime': tx = 'pywbem.CIMDateTime' elif tx == 'string': tx = 'unicode' else: tx = 'pywbem.%s' % tx.capitalize() if hasattr(obj, 'is_array') and obj.is_array: tx = '[%s,]' % tx if 'valuemap' in obj.qualifiers: tx+= ' self.Values.%s%s' % \ (method_name and '%s.'%method_name or '',obj.name) return tx ################# def is_required (obj): if 'required' in obj.qualifiers and obj.qualifiers['required'].value: return '(Required)' return '' ################# def build_val_map(obj): vm = obj.qualifiers['valuemap'].value if 'values' in obj.qualifiers: vals = obj.qualifiers['values'].value else: vals = vm tmap = zip(vals,vm) map = [] for t in tmap: nname = '' for ch in t[0]: if ch.isalnum(): nname+= ch else: nname+= '_' if hasattr(obj, 'return_type'): tp = obj.return_type else: tp = obj.type if tp == 'string': val = "'%s'" % t[1] else: try: int(t[1]) val = 'pywbem.%s(%s)' % (tp.capitalize(), t[1]) except ValueError: val = t[1] nname = "# "+nname map.append((nname,val)) return map valuemaps = {} for obj in cc.properties.values() + cc.methods.values(): if 'valuemap' in obj.qualifiers: valuemaps[obj.name] = {'':build_val_map(obj)} for meth in cc.methods.values(): for parm in meth.parameters.values(): if 'valuemap' in parm.qualifiers: if meth.name not in valuemaps: valuemaps[meth.name] = {} valuemaps[meth.name][parm.name] = build_val_map(parm) mappings = {'classname':cc.classname, 'classname_l':cc.classname.lower()} isAssoc = 'association' in cc.qualifiers code = '''"""Python Provider for %(classname)s Instruments the CIM class %(classname)s """ import pywbem class %(classname)sProvider(pywbem.CIMProvider): """Instrument the CIM class %(classname)s \n''' % mappings code+= format_desc(cc, 4) code+= ''' """''' args = inspect.getargspec(CIMProvider.get_instance)[0] args = ', '.join(args) code+= ''' def __init__ (self, env): logger = env.get_logger() logger.log_debug('Initializing provider %%s from %%s' \\ %% (self.__class__.__name__, __file__)) # If you will be filtering instances yourself according to # property_list, role, result_role, and result_class_name # parameters, set self.filter_results to False # self.filter_results = False def get_instance(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.get_instance()' \\ %% self.__class__.__name__) ''' % (args, CIMProvider.get_instance.__doc__ ) keyProps = [p for p in cc.properties.values() \ if 'key' in p.qualifiers] code+= ''' ux = model.update_existing # TODO fetch system resource matching the following keys:''' for kp in keyProps: code+= ''' # model['%s']''' % kp.name code+= '\n' props = cc.properties.values() props.sort() for prop in props: if 'key' in prop.qualifiers: continue #line = "#ux(%s=%s) # TODO (type = %s) %s" % \ # (prop.name, type_hint(prop), type_str(prop), is_required(prop)) line = "#ux(%s=%s) # TODO %s" % \ (prop.name, type_hint(prop), is_required(prop)) code+= ''' %s''' % line args = inspect.getargspec(CIMProvider.enum_instances)[0] args = ', '.join(args) code+= ''' return model def enum_instances(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.enum_instances()' \\ %% self.__class__.__name__) while False: # TODO more instances? # TODO fetch system resource # Key properties''' % (args, CIMProvider.enum_instances.__doc__) for kp in keyProps: if kp.name == 'CreationClassName': line = "model['%s'] = '%s'" % (kp.name, cc.classname) else: line = "#model['%s'] = # TODO (type = %s)" % \ (kp.name, type_str(kp)) code+=''' %s''' % line code+=''' if keys_only: yield model else: try: yield self.get_instance(env, model, cim_class) except pywbem.CIMError, (num, msg): if num not in (pywbem.CIM_ERR_NOT_FOUND, pywbem.CIM_ERR_ACCESS_DENIED): raise\n''' args = inspect.getargspec(CIMProvider.set_instance)[0] args = ', '.join(args) code+= ''' def set_instance(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.set_instance()' \\ %% self.__class__.__name__) # TODO create or modify the instance raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement return instance''' % (args, CIMProvider.set_instance.__doc__) args = inspect.getargspec(CIMProvider.delete_instance)[0] args = ', '.join(args) code+= ''' def delete_instance(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.delete_instance()' \\ %% self.__class__.__name__) # TODO delete the resource raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement ''' % (args, CIMProvider.delete_instance.__doc__) for method in cc.methods.values(): inParms = [ p for p in method.parameters.values() if \ 'in' in p.qualifiers and p.qualifiers['in'].value ] outParms = [ p for p in method.parameters.values() if \ 'out' in p.qualifiers and p.qualifiers['out'].value ] code+= ''' def cim_method_%s(self, env, object_name, method''' % method.name.lower() for p in inParms: code+= ''',\n%sparam_%s''' % (''.rjust(len(method.name)+20), p.name.lower()) code+= '''): """Implements %s.%s()\n''' % (cc.classname, method.name) code+= format_desc(method, 8) code+= ''' Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName specifying the object on which the method %s() should be invoked. method -- A pywbem.CIMMethod representing the method meta-data'''\ % method.name for p in inParms: code+= ''' param_%s -- The input parameter %s (type %s) %s''' \ % (p.name.lower(), p.name, type_str(p, method.name), is_required(p)) code+= format_desc(p, 12) code+=''' Returns a two-tuple containing the return value (type %s) and a dictionary with the out-parameters Output parameters:''' % type_str(method) if not outParms: code+= ' none' else: for p in outParms: code+=''' %s -- (type %s) %s''' % (p.name, type_str(p, method.name), is_required(p)) code+= format_desc(p, 12) code+=''' Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_NOT_FOUND (the target CIM Class or instance does not exist in the specified namespace) CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor the invocation request) CIM_ERR_FAILED (some other unspecified error occurred) """ logger = env.get_logger() logger.log_debug('Entering %%s.cim_method_%s()' \\ %% self.__class__.__name__) # TODO do something raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented out_params = {}''' % method.name.lower() for p in outParms: code+=''' #out_params['%s'] = %s # TODO''' % (p.name.lower(), type_hint(p, method.name)) code+=''' rval = None # TODO (type %s) return (rval, out_params) ''' % type_str(method) if isAssoc: args = inspect.getargspec(CIMProvider.references)[0] args = format_desc(', '.join(args), 19).strip() code+= ''' def references(%s): """%s""" logger = env.get_logger() logger.log_debug('Entering %%s.references()' \\ %% self.__class__.__name__) ch = env.get_cimom_handle() # This is a common pattern. YMMV''' % \ (args, CIMProvider.references.__doc__) refprops = [] for prop in cc.properties.values(): if prop.reference_class is not None: refprops.append((prop.name, prop.reference_class)) for refprop in refprops: code+= ''' if (not role or role.lower() == '%(refpropnamel)s') and \\ pywbem.is_subclass(ch, object_name.namespace, sub=object_name.classname, super='%(rolecname)s'): model['%(refpropname)s'] = object_name yield model # TODO: Add other REF properties. # Yield association instances where # object_name is %(refpropnamel)s. # Only appropriate if object_name.classname # is '%(rolecname)s' or a subclass.\n''' \ % {'refpropname':refprop[0], 'refpropnamel':refprop[0].lower(), 'rolecname':refprop[1]} if valuemaps: code+= ''' class Values(object):''' for group, maps in valuemaps.items(): code+= ''' class %s(object):''' % group if '' in maps: for value, vm in maps['']: if value in maps: value = value+'_' code+= ''' %s = %s''' % (value, vm) for pname, vms in maps.items(): if pname == '': continue code+= ''' class %s(object):''' % pname for value, vm in vms: code+= ''' %s = %s''' % (value, vm) code+= '\n' code+= ''' ## end of class %(classname)sProvider def get_providers(env): %(classname_l)s_prov = %(classname)sProvider(env) return {'%(classname)s': %(classname_l)s_prov} ''' % mappings owtypes = ['1', 'Instance'] pegtypes = ['2', 'Instance'] if isAssoc: owtypes[0]+= ',3' owtypes[1]+= ', Associator' pegtypes[0]+= ',3' pegtypes[1]+= ', Associator' if cc.methods: owtypes[0]+= ',6' owtypes[1]+= ', Method' pegtypes[0]+= ',5' pegtypes[1]+= ', Method' mof =''' // OpenWBEM Provider registration for %(classname)s instance of OpenWBEM_PyProviderRegistration { InstanceID = ""; // TODO NamespaceNames = {"root/cimv2"}; // TODO ClassName = "%(classname)s"; ProviderTypes = {%(owtypeNums)s}; // %(owtypeStrs)s ModulePath = "/usr/lib/pycim/%(classname)sProvider.py"; // TODO }; // Pegasus Provider registration for %(classname)s instance of PG_ProviderModule { Name = "/usr/lib/pycim/%(classname)sProvider.py"; InterfaceType = "Python"; InterfaceVersion = "1.0.0"; Location = "/usr/lib/pycim/%(classname)sProvider.py"; UserContext = 2; // Requestor Vendor = "TODO"; // TODO Version = "1.0"; }; instance of PG_Provider { Name = "%(classname)s"; ProviderModuleName = "/usr/lib/pycim/%(classname)sProvider.py"; }; instance of PG_ProviderCapabilities { CapabilityID = "%(classname)s"; ProviderModuleName = "/usr/lib/pycim/%(classname)sProvider.py"; ProviderName = "%(classname)s"; ClassName = "%(classname)s"; Namespaces = {"root/cimv2"}; // TODO ProviderType = {%(pegtypeNum)s}; // %(pegtypeStr)s };\n''' % {'classname': cc.classname, 'owtypeNums': owtypes[0], 'owtypeStrs': owtypes[1], 'pegtypeNum': pegtypes[0], 'pegtypeStr': pegtypes[1]} return code, mof class ProviderProxy(object): """Wraps a provider module, and routes requests into the module """ def __init__ (self, env, provid): if isinstance(provid, types.ModuleType): self.provmod = provid self.provid = provid.__name__ self.filename = provid.__file__ else: self.provid = provid # odd chars in a module name tend to break things provider_name = 'pyprovider_' for ch in provid: provider_name+= ch.isalnum() and ch or '_' # let providers import other providers in the same directory provdir = dirname(provid) if provdir not in sys.path: sys.path.append(provdir) # use full path in module name for uniqueness. try: self.provmod = load_source(provider_name, provid) except IOError, arg: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Error loading provider %s: %s" % (provid, arg)) self.filename = self.provmod.__file__ self.provregs = {} if hasattr(self.provmod, 'init'): self.provmod.init(env) if hasattr(self.provmod, 'get_providers'): self.provregs = pywbem.NocaseDict(self.provmod.get_providers(env)) def _get_callable (self, classname, cname): """Return a function or method object appropriate to fulfill a request classname -- The CIM class name associated with the request. cname -- The function or method name to look for. """ callable = None if classname in self.provregs: provClass = self.provregs[classname] if hasattr(provClass, cname): callable = getattr(provClass, cname) elif hasattr(self.provmod, cname): callable = getattr(self.provmod, cname) if callable is None: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "No callable for %s:%s on provider %s"%(classname, cname, self.provid)) return callable ############################################################################## # enumInstanceNames ############################################################################## def MI_enumInstanceNames (self, env, ns, cimClass): logger = env.get_logger() logger.log_debug('ProviderProxy MI_enumInstanceNames called...') for i in self._get_callable(cimClass.classname, 'MI_enumInstanceNames') \ (env, ns, cimClass): yield i logger.log_debug('CIMProvider MI_enumInstanceNames returning') ############################################################################## # enumInstances ############################################################################## def MI_enumInstances(self, env, ns, propertyList, requestedCimClass, cimClass): logger = env.get_logger() logger.log_debug('CIMProvider MI_enumInstances called...') for i in self._get_callable(cimClass.classname, 'MI_enumInstances') \ (env, ns, propertyList, requestedCimClass, cimClass): yield i logger.log_debug('CIMProvider MI_enumInstances returning') ############################################################################## # getInstance ############################################################################## def MI_getInstance(self, env, instanceName, propertyList, cimClass): logger = env.get_logger() logger.log_debug('CIMProvider MI_getInstance called...') rval = self._get_callable(cimClass.classname, 'MI_getInstance') \ (env, instanceName, propertyList, cimClass) logger.log_debug('CIMProvider MI_getInstance returning') return rval ############################################################################## # createInstance ############################################################################## def MI_createInstance(self, env, instance): logger = env.get_logger() logger.log_debug('CIMProvider MI_createInstance called...') rval = self._get_callable(instance.classname, 'MI_createInstance') \ (env, instance) logger.log_debug('CIMProvider MI_createInstance returning') return rval ############################################################################## # modifyInstance ############################################################################## def MI_modifyInstance(self, env, modifiedInstance, previousInstance, propertyList, cimClass): logger = env.get_logger() logger.log_debug('CIMProvider MI_modifyInstance called...') self._get_callable(cimClass.classname, 'MI_modifyInstance') \ (env, modifiedInstance, previousInstance, propertyList, cimClass) logger.log_debug('CIMProvider MI_modifyInstance returning') ############################################################################## # deleteInstance ############################################################################## def MI_deleteInstance(self, env, instanceName): logger = env.get_logger() logger.log_debug('CIMProvider MI_deleteInstance called...') self._get_callable(instanceName.classname, 'MI_deleteInstance') \ (env, instanceName) logger.log_debug('CIMProvider MI_deleteInstance returning') ############################################################################## # associators ############################################################################## def MI_associators(self, env, objectName, assocClassName, resultClassName, role, resultRole, propertyList): # NOTE: This should honor the parameters resultClassName, role, resultRole, # and propertyList logger = env.get_logger() logger.log_debug('CIMProvider MI_associators called. assocClass: %s' % (assocClassName)) cname = assocClassName for i in self._get_callable(cname, 'MI_associators') \ (env, objectName, assocClassName, resultClassName, role, resultRole, propertyList): yield i logger.log_debug('CIMProvider MI_associators returning') ############################################################################## # associatorNames ############################################################################## def MI_associatorNames(self, env, objectName, assocClassName, resultClassName, role, resultRole): logger = env.get_logger() logger.log_debug('CIMProvider MI_associatorNames called. assocClass: %s' % (assocClassName)) cname = assocClassName for i in self._get_callable(cname, 'MI_associatorNames') \ (env, objectName, assocClassName, resultClassName, role, resultRole): yield i logger.log_debug('CIMProvider MI_associatorNames returning') ############################################################################## # references ############################################################################## def MI_references(self, env, objectName, resultClassName, role, propertyList): logger = env.get_logger() logger.log_debug('CIMProvider MI_references called. resultClass: %s' % (resultClassName)) cname = resultClassName if not cname: return for i in self._get_callable(cname, 'MI_references') \ (env, objectName, resultClassName, role, propertyList): yield i logger.log_debug('CIMProvider MI_references returning') ############################################################################## # referenceNames ############################################################################## def MI_referenceNames(self, env, objectName, resultClassName, role): logger = env.get_logger() logger.log_debug('CIMProvider MI_referenceNames <1> called. resultClass: %s' % (resultClassName)) cname = resultClassName if not cname: return for i in self._get_callable(cname, 'MI_referenceNames') \ (env, objectName, resultClassName, role): yield i logger.log_debug('CIMProvider MI_referenceNames returning') ############################################################################## # invokeMethod # inputParam is a Dictionary where the key is the parameter name # and the value is the parameter value # The return value for invokeMethod must be a tuple of size 2 where # element 0 is a tuple of size 2 where element 0 is the return data type name # and element 1 is the actual data value # element 1 is a dictionary where the key is the output parameter name # and the value is a tuple of size 2 where element 0 is the data type name # for the output parameter and element 1 is the actual value of the # output parameter. ############################################################################## def MI_invokeMethod(self, env, objectName, metaMethod, inputParams): logger = env.get_logger() logger.log_debug('CIMProvider MI_invokeMethod called. method: %s:%s' \ % (objectName.classname,metaMethod.name)) rval = self._get_callable(objectName.classname, 'MI_invokeMethod') \ (env, objectName, metaMethod, inputParams) logger.log_debug('CIMProvider MI_invokeMethod returning') return rval ############################################################################## def MI_poll (self, env): logger = env.get_logger() logger.log_debug('CIMProvider MI_poll called') if hasattr(self.provmod, 'poll'): rval = self.provmod.poll(env) elif hasattr(self.provmod, 'MI_poll'): rval = self.provmod.MI_poll(env) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for polling"%self.provid) logger.log_debug('CIMProvider MI_poll returning %s' % str(rval)) return rval ############################################################################## def MI_getInitialPollingInterval (self, env): logger = env.get_logger() logger.log_debug('CIMProvider MI_poll called') if hasattr(self.provmod, 'get_initial_polling_interval'): rval = self.provmod.get_initial_polling_interval(env) elif hasattr(self.provmod, 'MI_getInitialPollingInterval'): rval = self.provmod.MI_getInitialPollingInterval(env) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for polling"%self.provid) logger.log_debug('CIMProvider MI_poll returning %s' % str(rval)) return rval ############################################################################## def MI_activateFilter (self, env, filter, namespace, classes, firstActivation): logger = env.get_logger() logger.log_debug('CIMProvider MI_activateFilter called') if hasattr(self.provmod, 'activate_filter'): self.provmod.activate_filter(env, filter, namespace, classes, firstActivation) elif hasattr(self.provmod, 'MI_activateFilter'): self.provmod.MI_activateFilter(env, filter, namespace, classes, firstActivation) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for activate filter"%self.provid) logger.log_debug('CIMProvider MI_activateFilter returning') ############################################################################## def MI_deActivateFilter(self, env, filter, namespace, classes, lastActivation): logger = env.get_logger() logger.log_debug('CIMProvider MI_deActivateFilter called') if hasattr(self.provmod, 'deactivate_filter'): self.provmod.deactivate_filter(env, filter, namespace, classes, lastActivation) elif hasattr(self.provmod, 'MI_deActivateFilter'): self.provmod.MI_deActivateFilter(env, filter, namespace, classes, lastActivation) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for deactivate filter"%self.provid) logger.log_debug('CIMProvider MI_deActivateFilter returning') ############################################################################## def MI_shutdown (self, env): modname = self.provmod.__name__ if hasattr(self.provmod, "shutdown"): self.provmod.shutdown(env) self.provmod = None del sys.modules[modname] #TODO concurrency problems here?? ############################################################################## def MI_canunload(self, env): if hasattr(self.provmod, "canunload"): return self.provmod.canunload else: return True ############################################################################## def MI_consumeIndication(self, env, destinationPath, indicationInstance): logger = env.get_logger() logger.log_debug('ProviderProxy MI_consumeIndication called') if hasattr(self.provmod, 'consume_indication'): self.provmod.consume_indication(env, destinationPath, indicationInstance) elif hasattr(self.provmod, 'MI_consumeIndication'): self.provmod.MI_consumeIndication(env, destinationPath, indicationInstance) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for consume indication" % \ self.provid) logger.log_debug('ProviderProxy MI_consumeIndication returning') ############################################################################## def MI_handleIndication(self, env, ns, handlerInstance, indicationInstance): logger = env.get_logger() logger.log_debug('ProviderProxy MI_handleIndication called') if hasattr(self.provmod, 'handle_indication'): self.provmod.handle_indication(env, ns, handlerInstance, indicationInstance) elif hasattr(self.provmod, 'MI_handleIndication'): self.provmod.MI_handleIndication(env, ns, handlerInstance, indicationInstance) else: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Provider %s has no support for handle indication"%self.provid) logger.log_debug('ProviderProxy MI_handleIndication returning') pywbem-0.7.0/PKG-INFO0000644000175000001440000000045311120521334012757 0ustar bartusersMetadata-Version: 1.0 Name: pywbem Version: 0.7.0 Summary: Python WBEM client library Home-page: http://pywbem.sf.net/ Author: Tim Potter Author-email: tpot@hp.com License: LGPLv2 Description: A pure-Python library for performing operations using the WBEM management protocol. Platform: any pywbem-0.7.0/mof_compiler.py0000755000175000001440000016115211117601457014731 0ustar bartusers#!/usr/bin/env python # # (C) Copyright 2006-2007 Novell, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2 of the # License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Author: Bart Whiteley import sys import os import lex import yacc from lex import TOKEN from cim_operations import CIMError, WBEMConnection from cim_obj import * from cim_constants import * from getpass import getpass _optimize = 1 _tabmodule='mofparsetab' _lextab='moflextab' reserved = { 'any':'ANY', 'as':'AS', 'association':'ASSOCIATION', 'class':'CLASS', 'disableoverride':'DISABLEOVERRIDE', 'boolean':'DT_BOOL', 'char16':'DT_CHAR16', 'datetime':'DT_DATETIME', 'pragma':'PRAGMA', 'real32':'DT_REAL32', 'real64':'DT_REAL64', 'sint16':'DT_SINT16', 'sint32':'DT_SINT32', 'sint64':'DT_SINT64', 'sint8':'DT_SINT8', 'string':'DT_STR', 'uint16':'DT_UINT16', 'uint32':'DT_UINT32', 'uint64':'DT_UINT64', 'uint8':'DT_UINT8', 'enableoverride':'ENABLEOVERRIDE', 'false':'FALSE', 'flavor':'FLAVOR', 'indication':'INDICATION', 'instance':'INSTANCE', 'method':'METHOD', 'null':'NULL', 'of':'OF', 'parameter':'PARAMETER', 'property':'PROPERTY', 'qualifier':'QUALIFIER', 'ref':'REF', 'reference':'REFERENCE', 'restricted':'RESTRICTED', 'schema':'SCHEMA', 'scope':'SCOPE', 'tosubclass':'TOSUBCLASS', 'translatable':'TRANSLATABLE', 'true':'TRUE', } tokens = reserved.values() + [ 'IDENTIFIER', 'stringValue', 'floatValue', 'charValue', 'binaryValue', 'octalValue', 'decimalValue', 'hexValue', ] literals = '#(){};[],$:=' # UTF-8 (from Unicode 4.0.0 standard): # Table 3-6. Well-Formed UTF-8 Byte Sequences Code Points # 1st Byte 2nd Byte 3rd Byte 4th Byte # U+0000..U+007F 00..7F # U+0080..U+07FF C2..DF 80..BF # U+0800..U+0FFF E0 A0..BF 80..BF # U+1000..U+CFFF E1..EC 80..BF 80..BF # U+D000..U+D7FF ED 80..9F 80..BF # U+E000..U+FFFF EE..EF 80..BF 80..BF # U+10000..U+3FFFF F0 90..BF 80..BF 80..BF # U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF # U+100000..U+10FFFF F4 80..8F 80..BF 80..BF utf8_2 = r'[\xC2-\xDF][\x80-\xBF]' utf8_3_1 = r'\xE0[\xA0-\xBF][\x80-\xBF]' utf8_3_2 = r'[\xE1-\xEC][\x80-\xBF][\x80-\xBF]' utf8_3_3 = r'\xED[\x80-\x9F][\x80-\xBF]' utf8_3_4 = r'[\xEE-\xEF][\x80-\xBF][\x80-\xBF]' utf8_4_1 = r'\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]' utf8_4_2 = r'[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]' utf8_4_3 = r'\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]' utf8Char = r'(%s)|(%s)|(%s)|(%s)|(%s)|(%s)|(%s)|(%s)' % (utf8_2, utf8_3_1, utf8_3_2, utf8_3_3, utf8_3_4, utf8_4_1, utf8_4_2, utf8_4_3) def t_COMMENT(t): r'//.*' pass def t_MCOMMENT(t): r'/\*(.|\n)*?\*/' t.lineno += t.value.count('\n') t_binaryValue = r'[+-]?[01]+[bB]' t_octalValue = r'[+-]?0[0-7]+' t_decimalValue = r'[+-]?([1-9][0-9]*|0)' t_hexValue = r'[+-]?0[xX][0-9a-fA-F]+' t_floatValue = r'[+-]?[0-9]*\.[0-9]+([eE][+-]?[0-9]+)?' simpleEscape = r"""[bfnrt'"\\]""" hexEscape = r'x[0-9a-fA-F]{1,4}' escapeSequence = r'[\\]((%s)|(%s))' % (simpleEscape, hexEscape) cChar = r"[^'\\\n\r]|(%s)" % escapeSequence sChar = r'[^"\\\n\r]|(%s)' % escapeSequence charValue = r"'%s'" % cChar t_stringValue = r'"(%s)*"' % sChar identifier_re = r'([a-zA-Z_]|(%s))([0-9a-zA-Z_]|(%s))*' % (utf8Char, utf8Char) @TOKEN(identifier_re) def t_IDENTIFIER(t): t.type = reserved.get(t.value.lower(),'IDENTIFIER') # check for reserved word return t # Define a rule so we can track line numbers def t_newline(t): r'\n+' t.lexer.lineno += len(t.value) t.lexer.linestart = t.lexpos t_ignore = ' \r\t' # Error handling rule def t_error(t): msg = "Illegal character '%s' " % t.value[0] msg+= "Line %d, col %d" % (t.lineno, _find_column(t.lexer.parser.mof, t)) t.lexer.parser.log(msg) t.lexer.skip(1) class MOFParseError(ValueError): pass def p_error(p): ex = MOFParseError() if p is None: ex.args = ('Unexpected end of file',) raise ex ex.file = p.lexer.parser.file ex.lineno = p.lineno ex.column = _find_column(p.lexer.parser.mof, p) ex.context = _get_error_context(p.lexer.parser.mof, p) raise ex def p_mofSpecification(p): """mofSpecification : mofProductionList""" def p_mofProductionList(p): """mofProductionList : empty | mofProductionList mofProduction """ def p_mofProduction(p): """mofProduction : compilerDirective | mp_createClass | mp_setQualifier | mp_createInstance """ def _create_ns(p, handle, ns): # Figure out the flavor of cim server cimom_type = None ns = ns.strip('/') try: inames = handle.EnumerateInstanceNames('__Namespace', namespace='root') inames = [x['name'] for x in inames] if 'PG_InterOp' in inames: cimom_type = 'pegasus' except CIMError, ce: if ce.args[0] != CIM_ERR_NOT_FOUND: ce.file_line = (p.parser.file, p.lexer.lineno) raise if not cimom_type: try: inames = handle.EnumerateInstanceNames('CIM_Namespace', namespace='Interop') inames = [x['name'] for x in inames] cimom_type = 'proper' except CIMError, ce: ce.file_line = (p.parser.file, p.lexer.lineno) raise if not cimom_type: ce = CIMError(CIM_ERR_FAILED, 'Unable to determine CIMOM type') ce.file_line = (p.parser.file, p.lexer.lineno) raise ce if cimom_type == 'pegasus': # To create a namespace in Pegasus, create an instance of # __Namespace with __Namespace.Name = '', and create it in # the target namespace to be created. inst = CIMInstance('__Namespace', properties={'Name':''}, path=CIMInstanceName('__Namespace', keybindings={'Name':''}, namespace=ns)) try: handle.CreateInstance(inst) except CIMError, ce: if ce.args[0] != CIM_ERR_ALREADY_EXISTS: ce.file_line = (p.parser.file, p.lexer.lineno) raise elif cimom_type == 'proper': inst = CIMInstance('CIM_Namespace', properties={'Name': ns}, path=CIMInstanceName('CIM_Namespace', namespace='root', keybindings={'Name':ns})) handle.CreateInstance(inst) def p_mp_createClass(p): """mp_createClass : classDeclaration | assocDeclaration | indicDeclaration """ ns = p.parser.handle.default_namespace cc = p[1] try: fixedNS = fixedRefs = fixedSuper = False while not fixedNS or not fixedRefs or not fixedSuper: try: if p.parser.verbose: p.parser.log('Creating class %s:%s' % (ns, cc.classname)) p.parser.handle.CreateClass(cc) if p.parser.verbose: p.parser.log('Created class %s:%s' % (ns,cc.classname)) p.parser.classnames[ns].append(cc.classname.lower()) break except CIMError, ce: ce.file_line = (p.parser.file, p.lexer.lineno) errcode = ce.args[0] if errcode == CIM_ERR_INVALID_NAMESPACE: if fixedNS: raise if p.parser.verbose: p.parser.log('Creating namespace ' + ns) _create_ns(p, p.parser.handle, ns) fixedNS = True continue if not p.parser.search_paths: raise if errcode == CIM_ERR_INVALID_SUPERCLASS: if fixedSuper: raise moffile = p.parser.mofcomp.find_mof(cc.superclass) if not moffile: raise p.parser.mofcomp.compile_file(moffile, ns) fixedSuper = True elif errcode in [CIM_ERR_INVALID_PARAMETER, CIM_ERR_NOT_FOUND, CIM_ERR_FAILED]: if fixedRefs: raise if not p.parser.qualcache[ns]: for fname in ['qualifiers', 'qualifiers_optional']: qualfile = p.parser.mofcomp.find_mof(fname) if qualfile: p.parser.mofcomp.compile_file(qualfile,ns) if not p.parser.qualcache[ns]: # can't find qualifiers raise objects = cc.properties.values() for meth in cc.methods.values(): objects+= meth.parameters.values() dep_classes = [] for obj in objects: if obj.type not in ['reference','string']: continue if obj.type == 'reference': if obj.reference_class.lower() not in dep_classes: dep_classes.append(obj.reference_class.lower()) continue # else obj.type is 'string' try: embedded_inst= obj.qualifiers['embeddedinstance'] except KeyError: continue embedded_inst = embedded_inst.value.lower() if embedded_inst not in dep_classes: dep_classes.append(embedded_inst) continue for klass in dep_classes: if klass in p.parser.classnames[ns]: continue try: # don't limit it with LocalOnly=True, # PropertyList, IncludeQualifiers=False, ... # because of caching in case we're using the # special WBEMConnection subclass used for # removing schema elements p.parser.handle.GetClass(klass, LocalOnly=False, IncludeQualifiers=True) p.parser.classnames[ns].append(klass) except CIMError: moffile = p.parser.mofcomp.find_mof(klass) if not moffile: raise p.parser.mofcomp.compile_file(moffile, ns) p.parser.classnames[ns].append(klass) fixedRefs = True else: raise except CIMError, ce: ce.file_line = (p.parser.file, p.lexer.lineno) if ce.args[0] != CIM_ERR_ALREADY_EXISTS: raise if p.parser.verbose: p.parser.log('Class %s already exist. Modifying...' % cc.classname) try: p.parser.handle.ModifyClass(cc, ns) except CIMError, ce: p.parser.log('Error Modifying class %s: %s, %s' % (cc.classname, ce.args[0], ce.args[1])) def p_mp_createInstance(p): """mp_createInstance : instanceDeclaration""" inst = p[1] if p.parser.verbose: p.parser.log('Creating instance of %s.' % inst.classname) try: p.parser.handle.CreateInstance(inst) except CIMError, ce: if ce.args[0] == CIM_ERR_ALREADY_EXISTS: if p.parser.verbose: p.parser.log('Instance of class %s already exist. Modifying...' % inst.classname) try: p.parser.handle.ModifyInstance(inst) except CIMError, ce: if ce.args[0] == CIM_ERR_NOT_SUPPORTED: if p.parser.verbose: p.parser.log('ModifyInstance not supported. Deleting instance of %s: %s' % (inst.classname, inst.path)) p.parser.handle.DeleteInstance(inst.path) if p.parser.verbose: p.parser.log('Creating instance of %s.' % inst.classname) p.parser.handle.CreateInstance(inst) else: ce.file_line = (p.parser.file, p.lexer.lineno) raise def p_mp_setQualifier(p): """mp_setQualifier : qualifierDeclaration""" qualdecl = p[1] ns = p.parser.handle.default_namespace if p.parser.verbose: p.parser.log('Setting qualifier %s' % qualdecl.name) try: p.parser.handle.SetQualifier(qualdecl) except CIMError, ce: if ce.args[0] == CIM_ERR_INVALID_NAMESPACE: if p.parser.verbose: p.parser.log('Creating namespace ' + ns) _create_ns(p, p.parser.handle, ns) if p.parser.verbose: p.parser.log('Setting qualifier %s' % qualdecl.name) p.parser.handle.SetQualifier(qualdecl) elif ce.args[0] == CIM_ERR_NOT_SUPPORTED: if p.parser.verbose: p.parser.log('Qualifier %s already exists. Deleting...' % qualdecl.name) p.parser.handle.DeleteQualifier(qualdecl.name) if p.parser.verbose: p.parser.log('Setting qualifier %s' % qualdecl.name) p.parser.handle.SetQualifier(qualdecl) else: ce.file_line = (p.parser.file, p.lexer.lineno) raise p.parser.qualcache[ns][qualdecl.name] = qualdecl def p_compilerDirective(p): """compilerDirective : '#' PRAGMA pragmaName '(' pragmaParameter ')'""" directive = p[3].lower() param = p[5] if directive == 'include': fname = param #if p.parser.file: fname = os.path.dirname(p.parser.file) + '/' + fname p.parser.mofcomp.compile_file(fname, p.parser.handle.default_namespace) elif directive == 'namespace': p.parser.handle.default_namespace = param if param not in p.parser.qualcache: p.parser.qualcache[param] = NocaseDict() p[0] = None def p_pragmaName(p): """pragmaName : identifier""" p[0] = p[1] def p_pragmaParameter(p): """pragmaParameter : stringValue""" p[0] = _fixStringValue(p[1]) def p_classDeclaration(p): """classDeclaration : CLASS className '{' classFeatureList '}' ';' | CLASS className superClass '{' classFeatureList '}' ';' | CLASS className alias '{' classFeatureList '}' ';' | CLASS className alias superClass '{' classFeatureList '}' ';' | qualifierList CLASS className '{' classFeatureList '}' ';' | qualifierList CLASS className superClass '{' classFeatureList '}' ';' | qualifierList CLASS className alias '{' classFeatureList '}' ';' | qualifierList CLASS className alias superClass '{' classFeatureList '}' ';' """ superclass = None alias = None quals = [] if isinstance(p[1], basestring): # no class qualifiers cname = p[2] if p[3][0] == '$': # alias present alias = p[3] if p[4] == '{': # no superclass cfl = p[5] else: # superclass superclass = p[4] cfl = p[6] else: # no alias if p[3] == '{': # no superclass cfl = p[4] else: # superclass superclass = p[3] cfl = p[5] else: # class qualifiers quals = p[1] cname = p[3] if p[4][0] == '$': # alias present alias = p[4] if p[5] == '{': # no superclass cfl = p[6] else: # superclass superclass = p[5] cfl = p[7] else: # no alias if p[4] == '{': # no superclass cfl = p[5] else: # superclass superclass = p[4] cfl = p[6] quals = dict([(x.name, x) for x in quals]) methods = {} props = {} for item in cfl: item.class_origin = cname if isinstance(item, CIMMethod): methods[item.name] = item else: props[item.name] = item p[0] = CIMClass(cname, properties=props, methods=methods, superclass=superclass, qualifiers=quals) if alias: p.parser.aliases[alias] = p[0] def p_classFeatureList(p): """classFeatureList : empty | classFeatureList classFeature """ if len(p) == 2: p[0] = [] else: p[0] = p[1] + [p[2]] def p_assocDeclaration(p): """assocDeclaration : '[' ASSOCIATION qualifierListEmpty ']' CLASS className '{' associationFeatureList '}' ';' | '[' ASSOCIATION qualifierListEmpty ']' CLASS className superClass '{' associationFeatureList '}' ';' | '[' ASSOCIATION qualifierListEmpty ']' CLASS className alias '{' associationFeatureList '}' ';' | '[' ASSOCIATION qualifierListEmpty ']' CLASS className alias superClass '{' associationFeatureList '}' ';' """ aqual = CIMQualifier('ASSOCIATION', True, type='boolean') # TODO flavor trash. quals = [aqual] + p[3] p[0] = _assoc_or_indic_decl(quals, p) def p_indicDeclaration(p): """indicDeclaration : '[' INDICATION qualifierListEmpty ']' CLASS className '{' classFeatureList '}' ';' | '[' INDICATION qualifierListEmpty ']' CLASS className superClass '{' classFeatureList '}' ';' | '[' INDICATION qualifierListEmpty ']' CLASS className alias '{' classFeatureList '}' ';' | '[' INDICATION qualifierListEmpty ']' CLASS className alias superClass '{' classFeatureList '}' ';' """ iqual = CIMQualifier('INDICATION', True, type='boolean') # TODO flavor trash. quals = [iqual] + p[3] p[0] = _assoc_or_indic_decl(quals, p) def _assoc_or_indic_decl(quals, p): """(refer to grammer rules on p_assocDeclaration and p_indicDeclaration)""" superclass = None alias = None cname = p[6] if p[7] == '{': cfl = p[8] elif p[7][0] == '$': # alias alias = p[7] if p[8] == '{': cfl = p[9] else: superclass = p[8] cfl = p[10] else: superclass = p[7] cfl = p[9] props = {} methods = {} for item in cfl: item.class_origin = cname if isinstance(item, CIMMethod): methods[item.name] = item else: props[item.name] = item quals = dict([(x.name, x) for x in quals]) cc = CIMClass(cname, properties=props, methods=methods, superclass=superclass, qualifiers=quals) if alias: p.parser.aliases[alias] = cc return cc def p_qualifierListEmpty(p): """qualifierListEmpty : empty | qualifierListEmpty ',' qualifier """ if len(p) == 2: p[0] = [] else: p[0] = p[1] + [p[3]] def p_associationFeatureList(p): """associationFeatureList : empty | associationFeatureList associationFeature """ if len(p) == 2: p[0] = [] else: p[0] = p[1] + [p[2]] def p_className(p): """className : identifier""" p[0] = p[1] def p_alias(p): """alias : AS aliasIdentifier""" p[0] = p[2] def p_aliasIdentifier(p): """aliasIdentifier : '$' identifier""" p[0] = '$%s' % p[2] def p_superClass(p): """superClass : ':' className""" p[0] = p[2] def p_classFeature(p): """classFeature : propertyDeclaration | methodDeclaration | referenceDeclaration """ p[0] = p[1] def p_associationFeature(p): """associationFeature : classFeature""" p[0] = p[1] def p_qualifierList(p): """qualifierList : '[' qualifier qualifierListEmpty ']'""" p[0] = [p[2]] + p[3] def p_qualifier(p): """qualifier : qualifierName | qualifierName ':' flavorList | qualifierName qualifierParameter | qualifierName qualifierParameter ':' flavorList """ qname = p[1] ns = p.parser.handle.default_namespace qval = None flavorlist = [] if len(p) == 3: qval = p[2] elif len(p) == 4: flavorlist = p[3] elif len(p) == 5: qval = p[2] flavorlist = p[4] try: qualdecl = p.parser.qualcache[ns][qname] except KeyError: try: quals = p.parser.handle.EnumerateQualifiers() except CIMError, ce: if ce.args[0] != CIM_ERR_INVALID_NAMESPACE: ce.file_line = (p.parser.file, p.lexer.lineno) raise _create_ns(p, p.parser.handle, ns) quals = None if quals: for qual in quals: p.parser.qualcache[ns][qual.name] = qual else: for fname in ['qualifiers', 'qualifiers_optional']: qualfile = p.parser.mofcomp.find_mof(fname) if qualfile: p.parser.mofcomp.compile_file(qualfile, ns) try: qualdecl = p.parser.qualcache[ns][qname] except KeyError: ce = CIMError(CIM_ERR_FAILED, 'Unknown Qualifier: %s' % qname) ce.file_line = (p.parser.file, p.lexer.lineno) raise ce flavors = _build_flavors(flavorlist, qualdecl) if qval is None: if qualdecl.type == 'boolean': qval = True else: qval = qualdecl.value # default value else: qval = tocimobj(qualdecl.type, qval) p[0] = CIMQualifier(qname, qval, type=qualdecl.type, **flavors) # TODO propagated? def p_flavorList(p): """flavorList : flavor | flavorList flavor """ if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[2]] def p_qualifierParameter(p): """qualifierParameter : '(' constantValue ')' | arrayInitializer """ if len(p) == 2: p[0] = p[1] else: p[0] = p[2] def p_flavor(p): """flavor : ENABLEOVERRIDE | DISABLEOVERRIDE | RESTRICTED | TOSUBCLASS | TRANSLATABLE """ p[0] = p[1].lower() def p_propertyDeclaration(p): """propertyDeclaration : propertyDeclaration_1 | propertyDeclaration_2 | propertyDeclaration_3 | propertyDeclaration_4 | propertyDeclaration_5 | propertyDeclaration_6 | propertyDeclaration_7 | propertyDeclaration_8 """ p[0] = p[1] def p_propertyDeclaration_1(p): """propertyDeclaration_1 : dataType propertyName ';'""" p[0] = CIMProperty(p[2], None, type=p[1]) def p_propertyDeclaration_2(p): """propertyDeclaration_2 : dataType propertyName defaultValue ';'""" p[0] = CIMProperty(p[2], p[3], type=p[1]) def p_propertyDeclaration_3(p): """propertyDeclaration_3 : dataType propertyName array ';'""" p[0] = CIMProperty(p[2], None, type=p[1], is_array=True, array_size=p[3]) def p_propertyDeclaration_4(p): """propertyDeclaration_4 : dataType propertyName array defaultValue ';'""" p[0] = CIMProperty(p[2], p[4], type=p[1], is_array=True, array_size=p[3]) def p_propertyDeclaration_5(p): """propertyDeclaration_5 : qualifierList dataType propertyName ';'""" quals = dict([(x.name, x) for x in p[1]]) p[0] = CIMProperty(p[3], None, type=p[2], qualifiers=quals) def p_propertyDeclaration_6(p): """propertyDeclaration_6 : qualifierList dataType propertyName defaultValue ';'""" quals = dict([(x.name, x) for x in p[1]]) p[0] = CIMProperty(p[3], tocimobj(p[2], p[4]), type=p[2], qualifiers=quals) def p_propertyDeclaration_7(p): """propertyDeclaration_7 : qualifierList dataType propertyName array ';'""" quals = dict([(x.name, x) for x in p[1]]) p[0] = CIMProperty(p[3], None, type=p[2], qualifiers=quals, is_array=True, array_size=p[4]) def p_propertyDeclaration_8(p): """propertyDeclaration_8 : qualifierList dataType propertyName array defaultValue ';'""" quals = dict([(x.name, x) for x in p[1]]) p[0] = CIMProperty(p[3], tocimobj(p[2], p[5]), type=p[2], qualifiers=quals, is_array=True, array_size=p[4]) def p_referenceDeclaration(p): """referenceDeclaration : objectRef referenceName ';' | objectRef referenceName defaultValue ';' | qualifierList objectRef referenceName ';' | qualifierList objectRef referenceName defaultValue ';' """ quals = [] dv = None if isinstance(p[1], list): # qualifiers quals = p[1] cname = p[2] pname = p[3] if len(p) == 6: dv = p[4] else: cname = p[1] pname = p[2] if len(p) == 5: dv = p[3] quals = dict([(x.name, x) for x in quals]) p[0] = CIMProperty(pname, dv, type='reference', reference_class=cname, qualifiers=quals) def p_methodDeclaration(p): """methodDeclaration : dataType methodName '(' ')' ';' | dataType methodName '(' parameterList ')' ';' | qualifierList dataType methodName '(' ')' ';' | qualifierList dataType methodName '(' parameterList ')' ';' """ paramlist = [] quals = [] if isinstance(p[1], basestring): # no quals dt = p[1] mname = p[2] if p[4] != ')': paramlist = p[4] else: # quals present quals = p[1] dt = p[2] mname = p[3] if p[5] != ')': paramlist = p[5] params = dict([(param.name, param) for param in paramlist]) quals = dict([(q.name, q) for q in quals]) p[0] = CIMMethod(mname, return_type=dt, parameters=params, qualifiers=quals) # note: class_origin is set when adding method to class. # TODO what to do with propagated? def p_propertyName(p): """propertyName : identifier""" p[0] = p[1] def p_referenceName(p): """referenceName : identifier""" p[0] = p[1] def p_methodName(p): """methodName : identifier""" p[0] = p[1] def p_dataType(p): """dataType : DT_UINT8 | DT_SINT8 | DT_UINT16 | DT_SINT16 | DT_UINT32 | DT_SINT32 | DT_UINT64 | DT_SINT64 | DT_REAL32 | DT_REAL64 | DT_CHAR16 | DT_STR | DT_BOOL | DT_DATETIME """ p[0] = p[1].lower() def p_objectRef(p): """objectRef : className REF""" p[0] = p[1] def p_parameterList(p): """parameterList : parameter | parameterList ',' parameter """ if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[3]] def p_parameter(p): """parameter : parameter_1 | parameter_2 | parameter_3 | parameter_4 """ p[0] = p[1] def p_parameter_1(p): """parameter_1 : dataType parameterName | dataType parameterName array """ args = {} if len(p) == 4: args['is_array'] = True args['array_size'] = p[3] p[0] = CIMParameter(p[2], p[1], **args) def p_parameter_2(p): """parameter_2 : qualifierList dataType parameterName | qualifierList dataType parameterName array """ args = {} if len(p) == 5: args['is_array'] = True args['array_size'] = p[4] quals = dict([(x.name, x) for x in p[1]]) p[0] = CIMParameter(p[3], p[2], qualifiers=quals, **args) def p_parameter_3(p): """parameter_3 : objectRef parameterName | objectRef parameterName array """ args = {} if len(p) == 4: args['is_array'] = True args['array_size'] = p[3] p[0] = CIMParameter(p[2], 'reference', reference_class=p[1], **args) def p_parameter_4(p): """parameter_4 : qualifierList objectRef parameterName | qualifierList objectRef parameterName array """ args = {} if len(p) == 5: args['is_array'] = True args['array_size'] = p[4] quals = dict([(x.name, x) for x in p[1]]) p[0] = CIMParameter(p[3], 'reference', qualifiers=quals, reference_class=p[2], **args) def p_parameterName(p): """parameterName : identifier""" p[0] = p[1] def p_array(p): """array : '[' ']' | '[' integerValue ']' """ if len(p) == 3: p[0] = None else: p[0] = p[2] def p_defaultValue(p): """defaultValue : '=' initializer""" p[0] = p[2] def p_initializer(p): """initializer : constantValue | arrayInitializer | referenceInitializer """ p[0] = p[1] def p_arrayInitializer(p): """arrayInitializer : '{' constantValueList '}' | '{' '}' """ if len(p) == 3: p[0] = [] else: p[0] = p[2] def p_constantValueList(p): """constantValueList : constantValue | constantValueList ',' constantValue """ if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[3]] def _fixStringValue(s): s = s[1:-1] rv = '' esc = False i = -1 while i < len(s) -1: i+= 1 ch = s[i] if ch == '\\' and not esc: esc = True continue if not esc: rv+= ch continue if ch == '"' : rv+= '"' elif ch == 'n' : rv+= '\n' elif ch == 't' : rv+= '\t' elif ch == 'b' : rv+= '\b' elif ch == 'f' : rv+= '\f' elif ch == 'r' : rv+= '\r' elif ch == '\\': rv+= '\\' elif ch in ['x','X']: hexc = 0 j = 0 i+= 1 while j < 4: c = s[i+j]; c = c.upper() if not c.isdigit() and not c in 'ABCDEF': break; hexc <<= 4 if c.isdigit(): hexc |= ord(c) - ord('0') else: hexc |= ord(c) - ord('A') + 0XA j+= 1 rv+= chr(hexc) i+= j-1 esc = False return rv def p_stringValueList(p): """stringValueList : stringValue | stringValueList stringValue """ if len(p) == 2: p[0] = _fixStringValue(p[1]) else: p[0] = p[1] + _fixStringValue(p[2]) def p_constantValue(p): """constantValue : integerValue | floatValue | charValue | stringValueList | booleanValue | nullValue """ p[0] = p[1] def p_integerValue(p): """integerValue : binaryValue | octalValue | decimalValue | hexValue """ p[0] = int(p[1]) # TODO deal with non-decimal values. def p_referenceInitializer(p): """referenceInitializer : objectHandle | aliasIdentifier """ if p[1][0] == '$': try: p[0] = p.parser.aliases[p[1]] except KeyError: ce = CIMError(CIM_ERR_FAILED, 'Unknown alias: ' + p[0]) ce.file_line = (p.parser.file, p.lexer.lineno) raise ce else: p[0] = p[1] def p_objectHandle(p): """objectHandle : identifier""" p[0] = p[1] def p_qualifierDeclaration(p): """qualifierDeclaration : QUALIFIER qualifierName qualifierType scope ';' | QUALIFIER qualifierName qualifierType scope defaultFlavor ';' """ qualtype = p[3] dt, is_array, array_size, value = qualtype qualname = p[2] scopes = p[4] if len(p) == 5: flist = [] else: flist = p[5] flavors = _build_flavors(flist) p[0] = CIMQualifierDeclaration(qualname, dt, value=value, is_array=is_array, array_size=array_size, scopes=scopes, **flavors) def _build_flavors(flist, qualdecl=None): flavors = {} if qualdecl is not None: flavors = {'overridable':qualdecl.overridable, 'translatable':qualdecl.translatable, 'toinstance':qualdecl.toinstance, 'tosubclass':qualdecl.tosubclass} if 'disableoverride' in flist: flavors['overridable'] = False if 'enableoverride' in flist: flavors['overridable'] = True if 'translatable' in flist: flavors['translatable'] = True if 'restricted' in flist: flavors['tosubclass'] = False if 'tosubclass' in flist: flavors['tosubclass'] = True try: if flavors['tosubclass']: flavors['toinstance'] = True except KeyError: pass return flavors def p_qualifierName(p): """qualifierName : identifier | ASSOCIATION | INDICATION """ p[0] = p[1] def p_qualifierType(p): """qualifierType : qualifierType_1 | qualifierType_2 """ p[0] = p[1] def p_qualifierType_1(p): """qualifierType_1 : ':' dataType array | ':' dataType array defaultValue """ dv = None if len(p) == 5: dv = p[4] p[0] = (p[2], True, p[3], dv) def p_qualifierType_2(p): """qualifierType_2 : ':' dataType | ':' dataType defaultValue """ dv = None if len(p) == 4: dv = p[3] p[0] = (p[2], False, None, dv) def p_scope(p): """scope : ',' SCOPE '(' metaElementList ')'""" slist = p[4] scopes = {} for i in ('SCHEMA', 'CLASS', 'ASSOCIATION', 'INDICATION', 'QUALIFIER', 'PROPERTY', 'REFERENCE', 'METHOD', 'PARAMETER', 'ANY'): scopes[i] = i in slist p[0] = scopes def p_metaElementList(p): """metaElementList : metaElement | metaElementList ',' metaElement """ if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[3]] def p_metaElement(p): """metaElement : SCHEMA | CLASS | ASSOCIATION | INDICATION | QUALIFIER | PROPERTY | REFERENCE | METHOD | PARAMETER | ANY """ p[0] = p[1].upper() def p_defaultFlavor(p): """defaultFlavor : ',' FLAVOR '(' flavorListWithComma ')'""" flist = p[4] flavors = {'ENABLEOVERRIDE':True, 'TOSUBCLASS':True, 'DISABLEOVERRIDE':False, 'RESTRICTED':False, 'TRANSLATABLE':False} for i in flist: flavors[i] = True p[0] = flavors def p_flavorListWithComma(p): """flavorListWithComma : flavor | flavorListWithComma ',' flavor """ if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[3]] def p_instanceDeclaration(p): """instanceDeclaration : INSTANCE OF className '{' valueInitializerList '}' ';' | INSTANCE OF className alias '{' valueInitializerList '}' ';' | qualifierList INSTANCE OF className '{' valueInitializerList '}' ';' | qualifierList INSTANCE OF className alias '{' valueInitializerList '}' ';' """ alias = None quals = {} ns = p.parser.handle.default_namespace if isinstance(p[1], basestring): # no qualifiers cname = p[3] if p[4] == '{': props = p[5] else: props = p[6] alias = p[4] else: cname = p[4] #quals = p[1] # qualifiers on instances are deprecated -- rightly so. if p[5] == '{': props = p[6] else: props = p[7] alias = p[5] try: cc = p.parser.handle.GetClass(cname, LocalOnly=False, IncludeQualifiers=True) p.parser.classnames[ns].append(cc.classname.lower()) except CIMError, ce: ce.file_line = (p.parser.file, p.lexer.lineno) if ce.args[0] == CIM_ERR_NOT_FOUND: file_ = p.parser.mofcomp.find_mof(cname) if p.parser.verbose: p.parser.log('Class %s does not exist' % cname) if file_: p.parser.mofcomp.compile_file(file_, ns) cc = p.parser.handle.GetClass(cname, LocalOnly=False, IncludeQualifiers=True) else: if p.parser.verbose: p.parser.log("Can't find file to satisfy class") ce = CIMError(CIM_ERR_INVALID_CLASS, cname) ce.file_line = (p.parser.file, p.lexer.lineno) raise ce else: raise path = CIMInstanceName(cname, namespace=ns) inst = CIMInstance(cname, properties=cc.properties, qualifiers=quals, path=path) for prop in props: pname = prop[1] pval = prop[2] try: cprop = inst.properties[pname] cprop.value = tocimobj(cprop.type, pval) except KeyError: ce = CIMError(CIM_ERR_INVALID_PARAMETER, 'Invalid property: %s' % pname) ce.file_line = (p.parser.file, p.lexer.lineno) raise ce except ValueError, ve: ce = CIMError(CIM_ERR_INVALID_PARAMETER, 'Invalid value for property: %s: %s' % (pname,ve.message)) ce.file_line = (p.parser.file, p.lexer.lineno) raise ce for prop in inst.properties.values(): if 'key' not in prop.qualifiers or not prop.qualifiers['key']: continue if prop.value is None: ce = CIMError(CIM_ERR_FAILED, 'Key property %s.%s is not set' % (cname, prop.name)) ce.file_line = (p.parser.file, p.lexer.lineno) raise ce inst.path.keybindings[prop.name] = prop.value if alias: p.parser.aliases[alias] = inst.path p[0] = inst def p_valueInitializerList(p): """valueInitializerList : valueInitializer | valueInitializerList valueInitializer """ if len(p) == 2: p[0] = [p[1]] else: p[0] = p[1] + [p[2]] def p_valueInitializer(p): """valueInitializer : identifier defaultValue ';' | qualifierList identifier defaultValue ';' """ if len(p) == 4: id_ = p[1] val = p[2] quals = [] else: quals = p[1] id_ = p[2] val = p[3] p[0] = (quals, id_, val) def p_booleanValue(p): """booleanValue : FALSE | TRUE """ p[0] = p[1].lower() == 'true' def p_nullValue(p): """nullValue : NULL""" p[0] = None def p_identifier(p): """identifier : IDENTIFIER | ANY | AS | CLASS | DISABLEOVERRIDE | dataType | ENABLEOVERRIDE | FLAVOR | INSTANCE | METHOD | OF | PARAMETER | PRAGMA | PROPERTY | QUALIFIER | REFERENCE | RESTRICTED | SCHEMA | SCOPE | TOSUBCLASS | TRANSLATABLE """ #| ASSOCIATION #| INDICATION p[0] = p[1] def p_empty(p): 'empty :' pass def _find_column(input, token): i = token.lexpos while i > 0: if input[i] == '\n': break i-= 1 column = (token.lexpos - i)+1 return column def _get_error_context(input, token): try: line = input[token.lexpos : input.index('\n', token.lexpos)] except ValueError: line = input[token.lexpos:] i = input.rfind('\n', 0, token.lexpos) if i < 0: i = 0 line = input[i:token.lexpos] + line lines = [line.strip('\r\n')] col = token.lexpos - i while len(lines) < 5 and i > 0: end = i i = input.rfind('\n', 0, i) if i < 0: i = 0 lines.insert(0, input[i:end].strip('\r\n')) pointer = '' for ch in token.value: pointer+= '^' pointline = '' i = 0 while i < col -1: if lines[-1][i].isspace(): pointline+= lines[-1][i] # otherwise, tabs complicate the alignment else: pointline+= ' ' i+= 1 lines.append(pointline + pointer) return lines def _print_logger(str): print str class MOFWBEMConnection(object): def __init__(self, conn=None): self.conn = conn self.class_names = {} self.qualifiers = {} self.instances = {} self.classes = {} if conn is None: self.__default_namespace = 'root/cimv2' def setns(self, value): if self.conn is not None: self.conn.default_namespace = value else: self.__default_namespace = value def getns(self): if self.conn is not None: return self.conn.default_namespace else: return self.__default_namespace default_namespace = property(getns, setns, None, "default_namespace property") def GetClass(self, *args, **kwargs): cname = len(args) > 0 and args[0] or kwargs['ClassName'] try: cc = self.classes[self.default_namespace][cname] except KeyError: if self.conn is None: ce = CIMError(CIM_ERR_NOT_FOUND, cname) raise ce cc = self.conn.GetClass(*args, **kwargs) try: self.classes[self.default_namespace][cc.classname] = cc except KeyError: self.classes[self.default_namespace] = \ NocaseDict({cc.classname:cc}) if 'LocalOnly' in kwargs and not kwargs['LocalOnly']: if cc.superclass: try: del kwargs['ClassName'] except KeyError: pass if len(args) > 0: args = args[1:] super_ = self.GetClass(cc.superclass, *args, **kwargs) for prop in super_.properties.values(): if prop.name not in cc.properties: cc.properties[prop.name] = prop for meth in super_.methods.values(): if meth.name not in cc.methods: cc.methods[meth.name] = meth return cc def CreateClass(self, *args, **kwargs): cc = len(args) > 0 and args[0] or kwargs['NewClass'] if cc.superclass: try: super_ = self.GetClass(cc.superclass, LocalOnly=True, IncludeQualifiers=False) except CIMError, ce: if ce.args[0] == CIM_ERR_NOT_FOUND: ce.args = (CIM_ERR_INVALID_SUPERCLASS, cc.superclass) raise else: raise try: self.classes[self.default_namespace][cc.classname] = cc except KeyError: self.classes[self.default_namespace] = \ NocaseDict({cc.classname:cc}) # TODO: should we see if it exists first with # self.conn.GetClass()? Do we want to create a class # that already existed? try: self.class_names[self.default_namespace].append(cc.classname) except KeyError: self.class_names[self.default_namespace] = [cc.classname] def ModifyClass(self, *args, **kwargs): raise CIMError(CIM_ERR_FAILED, 'This should not happen!') def ModifyInstance(self, *args, **kwargs): raise CIMError(CIM_ERR_FAILED, 'This should not happen!') def GetQualifier(self, *args, **kwargs): qualname = len(args) > 0 and args[0] or kwargs['QualifierName'] try: qual = self.qualifiers[self.default_namespace][qualname] except KeyError: if self.conn is None: raise CIMError(CIM_ERR_NOT_FOUND, qualname) qual = self.conn.GetQualifier(*args, **kwargs) return qual def SetQualifier(self, *args, **kwargs): qual = len(args) > 0 and args[0] or kwargs['QualifierDeclaration'] try: self.qualifiers[self.default_namespace][qual.name] = qual except KeyError: self.qualifiers[self.default_namespace] = \ NocaseDict({qual.name:qual}) def EnumerateQualifiers(self, *args, **kwargs): if self.conn is not None: rv = self.conn.EnumerateQualifiers(*args, **kwargs) else: rv = [] try: rv+= self.qualifiers[self.default_namespace].values() except KeyError: pass return rv def CreateInstance(self, *args, **kwargs): inst = len(args) > 0 and args[0] or kwargs['NewInstance'] try: self.instances[self.default_namespace].append(inst) except KeyError: self.instances[self.default_namespace] = [inst] return inst.path def rollback(self, verbose=False): for ns, insts in self.instances.items(): insts.reverse() for inst in insts: try: if verbose: print 'Deleting instance', inst.path self.conn.DeleteInstance(inst.path) except CIMError, ce: print 'Error deleting instance', inst.path print ' ', '%s %s' % (ce.args[0], ce.args[1]) for ns, cnames in self.class_names.items(): self.default_namespace = ns cnames.reverse() for cname in cnames: try: if verbose: print 'Deleting class %s:%s' % (ns, cname) self.conn.DeleteClass(cname) except CIMError, ce: print 'Error deleting class %s:%s' % (ns, cname) print ' ', '%s %s' % (ce.args[0], ce.args[1]) # TODO: do we want to do anything with qualifiers? def _errcode2string(code): d = { CIM_ERR_FAILED : 'A general error occurred', CIM_ERR_ACCESS_DENIED : 'Resource not available', CIM_ERR_INVALID_NAMESPACE : 'The target namespace does not exist', CIM_ERR_INVALID_PARAMETER : 'Parameter value(s) invalid', CIM_ERR_INVALID_CLASS : 'The specified Class does not exist', CIM_ERR_NOT_FOUND : 'Requested object could not be found', CIM_ERR_NOT_SUPPORTED : 'Operation not supported', CIM_ERR_CLASS_HAS_CHILDREN : 'Class has subclasses', CIM_ERR_CLASS_HAS_INSTANCES : 'Class has instances', CIM_ERR_INVALID_SUPERCLASS : 'Superclass does not exist', CIM_ERR_ALREADY_EXISTS : 'Object already exists', CIM_ERR_NO_SUCH_PROPERTY : 'Property does not exist', CIM_ERR_TYPE_MISMATCH : 'Value incompatible with type', CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED : 'Query language not supported', CIM_ERR_INVALID_QUERY : 'Query not valid', CIM_ERR_METHOD_NOT_AVAILABLE : 'Extrinsic method not executed', CIM_ERR_METHOD_NOT_FOUND : 'Extrinsic method does not exist', } try: s = d[code] except KeyError: s = 'Unknown Error' return s class MOFCompiler(object): def __init__(self, handle, search_paths=[], verbose=False, log_func=_print_logger): """Initialize the compiler. Keyword arguments: handle -- A WBEMConnection or similar object. The following attributes and methods need to be present, corresponding to the the attributes and methods on pywbem.WBEMConnection having the same names: - default_namespace - EnumerateInstanceNames() - CreateClass() - GetClass() - ModifyClass() - DeleteInstance() - CreateInstance() - ModifyInstance() - DeleteQualifier() - EnumerateQualifiers() - SetQualifier() search_paths -- A list of file system paths specifying where missing schema elements should be looked for. verbose -- True if extra messages should be printed. log_func -- A callable that takes a single string argument. The default logger prints to stdout. """ self.parser = yacc.yacc(tabmodule=_tabmodule, optimize=_optimize) self.parser.search_paths = search_paths self.handle = handle self.parser.handle = handle self.lexer = lex.lex(lextab=_lextab, optimize=_optimize) self.lexer.parser = self.parser self.parser.qualcache = {handle.default_namespace:NocaseDict()} self.parser.classnames = {handle.default_namespace:[]} self.parser.mofcomp = self self.parser.verbose = verbose self.parser.log = log_func self.parser.aliases = {} def compile_string(self, mof, ns, filename=None): """Compile a string of MOF. Arguments: mof -- The string of MOF ns -- The CIM namespace Keyword arguments: filename -- The name of the file that the MOF was read from. This is used in status and error messages. """ lexer = self.lexer.clone() lexer.parser = self.parser try: oldfile = self.parser.file except AttributeError: oldfile = None self.parser.file = filename try: oldmof = self.parser.mof except AttributeError: oldmof = None self.parser.mof = mof self.parser.handle.default_namespace = ns if ns not in self.parser.qualcache: self.parser.qualcache[ns] = NocaseDict() if ns not in self.parser.classnames: self.parser.classnames[ns] = [] try: rv = self.parser.parse(mof, lexer=lexer) self.parser.file = oldfile self.parser.mof = oldmof return rv except MOFParseError, pe: self.parser.log('Syntax error:') if hasattr(pe, 'file') and hasattr(pe, 'lineno'): self.parser.log('%s:%s:' % (pe.file, pe.lineno)) if hasattr(pe, 'context'): self.parser.log('\n'.join(pe.context)) if str(pe): self.parser.log(str(pe)) raise except CIMError, ce: if hasattr(ce, 'file_line'): self.parser.log('Fatal Error: %s:%s' % (ce.file_line[0], ce.file_line[1])) else: self.parser.log('Fatal Error:') self.parser.log('%s%s' % (_errcode2string(ce.args[0]), ce.args[1] and ': '+ce.args[1] or '')) raise def compile_file(self, filename, ns): """Compile MOF from a file. Arguments: filename -- The file to read MOF from ns -- The CIM namespace """ if self.parser.verbose: self.parser.log('Compiling file ' + filename) f = open(filename, 'r') mof = f.read() f.close() return self.compile_string(mof, ns, filename=filename) def find_mof(self, classname): """Find a MOF file corresponding to a CIM class name. The search_paths provided to __init__() are searched recursively. Arguments: classname -- The name of the class to look for """ classname = classname.lower() for search in self.parser.search_paths: for root, dirs, files in os.walk(search): for file_ in files: if file_.endswith('.mof') and \ file_[:-4].lower() == classname: return root + '/' + file_ return None def rollback(self, verbose=False): self.handle.rollback(verbose=verbose) def _build(): yacc.yacc(optimize=_optimize, tabmodule=_tabmodule) lex.lex(optimize=_optimize, lextab=_lextab) if __name__ == '__main__': from optparse import OptionParser usage = 'usage: %prog -n [options] ...' oparser = OptionParser(usage=usage) oparser.add_option('-s', '--search', dest='search', help='Search path to find missing schema elements. This option can be present multiple times.', metavar='Path', action='append') oparser.add_option('-n', '--namespace', dest='ns', help='Specify the namespace', metavar='Namespace') oparser.add_option('-u', '--url', dest='url', help='URL to the CIM Server', metavar='URL', default='/var/run/tog-pegasus/cimxml.socket') oparser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='Print more messages to stdout') oparser.add_option('-r', '--remove', action='store_true', dest='remove', default=False, help='Remove elements found in MOF, instead of create them') oparser.add_option('-l', '--username', dest='username', metavar='Username', help='Specify the username') oparser.add_option('-p', '--password', dest='password', metavar='Password', help='Specify the password') (options, args) = oparser.parse_args() search = options.search if not args: oparser.error('No input files given for parsing') if options.ns is None: oparser.error('No namespace given') passwd = options.password if options.username and not passwd: passwd = getpass('Enter password for %s: ' % options.username) if options.username: conn = WBEMConnection(options.url, (options.username, passwd)) else: conn = WBEMConnection(options.url) if options.remove: conn = MOFWBEMConnection(conn=conn) #conn.debug = True conn.default_namespace = options.ns if search is None: search = [] search = [os.path.abspath(x) for x in search] for fname in args: path = os.path.abspath(os.path.dirname(fname)) for spath in search: if path.startswith(spath): break else: search.append(path) # if removing, we'll be verbose later when we actually remove stuff. # We don't want MOFCompiler to be verbose, as that would be confusing. verbose = options.verbose and not options.remove mofcomp = MOFCompiler(handle=conn, search_paths=search, verbose=verbose) try: for fname in args: if fname[0] != '/': fname = os.path.curdir + '/' + fname mofcomp.compile_file(fname, options.ns) except MOFParseError, pe: sys.exit(1) except CIMError, ce: sys.exit(1) if options.remove: conn.rollback(verbose=options.verbose)