diff options
Diffstat (limited to 'qpid/python/qmf/qmfCommon.py')
-rw-r--r-- | qpid/python/qmf/qmfCommon.py | 1792 |
1 files changed, 1792 insertions, 0 deletions
diff --git a/qpid/python/qmf/qmfCommon.py b/qpid/python/qmf/qmfCommon.py new file mode 100644 index 0000000000..66162a18bb --- /dev/null +++ b/qpid/python/qmf/qmfCommon.py @@ -0,0 +1,1792 @@ + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +import sys +import socket +import os +import time +import logging +from threading import Lock +from threading import Condition +try: + import hashlib + _md5Obj = hashlib.md5 +except ImportError: + import md5 + _md5Obj = md5.new + + + + +## +## Constants +## + +AMQP_QMF_TOPIC = "amq.topic" +AMQP_QMF_DIRECT = "amq.direct" +AMQP_QMF_NAME_SEPARATOR = "/" +AMQP_QMF_AGENT_LOCATE = "amq.topic/agent.locate" +AMQP_QMF_AGENT_INDICATION = "amq.topic/agent.ind" + + +##============================================================================== +## Agent Identification +##============================================================================== + +class AgentId(object): + """ + Uniquely identifies a management agent within the entire management domain. + + Map format: + map["vendor"] = str, name of vendor of the agent + map["product"] = str, name of product using agent + map["name"] = str, name of agent, unique within vendor and product. + """ + _separator = ":" + def __init__(self, vendor, product, name): + """ + Note: this object must be immutable, as it is used to index into a dictionary + """ + self._vendor = vendor + self._product = product + self._name = name + + def vendor(self): + return self._vendor + + def product(self): + return self._product + + def name(self): + return self._name + + def mapEncode(self): + _map = {} + _map["vendor"] = self._vendor + _map["product"] = self._product + _map["name"] = self._name + return _map + + def __cmp__(self, other): + if not isinstance(other, AgentId) : + raise TypeError("Invalid types for compare") + # return 1 + me = str(self) + them = str(other) + + if me < them: + return -1 + if me > them: + return 1 + return 0 + + def __hash__(self): + return (self._vendor, self._product, self._name).__hash__() + + def __repr__(self): + return self._vendor + AgentId._separator + \ + self._product + AgentId._separator + \ + self._name + + def __str__(self): + return self.__repr__() + + + +def AgentIdFactory( param ): + """ + Factory for constructing an AgentId class from various sources. + + @type param: various + @param param: object to use for constructing AgentId + @rtype: AgentId + @returns: a new AgentId instance + """ + if type(param) == str: + if param.count(AgentId._separator) < 2: + raise TypeError("AgentId string format must be 'vendor:product:name'") + vendor, product, name = param.split(AgentId._separator) + return AgentId(vendor, product, name) + if type(param) == dict: + # construct from map encoding + if not "vendor" in param: + raise TypeError("requires 'vendor' value") + if not "product" in param: + raise TypeError("requires 'product' value") + if not "name" in param: + raise TypeError("requires 'name' value") + return AgentId( param["vendor"], param["product"], param["name"] ) + raise TypeError("Invalid type for AgentId construction") + + + +##============================================================================== +## DATA MODEL +##============================================================================== + + + +class ObjectId(object): + """ + An instance of managed object must be uniquely identified within the + management system. Each managed object is given a key that is unique + within the domain of the object's managing Agent. Note that these + keys are not unique across Agents. Therefore, a globally unique name + for an instance of a managed object is the concatenation of the + object's key and the managing Agent's AgentId. + + Map format: + map["agent_id"] = map representation of AgentId + map["primary_key"] = str, key for managed object + """ + def __init__(self, agentid, name): + if not isinstance(agentid, AgentId): + raise TypeError("requires an AgentId class") + self._agentId = agentid; + self._name = name; + + def getAgentId(self): + """ + @rtype: class AgentId + @returns: Id of agent that manages the object. + """ + return self._agentId + + def getPrimaryKey(self): + """ + @rtype: str + @returns: Key of managed object. + """ + return self._name + + def mapEncode(self): + _map = {} + _map["agent_id"] = self._agentId.mapEncode() + _map["primary_key"] = self._name + return _map + + def __repr__(self): + return "%s:%s" % (self._agentId, self._name) + + def __cmp__(self, other): + if not isinstance(other, ObjectId) : + raise TypeError("Invalid types for compare") + + if self._agentId < other._agentId: + return -1 + if self._agentId > other._agentId: + return 1 + if self._name < other._name: + return -1 + if self._name > other._name: + return 1 + return 0 + + def __hash__(self): + return (hash(self._agentId), self._name).__hash__() + + +def ObjectIdFactory( param ): + """ + Factory for constructing ObjectIds from various sources + + @type param: various + @param param: object to use for constructing ObjectId + @rtype: ObjectId + @returns: a new ObjectId instance + """ + if type(param) == dict: + # construct from map + if "agent_id" not in param: + raise TypeError("requires 'agent_id' value") + if "primary_key" not in param: + raise TypeError("requires 'primary_key' value") + + return ObjectId( AgentIdFactory(param["agent_id"]), param["primary_key"] ) + + else: + raise TypeError("Invalid type for ObjectId construction") + + + + +class QmfData(object): + """ + Base data class representing arbitrarily structure data. No schema or + managing agent is associated with data of this class. + + Map format: + map["properties"] = map of unordered "name"=<value> pairs (optional) + """ + def __init__(self, _props={}, _const=False): + """ + @type _props: dict + @param _props: dictionary of initial name=value pairs for object's property data. + @type _const: boolean + @param _const: if true, this object cannot be modified + """ + self._properties = _props.copy() + self._const = _const + self._managed = False # not managed by an Agent + self._described = False # not described by a schema + + def isManaged(self): + return self._managed + + def isDescribed(self): + return self._described + + def getProperties(self): + return self._properties.copy() + + def getProperty(self, _name): + return self._properties[_name] + + def setProperty(self, _name, _value): + if self._const: + raise Exception("cannot modify constant data object") + self._properties[_name] = _value + return _value + + def mapEncode(self): + return self._properties.copy() + + def __repr__(self): + return str(self.mapEncode()) + + def __setattr__(self, _name, _value): + # ignore private data members + if _name[0] == '_': + return super.__setattr__(self, _name, _value) + # @todo: this is bad - what if the same name is used for a + # property and statistic or argument? + if _name in self._properties: + return self.setProperty(_name, _value) + return super.__setattr__(self, _name, _value) + + def __getattr__(self, _name): + # @todo: this is bad - what if the same name is used for a + # property and statistic or argument? + if _name in self._properties: return self.getProperty(_name) + raise AttributeError("no item named '%s' in this object" % _name) + + def __getitem__(self, _name): + return self.__getattr__(_name) + + def __setitem__(self, _name, _value): + return self.__setattr__(_name, _value) + + +def QmfDataFactory( param, const=False ): + """ + Factory for constructing an QmfData class from various sources. + + @type param: various + @param param: object to use for constructing QmfData instance + @rtype: QmfData + @returns: a new QmfData instance + """ + if type(param) == dict: + # construct from map + return QmfData( _props=param, _const=const ) + + else: + raise TypeError("Invalid type for QmfData construction") + + + +class QmfDescribed(QmfData): + """ + Data that has a formally defined structure is represented by the + QmfDescribed class. This class extends the QmfData class by + associating the data with a formal schema (SchemaObjectClass). + + Map format: + map["schema_id"] = map representation of a SchemaClassId instance + map["properties"] = map representation of a QmfData instance + """ + def __init__(self, _schema=None, _schemaId=None, _props={}, _const=False ): + """ + @type _schema: class SchemaClass or derivative + @param _schema: an instance of the schema used to describe this object. + @type _schemaId: class SchemaClassId + @param _schemaId: if schema instance not available, this is mandatory. + @type _props: dict + @param _props: dictionary of initial name=value pairs for object's property data. + @type _const: boolean + @param _const: if true, this object cannot be modified + """ + super(QmfDescribed, self).__init__(_props, _const) + self._validated = False + self._described = True + self._schema = _schema + if _schema: + self._schemaId = _schema.getClassId() + if self._const: + self._validate() + else: + if _schemaId: + self._schemaId = _schemaId + else: + raise Exception("A SchemaClass or SchemaClassId must be provided") + + + def getSchemaClassId(self): + """ + @rtype: class SchemaClassId + @returns: the identifier of the Schema that describes the structure of the data. + """ + return self._schemaId + + def setSchema(self, _schema): + """ + @type _schema: class SchemaClass or derivative + @param _schema: instance of schema used to describe the structure of the data. + """ + if self._schemaId != _schema.getClassId(): + raise Exception("Cannot reset current schema to a different schema") + oldSchema = self._schema + self._schema = _schema + if not oldSchema and self._const: + self._validate() + + def getPrimaryKey(self): + """ + Get a string composed of the object's primary key properties. + @rtype: str + @returns: a string composed from primary key property values. + """ + if not self._schema: + raise Exception("schema not available") + + if not self._validated: + self._validate() + + if self._schema._pkeyNames == 0: + if len(self._properties) != 1: + raise Exception("no primary key defined") + return str(self._properties.values()[0]) + + result = u"" + for pkey in self._schema._pkeyNames: + if result != u"": + result += u":" + try: + valstr = unicode(self._properties[pkey]) + except: + valstr = u"<undecodable>" + result += valstr + return result + + + def mapEncode(self): + _map = {} + _map["schema_id"] = self._schemaId.mapEncode() + _map["properties"] = super(QmfDescribed, self).mapEncode() + return _map + + def _validate(self): + """ + Compares this object's data against the associated schema. Throws an + exception if the data does not conform to the schema. + """ + for name,val in self._schema._properties.iteritems(): + # @todo validate: type compatible with amqp_type? + # @todo validate: primary keys have values + if name not in self._properties: + if val._isOptional: + # ok not to be present, put in dummy value + # to simplify access + self._properties[name] = None + else: + raise Exception("Required property '%s' not present." % name) + self._validated = True + + + +def QmfDescribedFactory( param, _schema=None ): + """ + Factory for constructing an QmfDescribed class from various sources. + + @type param: various + @param param: object to use for constructing QmfDescribed instance + @type _schema: SchemaClass + @param _schema: instance of the SchemaClass that describes this instance + @rtype: QmfDescribed + @returns: a new QmfDescribed instance + """ + if type(param) == dict: + # construct from map + if "schema_id" not in param: + raise TypeError("requires 'schema_id' value") + if "properties" not in param: + raise TypeError("requires 'properties' value") + + return QmfDescribed( _schema=_schema, _schemaId=SchemaClassIdFactory( param["schema_id"] ), + _props = param["properties"] ) + else: + raise TypeError("Invalid type for QmfDescribed construction") + + + +class QmfManaged(QmfDescribed): + """ + Data that has a formally defined structure, and for which each + instance of the data is managed by a particular Agent is represented + by the QmfManaged class. This class extends the QmfDescribed class by + associating the described data with a particular Agent. + + Map format: + map["object_id"] = map representation of an ObjectId value + map["schema_id"] = map representation of a SchemaClassId instance + map["qmf_data"] = map representation of a QmfData instance + map["timestamps"] = array of AMQP timestamps. [0] = time of last update, + [1] = creation timestamp, [2] = deletion timestamp or zero. + """ + _ts_update = 0 + _ts_create = 1 + _ts_delete = 2 + def __init__( self, _agentId=None, _schema=None, _schemaId=None, + _props={}, _const=False ): + """ + @type _agentId: class AgentId + @param _agentId: globally unique identifier of the managing agent. + @type _schema: class SchemaClass or derivative + @param _schema: an instance of the schema used to describe this object. + @type _schemaId: class SchemaClassId + @param _schemaId: if schema instance not available, this is mandatory. + @type _props: dict + @param _props: dictionary of initial name=value pairs for object's property data. + @type _const: boolean + @param _const: if true, this object cannot be modified + """ + super(QmfManaged, self).__init__(_schema=_schema, _schemaId=_schemaId, + _props=_props, _const=_const) + self._managed = True + self._agentId = _agentId + # timestamp, in millisec since epoch UTC + _ctime = long(time.time() * 1000) + self._timestamps = [_ctime,_ctime,0] + + + def getObjectId(self): + """ + @rtype: class ObjectId + @returns: the ObjectId that uniquely identifies this managed object. + """ + return ObjectId(self._agentId, self.getPrimaryKey()) + + def isDeleted(self): + return self._timestamps[QmfManaged._ts_delete] == 0 + + def mapEncode(self): + _map = super(QmfManaged, self).mapEncode() + _map["agent_id"] = self._agentId.mapEncode() + _map["timestamps"] = self._timestamps[:] + return _map + + + +def QmfManagedFactory( param, _schema=None ): + """ + Factory for constructing an QmfManaged instance from various sources. + + @type param: various + @param param: object to use for constructing QmfManaged instance + @type _schema: SchemaClass + @param _schema: instance of the SchemaClass that describes this instance + @rtype: QmfManaged + @returns: a new QmfManaged instance + """ + if type(param) == dict: + # construct from map + if "agent_id" not in param: + raise TypeError("requires 'agent_id' value") + _qd = QmfDescribedFactory( param, _schema ) + + return QmfManaged( _agentId = AgentIdFactory(param["agent_id"]), + _schema=_schema, _schemaId=_qd._schemaId, + _props = _qd._properties ) + else: + raise TypeError("Invalid type for QmfManaged construction") + + + +class QmfEvent(QmfDescribed): + """ + A QMF Event is a type of described data that is not managed. Events are + notifications that are sent by Agents. An event notifies a Console of a + change in some aspect of the system under managment. + """ + def __init__(self, _map=None, + _timestamp=None, _agentId=None, + _schema=None, _schemaId=None, + _props={}, _const=False): + """ + @type _map: dict + @param _map: if not None, construct instance from map representation. + @type _timestamp: int + @param _timestamp: moment in time when event occurred, expressed + as milliseconds since Midnight, Jan 1, 1970 UTC. + @type _agentId: class AgentId + @param _agentId: Identifies agent issuing this event. + @type _schema: class Schema + @param _schema: + @type _schemaId: class SchemaClassId (event) + @param _schemaId: identi + """ + if _map: + if type(_map) != dict: + raise TypeError("parameter '_map' must be of type 'dict'") + if "timestamp" not in _map: + pass + if "agent_id" not in _map: + pass + _qe = QmfDescribedFactory( _map, _schema ) + super(QmfEvent, self).__init__( _schema=_qe._schema, _schemaId=_qe._schemaId, + _props=_qe._properties, _const=_qe._const ) + self._timestamp = long(_map["timestamp"]) + self._agentId = AgentIdFactory(_map["agent_id"]) + else: + super(QmfEvent, self).__init__(_schema=_schema, _schemaId=_schemaId, + _props=_props, _const=_const) + self._timestamp = long(_timestamp) + self._agentId = _agentId + + def getTimestamp(self): return self._timestamp + + def getAgentId(self): return self._agentId + + def mapEncode(self): + _map = super(QmfEvent, self).mapEncode() + _map["timestamp"] = self._timestamp + _map["agent_id"] = self._agentId.mapEncode() + return _map + + + +#============================================================================== +#============================================================================== +#============================================================================== + + + +class QmfObject(object): + # attr_reader :impl, :object_class + def __init__(self, cls, kwargs={}): + pass +# self._cv = Condition() +# self._sync_count = 0 +# self._sync_result = None +# self._allow_sets = False +# if kwargs.has_key("broker"): +# self._broker = kwargs["broker"] +# else: +# self._broker = None +# if cls: +# self.object_class = cls +# self.impl = qmfengine.Object(self.object_class.impl) +# elif kwargs.has_key("impl"): +# self.impl = qmfengine.Object(kwargs["impl"]) +# self.object_class = SchemaObjectClass(None, +# None, +# {"impl":self.impl.getClass()}) +# else: +# raise Exception("Argument error: required parameter ('impl') not supplied") + + +# def destroy(self): +# self.impl.destroy() + + +# def object_id(self): +# return ObjectId(self.impl.getObjectId()) + + +# def set_object_id(self, oid): +# self.impl.setObjectId(oid.impl) + + +# def properties(self): +# list = [] +# for prop in self.object_class.properties: +# list.append([prop, self.get_attr(prop.name())]) +# return list + + +# def statistics(self): +# list = [] +# for stat in self.object_class.statistics: +# list.append([stat, self.get_attr(stat.name())]) +# return list + + +# def get_attr(self, name): +# val = self._value(name) +# vType = val.getType() +# if vType == TYPE_UINT8: return val.asUint() +# elif vType == TYPE_UINT16: return val.asUint() +# elif vType == TYPE_UINT32: return val.asUint() +# elif vType == TYPE_UINT64: return val.asUint64() +# elif vType == TYPE_SSTR: return val.asString() +# elif vType == TYPE_LSTR: return val.asString() +# elif vType == TYPE_ABSTIME: return val.asInt64() +# elif vType == TYPE_DELTATIME: return val.asUint64() +# elif vType == TYPE_REF: return ObjectId(val.asObjectId()) +# elif vType == TYPE_BOOL: return val.asBool() +# elif vType == TYPE_FLOAT: return val.asFloat() +# elif vType == TYPE_DOUBLE: return val.asDouble() +# elif vType == TYPE_UUID: return val.asUuid() +# elif vType == TYPE_INT8: return val.asInt() +# elif vType == TYPE_INT16: return val.asInt() +# elif vType == TYPE_INT32: return val.asInt() +# elif vType == TYPE_INT64: return val.asInt64() +# else: +# # when TYPE_MAP +# # when TYPE_OBJECT +# # when TYPE_LIST +# # when TYPE_ARRAY +# logging.error( "Unsupported type for get_attr? '%s'" % str(val.getType()) ) +# return None + + +# def set_attr(self, name, v): +# val = self._value(name) +# vType = val.getType() +# if vType == TYPE_UINT8: return val.setUint(v) +# elif vType == TYPE_UINT16: return val.setUint(v) +# elif vType == TYPE_UINT32: return val.setUint(v) +# elif vType == TYPE_UINT64: return val.setUint64(v) +# elif vType == TYPE_SSTR: +# if v: return val.setString(v) +# else: return val.setString('') +# elif vType == TYPE_LSTR: +# if v: return val.setString(v) +# else: return val.setString('') +# elif vType == TYPE_ABSTIME: return val.setInt64(v) +# elif vType == TYPE_DELTATIME: return val.setUint64(v) +# elif vType == TYPE_REF: return val.setObjectId(v.impl) +# elif vType == TYPE_BOOL: return val.setBool(v) +# elif vType == TYPE_FLOAT: return val.setFloat(v) +# elif vType == TYPE_DOUBLE: return val.setDouble(v) +# elif vType == TYPE_UUID: return val.setUuid(v) +# elif vType == TYPE_INT8: return val.setInt(v) +# elif vType == TYPE_INT16: return val.setInt(v) +# elif vType == TYPE_INT32: return val.setInt(v) +# elif vType == TYPE_INT64: return val.setInt64(v) +# else: +# # when TYPE_MAP +# # when TYPE_OBJECT +# # when TYPE_LIST +# # when TYPE_ARRAY +# logging.error("Unsupported type for get_attr? '%s'" % str(val.getType())) +# return None + + +# def __getitem__(self, name): +# return self.get_attr(name) + + +# def __setitem__(self, name, value): +# self.set_attr(name, value) + + +# def inc_attr(self, name, by=1): +# self.set_attr(name, self.get_attr(name) + by) + + +# def dec_attr(self, name, by=1): +# self.set_attr(name, self.get_attr(name) - by) + + + + +# def _invokeMethod(self, name, argMap): +# """ +# Private: Helper function that invokes an object's method, and waits for the result. +# """ +# self._cv.acquire() +# try: +# timeout = 30 +# self._sync_count = 1 +# self.impl.invokeMethod(name, argMap, self) +# if self._broker: +# self._broker.conn.kick() +# self._cv.wait(timeout) +# if self._sync_count == 1: +# raise Exception("Timed out: waiting for response to method call.") +# finally: +# self._cv.release() + +# return self._sync_result + + +# def _method_result(self, result): +# """ +# Called to return the result of a method call on an object +# """ +# self._cv.acquire(); +# try: +# self._sync_result = result +# self._sync_count -= 1 +# self._cv.notify() +# finally: +# self._cv.release() + + +# def _marshall(schema, args): +# ''' +# Private: Convert a list of arguments (positional) into a Value object of type "map". +# Used to create the argument parameter for an object's method invokation. +# ''' +# # Build a map of the method's arguments +# map = qmfengine.Value(TYPE_MAP) +# for arg in schema.arguments: +# if arg.direction == DIR_IN or arg.direction == DIR_IN_OUT: +# map.insert(arg.name, qmfengine.Value(arg.typecode)) + +# # install each argument's value into the map +# marshalled = Arguments(map) +# idx = 0 +# for arg in schema.arguments: +# if arg.direction == DIR_IN or arg.direction == DIR_IN_OUT: +# if args[idx]: +# marshalled[arg.name] = args[idx] +# idx += 1 + +# return marshalled.map + + +# def _value(self, name): +# val = self.impl.getValue(name) +# if not val: +# raise Exception("Argument error: attribute named '%s' not defined for package %s, class %s" % +# (name, +# self.object_class.impl.getClassKey().getPackageName(), +# self.object_class.impl.getClassKey().getClassName())) +# return val + + + + + +class Arguments(object): + def __init__(self, map): + pass +# self.map = map +# self._by_hash = {} +# key_count = self.map.keyCount() +# a = 0 +# while a < key_count: +# self._by_hash[self.map.key(a)] = self.by_key(self.map.key(a)) +# a += 1 + + +# def __getitem__(self, key): +# return self._by_hash[key] + + +# def __setitem__(self, key, value): +# self._by_hash[key] = value +# self.set(key, value) + + +# def __iter__(self): +# return self._by_hash.__iter__ + + +# def __getattr__(self, name): +# if name in self._by_hash: +# return self._by_hash[name] +# return super.__getattr__(self, name) + + +# def __setattr__(self, name, value): +# # +# # ignore local data members +# # +# if (name[0] == '_' or +# name == 'map'): +# return super.__setattr__(self, name, value) + +# if name in self._by_hash: +# self._by_hash[name] = value +# return self.set(name, value) + +# return super.__setattr__(self, name, value) + + +# def by_key(self, key): +# val = self.map.byKey(key) +# vType = val.getType() +# if vType == TYPE_UINT8: return val.asUint() +# elif vType == TYPE_UINT16: return val.asUint() +# elif vType == TYPE_UINT32: return val.asUint() +# elif vType == TYPE_UINT64: return val.asUint64() +# elif vType == TYPE_SSTR: return val.asString() +# elif vType == TYPE_LSTR: return val.asString() +# elif vType == TYPE_ABSTIME: return val.asInt64() +# elif vType == TYPE_DELTATIME: return val.asUint64() +# elif vType == TYPE_REF: return ObjectId(val.asObjectId()) +# elif vType == TYPE_BOOL: return val.asBool() +# elif vType == TYPE_FLOAT: return val.asFloat() +# elif vType == TYPE_DOUBLE: return val.asDouble() +# elif vType == TYPE_UUID: return val.asUuid() +# elif vType == TYPE_INT8: return val.asInt() +# elif vType == TYPE_INT16: return val.asInt() +# elif vType == TYPE_INT32: return val.asInt() +# elif vType == TYPE_INT64: return val.asInt64() +# else: +# # when TYPE_MAP +# # when TYPE_OBJECT +# # when TYPE_LIST +# # when TYPE_ARRAY +# logging.error( "Unsupported Type for Get? '%s'" % str(val.getType())) +# return None + + +# def set(self, key, value): +# val = self.map.byKey(key) +# vType = val.getType() +# if vType == TYPE_UINT8: return val.setUint(value) +# elif vType == TYPE_UINT16: return val.setUint(value) +# elif vType == TYPE_UINT32: return val.setUint(value) +# elif vType == TYPE_UINT64: return val.setUint64(value) +# elif vType == TYPE_SSTR: +# if value: +# return val.setString(value) +# else: +# return val.setString('') +# elif vType == TYPE_LSTR: +# if value: +# return val.setString(value) +# else: +# return val.setString('') +# elif vType == TYPE_ABSTIME: return val.setInt64(value) +# elif vType == TYPE_DELTATIME: return val.setUint64(value) +# elif vType == TYPE_REF: return val.setObjectId(value.impl) +# elif vType == TYPE_BOOL: return val.setBool(value) +# elif vType == TYPE_FLOAT: return val.setFloat(value) +# elif vType == TYPE_DOUBLE: return val.setDouble(value) +# elif vType == TYPE_UUID: return val.setUuid(value) +# elif vType == TYPE_INT8: return val.setInt(value) +# elif vType == TYPE_INT16: return val.setInt(value) +# elif vType == TYPE_INT32: return val.setInt(value) +# elif vType == TYPE_INT64: return val.setInt64(value) +# else: +# # when TYPE_MAP +# # when TYPE_OBJECT +# # when TYPE_LIST +# # when TYPE_ARRAY +# logging.error("Unsupported Type for Set? '%s'" % str(val.getType())) +# return None + + + +class MethodResponse(object): + def __init__(self, impl): + pass +# self.impl = qmfengine.MethodResponse(impl) + + +# def status(self): +# return self.impl.getStatus() + + +# def exception(self): +# return self.impl.getException() + + +# def text(self): +# return exception().asString() + + +# def args(self): +# return Arguments(self.impl.getArgs()) + + +# def __getattr__(self, name): +# myArgs = self.args() +# return myArgs.__getattr__(name) + + +# def __setattr__(self, name, value): +# if name == 'impl': +# return super.__setattr__(self, name, value) + +# myArgs = self.args() +# return myArgs.__setattr__(name, value) + + + +# ##============================================================================== +# ## QUERY +# ##============================================================================== + + +class Query: + def __init__(self, kwargs={}): + pass +# if "impl" in kwargs: +# self.impl = kwargs["impl"] +# else: +# package = '' +# if "key" in kwargs: +# # construct using SchemaClassKey: +# self.impl = qmfengine.Query(kwargs["key"]) +# elif "object_id" in kwargs: +# self.impl = qmfengine.Query(kwargs["object_id"].impl) +# else: +# if "package" in kwargs: +# package = kwargs["package"] +# if "class" in kwargs: +# self.impl = qmfengine.Query(kwargs["class"], package) +# else: +# raise Exception("Argument error: invalid arguments, use 'key', 'object_id' or 'class'[,'package']") + + +# def package_name(self): return self.impl.getPackage() +# def class_name(self): return self.impl.getClass() +# def object_id(self): +# _objid = self.impl.getObjectId() +# if _objid: +# return ObjectId(_objid) +# else: +# return None + + +##============================================================================== +## SCHEMA +##============================================================================== + +# known schema types + +SchemaTypeData = "data" +SchemaTypeEvent = "event" + +# format convention for schema hash + +_schemaHashStrFormat = "%08x-%08x-%08x-%08x" +_schemaHashStrDefault = "00000000-00000000-00000000-00000000" + +# Argument typecodes, access, and direction qualifiers + +class qmfTypes(object): + TYPE_UINT8 = 1 + TYPE_UINT16 = 2 + TYPE_UINT32 = 3 + TYPE_UINT64 = 4 + TYPE_SSTR = 6 + TYPE_LSTR = 7 + TYPE_ABSTIME = 8 + TYPE_DELTATIME = 9 + TYPE_REF = 10 + TYPE_BOOL = 11 + TYPE_FLOAT = 12 + TYPE_DOUBLE = 13 + TYPE_UUID = 14 + TYPE_MAP = 15 + TYPE_INT8 = 16 + TYPE_INT16 = 17 + TYPE_INT32 = 18 + TYPE_INT64 = 19 + TYPE_OBJECT = 20 + TYPE_LIST = 21 + TYPE_ARRAY = 22 + + +class qmfAccess(object): + READ_CREATE = 1 + READ_WRITE = 2 + READ_ONLY = 3 + + +class qmfDirection(object): + DIR_IN = 1 + DIR_OUT = 2 + DIR_IN_OUT = 3 + + + +def _toBool( param ): + """ + Helper routine to convert human-readable representations of + boolean values to python bool types. + """ + _false_strings = ["off", "no", "false", "0", "none"] + _true_strings = ["on", "yes", "true", "1"] + if type(param) == str: + lparam = param.lower() + if lparam in _false_strings: + return False + if lparam in _true_strings: + return True + raise TypeError("unrecognized boolean string: '%s'" % param ) + else: + return bool(param) + + + +class SchemaClassId(object): + """ + Unique identifier for an instance of a SchemaClass. + + Map format: + map["package_name"] = str, name of associated package + map["class_name"] = str, name of associated class + map["type"] = str, "data"|"event", default: "data" + optional: + map["hash_str"] = str, hash value in standard format or None + if hash is unknown. + """ + def __init__(self, pname, cname, stype=SchemaTypeData, hstr=None): + """ + @type pname: str + @param pname: the name of the class's package + @type cname: str + @param cname: name of the class + @type stype: str + @param stype: schema type [SchemaTypeData | SchemaTypeEvent] + @type hstr: str + @param hstr: the hash value in '%08x-%08x-%08x-%08x' format + """ + self._pname = pname + self._cname = cname + if stype != SchemaTypeData and stype != SchemaTypeEvent: + raise TypeError("Invalid SchemaClassId type: '%s'" % stype) + self._type = stype + self._hstr = hstr + if self._hstr: + try: + # sanity check the format of the hash string + hexValues = hstr.split("-") + h0 = int(hexValues[0], 16) + h1 = int(hexValues[1], 16) + h2 = int(hexValues[2], 16) + h3 = int(hexValues[3], 16) + except: + raise Exception("Invalid SchemaClassId format: bad hash string: '%s':" + % hstr) + + def getPackageName(self): + """ + Access the package name in the SchemaClassId. + + @rtype: str + """ + return self._pname + + + def getClassName(self): + """ + Access the class name in the SchemaClassId + + @rtype: str + """ + return self._cname + + + def getHashString(self): + """ + Access the schema's hash as a string value + + @rtype: str + """ + return self._hstr + + + def getType(self): + """ + Returns the type code associated with this Schema + + @rtype: str + """ + return self._type + + def mapEncode(self): + _map = {} + _map["package_name"] = self._pname + _map["class_name"] = self._cname + _map["type"] = self._type + if self._hstr: _map["hash_str"] = self._hstr + return _map + + def __repr__(self): + if self._type == SchemaTypeEvent: + stype = "event" + else: + stype = "data" + hstr = self.getHashString() + if not hstr: + hstr = _schemaHashStrDefault + return self._pname + ":" + self._cname + ":" + stype + "(" + hstr + ")" + + + def __cmp__(self, other): + if not isinstance(other, SchemaClassId) : + raise TypeError("Invalid types for compare") + # return 1 + me = str(self) + them = str(other) + if me < them: + return -1 + if me > them: + return 1 + return 0 + + + def __hash__(self): + return (self._pname, self._cname, self._hstr).__hash__() + + + +def SchemaClassIdFactory( param ): + """ + Factory for constructing SchemaClassIds from various sources + + @type param: various + @param param: object to use for constructing SchemaClassId + @rtype: SchemaClassId + @returns: a new SchemaClassId instance + """ + if type(param) == str: + # construct from __repr__/__str__ representation + try: + pname, cname, rest = param.split(":") + stype,rest = rest.split("("); + hstr = rest.rstrip(")"); + if hstr == _schemaHashStrDefault: + hstr = None + return SchemaClassId( pname, cname, stype, hstr ) + except: + raise TypeError("Invalid string format: '%s'" % param) + + if type(param) == dict: + # construct from map representation + if "package_name" in param: + pname = param["package_name"] + else: + raise TypeError("'package_name' attribute is required") + + if "class_name" in param: + cname = param["class_name"] + else: + raise TypeError("'class_name' attribute is required") + + hstr = None + if "hash_str" in param: + hstr = param["hash_str"] + + stype = "data" + if "type" in param: + stype = param["type"] + + return SchemaClassId( pname, cname, stype, hstr ) + + else: + raise TypeError("Invalid type for SchemaClassId construction") + + + +class SchemaProperty(object): + """ + Describes the structure of a Property data object. + Map format: + map["amqp_type"] = int, AMQP type code indicating property's data type + + optional: + map["access"] = str, access allowed to this property, default "RO" + map["index"] = bool, True if this property is an index value, default False + map["optional"] = bool, True if this property is optional, default False + map["unit"] = str, describes units used + map["min"] = int, minimum allowed value + map["max"] = int, maximun allowed value + map["maxlen"] = int, if string type, this is the maximum length in bytes + required to represent the longest instance of this string. + map["desc"] = str, human-readable description of this argument + map["reference"] = str, ??? + map["parent_ref"] = bool, true if this property references an object in + which this object is in a child-parent relationship. Default False + """ + __hash__ = None + _access_strings = ["RO","RW","RC"] + def __init__(self, typeCode, kwargs={}): + self._type = typeCode + self._access = "RO" + self._isIndex = False + self._isOptional = False + self._unit = None + self._min = None + self._max = None + self._maxlen = None + self._desc = None + self._reference = None + self._isParentRef = False + + for key, value in kwargs.items(): + if key == "access": + value = str(value).upper() + if value not in self._access_strings: + raise TypeError("invalid value for access parameter: '%s':" % value ) + self._access = value + elif key == "index" : self._isIndex = _toBool(value) + elif key == "optional": self._isOptional = _toBool(value) + elif key == "unit" : self._unit = value + elif key == "min" : self._min = value + elif key == "max" : self._max = value + elif key == "maxlen" : self._maxlen = value + elif key == "desc" : self._desc = value + elif key == "reference" : self._reference = value + elif key == "parent_ref" : self._isParentRef = _toBool(value) + + def getType(self): return self._type + + def getAccess(self): return self._access + + def isOptional(self): return self._isOptional + + def isIndex(self): return self._isIndex + + def getUnit(self): return self._unit + + def getMin(self): return self._min + + def getMax(self): return self._max + + def getMaxLen(self): return self._maxlen + + def getDesc(self): return self._desc + + def getReference(self): return self._reference + + def isParentRef(self): return self._isParentRef + + def mapEncode(self): + """ + Return the map encoding of this schema. + """ + _map = {} + _map["amqp_type"] = self._type + _map["access"] = self._access + _map["index"] = self._isIndex + _map["optional"] = self._isOptional + if self._unit: _map["unit"] = self._unit + if self._min: _map["min"] = self._min + if self._max: _map["max"] = self._max + if self._maxlen: _map["maxlen"] = self._maxlen + if self._desc: _map["desc"] = self._desc + if self._reference: _map["reference"] = self._reference + _map["parent_ref"] = self._isParentRef + return _map + + def __repr__(self): return str(self.mapEncode()) + + def _updateHash(self, hasher): + """ + Update the given hash object with a hash computed over this schema. + """ + hasher.update(str(self._type)) + hasher.update(str(self._isIndex)) + hasher.update(str(self._isOptional)) + if self._access: hasher.update(self._access) + if self._unit: hasher.update(self._unit) + if self._desc: hasher.update(self._desc) + + + +def SchemaPropertyFactory( _map ): + """ + Factory for constructing SchemaProperty from a map + + @type _map: dict + @param _map: from mapEncode() of SchemaProperty + @rtype: SchemaProperty + @returns: a new SchemaProperty instance + """ + if type(_map) == dict: + # construct from map + if "amqp_type" not in _map: + raise TypeError("requires 'amqp_type' value") + + return SchemaProperty( _map["amqp_type"], _map ) + + else: + raise TypeError("Invalid type for SchemaProperty construction") + + + + +class SchemaArgument(object): + """ + Describes the structure of an Argument to a Method call. + Map format: + map["amqp_type"] = int, type code indicating argument's data type + map["dir"] = str, direction for an argument associated with a + Method, "I"|"O"|"IO", default value: "I" + optional: + map["desc"] = str, human-readable description of this argument + map["default"] = by amqp_type, default value to use if none provided + """ + __hash__ = None + _dir_strings = ["I", "O", "IO"] + def __init__(self, typeCode, kwargs={}): + self._type = typeCode + self._dir = "I" + self._desc = None + self._default = None + + for key, value in kwargs.items(): + if key == "dir": + value = str(value).upper() + if value not in self._dir_strings: + raise TypeError("invalid value for dir parameter: '%s'" % value) + self._dir = value + elif key == "desc" : self._desc = value + elif key == "default" : self._default = value + + def getType(self): return self._type + + def getDirection(self): return self._dir + + def getDesc(self): return self._desc + + def getDefault(self): return self._default + + def mapEncode(self): + """ + Return the map encoding of this schema. + """ + _map = {} + _map["amqp_type"] = self._type + _map["dir"] = self._dir + # optional: + if self._default: _map["default"] = self._default + if self._desc: _map["desc"] = self._desc + return _map + + def __repr__(self): return str(self.mapEncode()) + + def _updateHash(self, hasher): + """ + Update the given hash object with a hash computed over this schema. + """ + hasher.update(str(self._type)) + hasher.update(self._dir) + if self._desc: hasher.update(self._desc) + + + +def SchemaArgumentFactory( param ): + """ + Factory for constructing SchemaArguments from various sources + + @type param: various + @param param: object to use for constructing SchemaArgument + @rtype: SchemaArgument + @returns: a new SchemaArgument instance + """ + if type(param) == dict: + # construct from map + if not "amqp_type" in param: + raise TypeError("requires 'amqp_type' value") + + return SchemaArgument( param["amqp_type"], param ) + + else: + raise TypeError("Invalid type for SchemaArgument construction") + + + +class SchemaMethod(object): + """ + The SchemaMethod class describes the method's structure, and contains a + SchemaProperty class for each argument declared by the method. + + Map format: + map["arguments"] = map of "name"=<SchemaArgument> pairs. + map["desc"] = str, description of the method + """ + def __init__(self, args={}, _desc=None): + """ + Construct a SchemaMethod. + + @type args: map of "name"=<SchemaProperty> objects + @param args: describes the arguments accepted by the method + @type _desc: str + @param _desc: Human-readable description of the schema + """ + self._arguments = args.copy() + self._desc = _desc + + def getDesc(self): return self._desc + + def getArgCount(self): return len(self._arguments) + + def getArguments(self): return self._arguments.copy() + + def getArgument(self, name): return self._arguments[name] + + def addArgument(self, name, schema): + """ + Add an argument to the list of arguments passed to this method. + Used by an agent for dynamically creating method schema. + + @type name: string + @param name: name of new argument + @type schema: SchemaProperty + @param schema: SchemaProperty to add to this method + """ + if not isinstance(schema, SchemaProperty): + raise TypeError("argument must be a SchemaProperty class") + self._arguments[name] = schema + + def mapEncode(self): + """ + Return the map encoding of this schema. + """ + _map = {} + _args = {} + for name,val in self._arguments.iteritems(): + _args[name] = val.mapEncode() + _map["arguments"] = _args + if self._desc: _map["desc"] = self._desc + return _map + + def __repr__(self): + result = "(" + first = True + for name,arg in self._arguments.iteritems(): + if arg._dir.find("I") != -1: + if first: + first = False + else: + result += ", " + result += name + result += ")" + return result + + def _updateHash(self, hasher): + """ + Update the given hash object with a hash computed over this schema. + """ + for name,val in self._arguments.iteritems(): + hasher.update(name) + val._updateHash(hasher) + if self._desc: hasher.update(self._desc) + + + +def SchemaMethodFactory( param ): + """ + Factory for constructing a SchemaMethod from various sources + + @type param: various + @param param: object to use for constructing a SchemaMethod + @rtype: SchemaMethod + @returns: a new SchemaMethod instance + """ + if type(param) == dict: + # construct from map + args = {} + desc = None + if "arguments" in param: + for name,val in param["arguments"].iteritems(): + args[name] = SchemaArgumentFactory(val) + if "desc" in param: + desc = param["desc"] + + return SchemaMethod( args, desc ) + + else: + raise TypeError("Invalid type for SchemaMethod construction") + + + +class SchemaClass(object): + """ + Base class for Data and Event Schema classes. + """ + def __init__(self, pname, cname, stype, desc=None, hstr=None): + """ + Schema Class constructor. + + @type pname: str + @param pname: package name + @type cname: str + @param cname: class name + @type stype: str + @param stype: type of schema, either "data" or "event" + @type desc: str + @param desc: Human-readable description of the schema + @type hstr: str + @param hstr: hash computed over the schema body, else None + """ + self._pname = pname + self._cname = cname + self._type = stype + self._desc = desc + if hstr: + self._classId = SchemaClassId( pname, cname, stype, hstr ) + else: + self._classId = None + self._properties = {} + self._methods = {} + self._pkeyNames = [] + + + def getClassId(self): + if not self._classId: + self.generateHash() + return self._classId + + def getDesc(self): return self._desc + + def generateHash(self): + """ + generate an md5 hash over the body of the schema, + and return a string representation of the hash + in format "%08x-%08x-%08x-%08x" + """ + md5Hash = _md5Obj() + md5Hash.update(self._pname) + md5Hash.update(self._cname) + md5Hash.update(self._type) + for name,x in self._properties.iteritems(): + md5Hash.update(name) + x._updateHash( md5Hash ) + for name,x in self._methods.iteritems(): + md5Hash.update(name) + x._updateHash( md5Hash ) + idx = 0 + for name in self._pkeyNames: + md5Hash.update(str(idx) + name) + idx += 1 + hstr = md5Hash.hexdigest()[0:8] + "-" +\ + md5Hash.hexdigest()[8:16] + "-" +\ + md5Hash.hexdigest()[16:24] + "-" +\ + md5Hash.hexdigest()[24:32] + # update classId with new hash value + self._classId = SchemaClassId( self._pname, + self._cname, + self._type, + hstr ) + return hstr + + + def mapEncode(self): + """ + Return the map encoding of this schema. + """ + _map = {} + _map["schema_id"] = self.getClassId().mapEncode() + _map["desc"] = self._desc + if len(self._properties): + _props = {} + for name,val in self._properties.iteritems(): + _props[name] = val.mapEncode() + _map["properties"] = _props + + if len(self._methods): + _meths = {} + for name,val in self._methods.iteritems(): + _meths[name] = val.mapEncode() + _map["methods"] = _meths + + if len(self._pkeyNames): + _map["primary_key"] = self._pkeyNames[:] + return _map + + def __repr__(self): + return str(self.getClassId()) + + + +class SchemaObjectClass(SchemaClass): + """ + A schema class that describes a data object. The data object is composed + of zero or more properties and methods. An instance of the SchemaObjectClass + can be identified using a key generated by concantenating the values of + all properties named in the primary key list. + + Map format: + map["schema_id"] = map, SchemaClassId map for this object. + map["desc"] = human readable description of this schema. + map["primary_key"] = ordered list of property names used to construct the Primary Key" + map["properties"] = map of "name":SchemaProperty instances. + map["methods"] = map of "name":SchemaMethods instances. + """ + def __init__( self, pname, cname, desc=None, _hash=None, + _props={}, _pkey=[], _methods={}): + """ + @type pname: str + @param pname: name of package this schema belongs to + @type cname: str + @param cname: class name for this schema + @type desc: str + @param desc: Human-readable description of the schema + @type _hash: str + @param _methods: hash computed on the body of this schema, if known + @type _props: map of 'name':<SchemaProperty> objects + @param _props: all properties provided by this schema + @type _pkey: list of strings + @param _pkey: names of each property to be used for constructing the primary key + @type _methods: map of 'name':<SchemaMethod> objects + @param _methods: all methods provided by this schema + """ + super(SchemaObjectClass, self).__init__(pname, + cname, + SchemaTypeData, + desc, + _hash) + self._properties = _props.copy() + self._pkeyNames = _pkey[:] + self._methods = _methods.copy() + + def getPrimaryKeyList(self): return self._pkeyNames[:] + + def getPropertyCount(self): return len(self._properties) + def getProperties(self): return self._properties.copy() + def getProperty(self, name): return self._properties[name] + + def getMethodCount(self): return len(self._methods) + def getMethods(self): return self._methods.copy() + def getMethod(self, name): return self._methods[name] + + def addProperty(self, name, prop): + self._properties[name] = prop + # need to re-generate schema hash + self._classId = None + + def addMethod(self, name, method): + self._methods[name] = method + self._classId = None + + + +def SchemaObjectClassFactory( param ): + """ + Factory for constructing a SchemaObjectClass from various sources. + + @type param: various + @param param: object to use for constructing a SchemaObjectClass instance + @rtype: SchemaObjectClass + @returns: a new SchemaObjectClass instance + """ + if type(param) == dict: + classId = None + properties = {} + methods = {} + pkey = [] + if "schema_id" in param: + classId = SchemaClassIdFactory(param["schema_id"]) + if (not classId) or (classId.getType() != SchemaTypeData): + raise TypeError("Invalid SchemaClassId specified: %s" % classId) + if "desc" in param: + desc = param["desc"] + if "primary_key" in param: + pkey = param["primary_key"] + if "properties" in param: + for name,val in param["properties"].iteritems(): + properties[name] = SchemaPropertyFactory(val) + if "methods" in param: + for name,val in param["methods"].iteritems(): + methods[name] = SchemaMethodFactory(val) + + return SchemaObjectClass( classId.getPackageName(), + classId.getClassName(), + desc, + _hash = classId.getHashString(), + _props = properties, _pkey = pkey, + _methods = methods) + + else: + raise TypeError("Invalid type for SchemaObjectClass construction") + + + +class SchemaEventClass(SchemaClass): + """ + A schema class that describes an event. The event is composed + of zero or more properties. + + Map format: + map["schema_id"] = map, SchemaClassId map for this object. + map["desc"] = string description of this schema + map["properties"] = map of "name":SchemaProperty values. + """ + def __init__( self, pname, cname, desc=None, _props={}, _hash=None ): + super(SchemaEventClass, self).__init__(pname, + cname, + SchemaTypeEvent, + desc, + _hash ) + self._properties = _props.copy() + + def getPropertyCount(self): return len(self._properties) + def getProperties(self): return self._properties.copy() + def getProperty(self, name): return self._properties[name] + def addProperty(self, name, prop): + self._properties[name] = prop + # need to re-generate schema hash + self._classId = None + + def mapEncode(self): + _map = super(SchemaEventClass, self).mapEncode() + return _map + + + +def SchemaEventClassFactory( param ): + """ + Factory for constructing a SchemaEventClass from various sources. + + @type param: various + @param param: object to use for constructing a SchemaEventClass instance + @rtype: SchemaEventClass + @returns: a new SchemaEventClass instance + """ + if type(param) == dict: + logging.debug( "constructing SchemaEventClass from map '%s'" % param ) + classId = None + properties = {} + if "schema_id" in param: + classId = SchemaClassIdFactory(param["schema_id"]) + if (not classId) or (classId.getType() != SchemaTypeEvent): + raise TypeError("Invalid SchemaClassId specified: %s" % classId) + if "desc" in param: + desc = param["desc"] + if "properties" in param: + for name,val in param["properties"].iteritems(): + properties[name] = SchemaPropertyFactory(val) + + return SchemaEventClass( classId.getPackageName(), + classId.getClassName(), + desc, + _hash = classId.getHashString(), + _props = properties ) + else: + raise TypeError("Invalid type for SchemaEventClass construction") + + + + + + + + + |