--- a/configurationengine/source/cone/confml/model.py Fri Mar 12 08:30:17 2010 +0200
+++ b/configurationengine/source/cone/confml/model.py Tue Aug 10 14:29:28 2010 +0300
@@ -21,9 +21,15 @@
All Confml element attributes become attributes of this instance.
"""
import types
+import sys
+import re
from cone.public import api, exceptions, container, utils
class ConfmlElement(api.Base):
+ def __init__(self, ref="", **kwargs):
+ super(ConfmlElement,self).__init__(ref, **kwargs)
+ self.lineno = None
+
def _get_mapper(self,modelname):
"""
Return a instance of appropriate mapper for given model.
@@ -48,6 +54,18 @@
pass
""" The description as a property """
desc = property(get_desc,set_desc,del_desc)
+
+class ConfmlData(api.Data):
+ """
+ The data element can contain any data setting for a feature. The data element can be
+ a value definition for any type of data. It basically just links some data to a feature.
+ The default Data attribute is 'data', but it can be any string. For example current use case
+ is 'rfs'.
+ """
+ def __init__(self, **kwargs):
+ super(ConfmlData,self).__init__(**kwargs)
+ self.lineno = None
+
class ConfmlConfiguration(ConfmlElement, api.Configuration):
"""
Confml configuration class.
@@ -59,6 +77,14 @@
if kwargs.get('desc'):
self.desc = kwargs.get('desc')
+ def _view_class(self):
+ return ConfmlView
+
+ def _feature_class(self):
+ return ConfmlFeature
+
+ def _configuration_class(self):
+ return ConfmlConfiguration
def get_desc(self):
"""
@@ -84,7 +110,7 @@
def get_meta(self):
"""
- @return: The description of the Configuration.
+ @return: The meta element of the Configuration.
"""
try:
meta = getattr(self,ConfmlMeta.refname)
@@ -104,88 +130,13 @@
""" The meta element as a property """
meta = property(get_meta,set_meta,del_meta)
-
-class ConfmlGroup(ConfmlElement, api.Group):
- """
- Confml view.
+class ConfmlSettingAttributes(ConfmlElement):
"""
- def __init__(self, ref="", **kwargs):
- super(ConfmlGroup,self).__init__(ref,**kwargs)
- if kwargs.get('icon'):
- self.icon = kwargs.get('icon')
- if kwargs.get('desc'):
- self.desc = kwargs.get('desc')
-
- def get_icon(self):
- try:
- icon = getattr(self,ConfmlIcon.refname)
- return icon.href
- except AttributeError:
- return None
- def set_icon(self,value): self._add(ConfmlIcon(value))
- def del_icon(self):
- try:
- self._remove(ConfmlIcon.refname)
- except exceptions.NotFound:
- pass
- """ The icon as a property """
- icon = property(get_icon,set_icon,del_icon)
-
- def get_desc(self):
- try:
- desc = getattr(self,ConfmlDescription.refname)
- return desc.text
- except AttributeError:
- return None
- def set_desc(self,value): self._add(ConfmlDescription(value))
- def del_desc(self):
- try:
- self._remove(ConfmlDescription.refname)
- except exceptions.NotFound:
- pass
- """ The description as a property """
- desc = property(get_desc,set_desc,del_desc)
-
-
-class ConfmlView(api.View):
+ Abstract base class for setting attributes. This is used as
+ a base in actual ConfmlSetting and ConfmlFeatureLink.
"""
- Confml view.
- """
- def __init__(self, ref="", **kwargs):
- super(ConfmlView,self).__init__(ref,**kwargs)
- if kwargs.get('desc'):
- self.desc = kwargs.get('desc')
-
-
- def get_desc(self):
- try:
- desc = getattr(self,ConfmlDescription.refname)
- return desc.text
- except AttributeError:
- return None
- def set_desc(self,value): self._add(ConfmlDescription(value))
- def del_desc(self):
- try:
- self._remove(ConfmlDescription.refname)
- except exceptions.NotFound:
- pass
- """ The description as a property """
- desc = property(get_desc,set_desc,del_desc)
-
-class ConfmlFeature(ConfmlElement, api.Feature):
- pass
-
-class ConfmlSetting(ConfmlElement, api.Feature):
- """
- Confml setting class. Attribute 'options' contains options of this setting.
- """
- supported_types = ['int',
- 'string',
- 'boolean',
- 'selection']
def __init__(self, ref,**kwargs):
- super(ConfmlSetting,self).__init__(ref,**kwargs)
- self.type = kwargs.get('type',None)
+ super(ConfmlSettingAttributes,self).__init__(ref,**kwargs)
if kwargs.get('desc'):
self.desc = kwargs.get('desc')
if kwargs.get('minOccurs'):
@@ -196,10 +147,10 @@
self.maxLength = kwargs.get('maxLength')
if kwargs.get('minLength'):
self.minLength = kwargs.get('minLength')
- if kwargs.get('mapKey'):
- self.mapKey = kwargs.get('mapKey')
- if kwargs.get('mapValue'):
- self.mapValue = kwargs.get('mapValue')
+ self.mapKey = kwargs.get('mapKey')
+ self.mapValue = kwargs.get('mapValue')
+ self.displayName = kwargs.get('displayName')
+
self.readOnly = kwargs.get('readOnly',None)
self.constraint = kwargs.get('constraint',None)
@@ -212,39 +163,6 @@
"""
return api.ValueRe('.*')
- def add_property(self, **kwargs):
- """
- @param name=str: property name
- @param value=str: property value
- @param unit=str: property unit, e.g. kB
- """
- self._add(ConfmlProperty(**kwargs), container.APPEND)
-
- def get_property(self, name):
- """
- @param name: The name of the property
- """
- for property in utils.get_list(self._get(ConfmlProperty.refname)):
- if property.name == name:
- return property
- raise exceptions.NotFound("ConfmlProperty with name %s not found!" % name)
-
- def remove_property(self, name):
- """
- remove a given option from this feature by name.
- @param name:
- """
- for property in self._get(ConfmlProperty.refname):
- if property.name == name:
- return self._remove(property.get_fullref())
- raise exceptions.NotFound("ConfmlProperty with name %s not found!" % name)
-
- def list_properties(self):
- """
- Return a array of all Feature children references under this object.
- """
- return [obj.name for obj in utils.get_list(self._get(ConfmlProperty.refname))]
-
def get_maxlength(self):
try:
return getattr(self,ConfmlMaxLength.refname).value
@@ -259,7 +177,7 @@
self._remove(ConfmlMaxLength.refname)
except exceptions.NotFound:
pass
- """ The description as a property """
+ """ The maxLength as a property """
maxLength = property(get_maxlength,set_maxlength,del_maxlength)
def get_minlength(self):
@@ -276,8 +194,25 @@
self._remove(ConfmlMinLength.refname)
except exceptions.NotFound:
pass
- """ The description as a property """
+ """ The minLength as a property """
minLength = property(get_minlength,set_minlength,del_minlength)
+
+ def get_length(self):
+ try:
+ return getattr(self,ConfmlLength.refname).value
+ except AttributeError:
+ return None
+
+ def set_length(self,value):
+ self._add(ConfmlLength(value))
+
+ def del_length(self):
+ try:
+ self._remove(ConfmlLength.refname)
+ except exceptions.NotFound:
+ pass
+ """ The length as a property """
+ length = property(get_length,set_length,del_length)
def get_minInclusive(self):
try:
@@ -390,22 +325,112 @@
@property
def properties(self):
- dict = {}
- for property in utils.get_list(self._get(ConfmlProperty.refname)):
- dict[property.name] = property
- return dict
+ propdict = {}
+ for prop in self._objects(type=api.Property):
+ propdict[prop.name] = prop
+ return propdict
- def get_rfs(self,):
- return super(ConfmlSetting,self).get_value('rfs')
+ def get_rfs(self):
+ return super(ConfmlSettingAttributes,self).get_value('rfs')
def set_rfs(self, value):
- super(ConfmlSetting,self).set_value('rfs',value)
+ super(ConfmlSettingAttributes,self).set_value('rfs',value)
def del_rfs(self):
- super(ConfmlSetting,self).del_value('rfs')
+ super(ConfmlSettingAttributes,self).del_value('rfs')
rfs = property(get_rfs,set_rfs,del_rfs)
+
+class ConfmlGroup(ConfmlElement, api.Group):
+ """
+ Confml view.
+ """
+ def __init__(self, ref="", **kwargs):
+ super(ConfmlGroup,self).__init__(ref,**kwargs)
+ if kwargs.get('icon'):
+ self.icon = kwargs.get('icon')
+ if kwargs.get('desc'):
+ self.desc = kwargs.get('desc')
+
+ def _group_class(self):
+ return ConfmlGroup
+
+ def _featurelink_class(self):
+ return ConfmlFeatureLink
+
+ def get_icon(self):
+ try:
+ icon = getattr(self,ConfmlIcon.refname)
+ return icon.href
+ except AttributeError:
+ return None
+ def set_icon(self,value): self._add(ConfmlIcon(value))
+ def del_icon(self):
+ try:
+ self._remove(ConfmlIcon.refname)
+ except exceptions.NotFound:
+ pass
+ """ The icon as a property """
+ icon = property(get_icon,set_icon,del_icon)
+
+ def get_desc(self):
+ try:
+ desc = getattr(self,ConfmlDescription.refname)
+ return desc.text
+ except AttributeError:
+ return None
+ def set_desc(self,value): self._add(ConfmlDescription(value))
+ def del_desc(self):
+ try:
+ self._remove(ConfmlDescription.refname)
+ except exceptions.NotFound:
+ pass
+ """ The description as a property """
+ desc = property(get_desc,set_desc,del_desc)
+
+
+class ConfmlView(api.View, ConfmlGroup):
+ """
+ Confml view.
+ """
+ def __init__(self, ref="", **kwargs):
+ super(ConfmlView,self).__init__(ref,**kwargs)
+ if kwargs.get('desc'):
+ self.desc = kwargs.get('desc')
+
+ def get_desc(self):
+ try:
+ desc = getattr(self,ConfmlDescription.refname)
+ return desc.text
+ except AttributeError:
+ return None
+ def set_desc(self,value): self._add(ConfmlDescription(value))
+ def del_desc(self):
+ try:
+ self._remove(ConfmlDescription.refname)
+ except exceptions.NotFound:
+ pass
+ """ The description as a property """
+ desc = property(get_desc,set_desc,del_desc)
+
+
+class ConfmlFeature(ConfmlElement, api.Feature):
+ def _feature_class(self):
+ return ConfmlSetting
+
+class ConfmlSetting(ConfmlSettingAttributes, api.Feature):
+ """
+ Confml setting class. Attribute 'options' contains options of this setting.
+ """
+ supported_types = ['int',
+ 'string',
+ 'boolean',
+ 'selection']
+ def __init__(self, ref,**kwargs):
+ super(ConfmlSetting,self).__init__(ref,**kwargs)
+ self.type = kwargs.get('type',None)
+
def get_value_cast(self, value, attr=None):
"""
A function to perform the value type casting in get operation
@@ -425,13 +450,17 @@
@param value: the value to cast
@param attr: the attribute which is fetched from model (normally in confml either None='data' or 'rfs')
"""
+ # Add a exception case for None value, because the data casting will always fail for it
+ if value == None:
+ return value
+
if not attr or attr == 'data':
return self.set_data_cast(value)
elif attr == 'rfs':
return self.set_rfs_cast(value)
else:
return value
-
+
def get_data_cast(self, value):
"""
A function to perform the data type casting in get operation
@@ -600,48 +629,78 @@
"""
Confml setting class for multiSelection type.
"""
-
+
+ # Pattern for checking whether a data value should be interpreted
+ # in the old style (e.g. '"opt1" "opt2" "opt3"')
+ OLD_STYLE_DATA_PATTERN = re.compile(r'"[^"]*([^"]*" ")*[^"]*"')
+
def __init__(self, ref,**kwargs):
kwargs['type'] = 'multiSelection'
ConfmlSetting.__init__(self,ref,**kwargs)
-
+ def add_data(self, data):
+ """
+ Add a data value.
+ @param data: A Data object
+ """
+ # If there are existing data objects added to the proxy, and they
+ # are not in the same DataContainer (ConfML data section), change the
+ # policy to replace
+ if self.dataproxy.datas.get(data.attr):
+ existing_data_obj = self.dataproxy.datas[data.attr][-1]
+ existing_obj_parent = existing_data_obj._find_parent_or_default(type=api.DataContainer)
+ new_obj_parent = data._find_parent_or_default(type=api.DataContainer)
+
+ if existing_obj_parent is not new_obj_parent:
+ self.dataproxy.datas[data.attr] = []
+
+ self.dataproxy._add_data(data)
+
def get_valueset(self):
"""
Get the ValueSet object for this feature, that has the list of available values.
"""
return api.Feature.get_valueset(self)
-
- def get_data_cast(self, value):
- """
- A function to perform the value type casting in get operation
- """
- try:
- if not isinstance(value, types.ListType):
- values = value.split('" "')
- for i in range(len(values)):
- if values[i].startswith('"'):
- values[i] = values[i][1:]
- if values[i].endswith('"'):
- values[i] = values[i][:-1]
- return values
- return value
- except AttributeError:
- return None
- def set_data_cast(self, value):
- """
- A function to perform the value type casting in the set operation
- """
+ def convert_data_to_value(self, data_objects, cast=True, attr=None):
+ if len(data_objects) == 1:
+ d = data_objects[0]
+
+ # Special handling for cases where the data is in the old format
+ # (pre-2.88 ConfML spec)
+ if d.value is not None:
+ if self.OLD_STYLE_DATA_PATTERN.match(d.value):
+ return tuple([v.rstrip('"').lstrip('"') for v in d.value.split('" "')])
+
+ # Single data object with empty="true" means that nothing is selected
+ if d.empty: return ()
- if isinstance(value, list):
- value = " ".join(['"%s"' % elem for elem in value])
- return value
+ # Read each data value (or name-ID mapped value) into result
+ result = []
+ for data_obj in data_objects:
+ if data_obj.map:
+ value = self._resolve_name_id_mapped_value(data_obj.map, cast_value=cast)
+ else:
+ value = data_obj.value
+ result.append(value)
+ result = utils.distinct_array(result)
+
+ # Handle None in the result (data element with no text data)
+ if None in result:
+ # If the empty string is a valid option, change the None to that,
+ # otherwise ignore
+ index = result.index(None)
+ if '' in self.get_valueset(): result[index] = ''
+ else: del result[index]
+
+ return tuple(result)
- def set_value(self, value):
- if not isinstance(value, types.ListType):
- raise ValueError("Only list types are allowed.")
- self.value = value
+ def convert_value_to_data(self, value, attr=None):
+ if not isinstance(value, (types.ListType, types.TupleType, types.NoneType)):
+ raise ValueError("Only list, tuple and None types are allowed.")
+
+ if value: return [api.Data(fqr=self.fqr, value=v, attr=attr) for v in value]
+ else: return [api.Data(fqr=self.fqr, empty=True, attr=attr)]
class ConfmlDateSetting(ConfmlSetting):
"""
@@ -667,6 +726,32 @@
kwargs['type'] = 'dateTime'
ConfmlSetting.__init__(self,ref,**kwargs)
+class ConfmlHexBinarySetting(ConfmlSetting):
+ """
+ Confml setting class for hex-binary type.
+ """
+ def __init__(self, ref,**kwargs):
+ kwargs['type'] = 'hexBinary'
+ ConfmlSetting.__init__(self,ref,**kwargs)
+
+ def get_valueset(self):
+ return api.ValueRe(r'^([0123456789ABCDEF]{2})*$')
+
+ def get_data_cast(self, value):
+ value = value or '' # Handle None
+ if value not in self.get_valueset():
+ raise ValueError("Cannot convert value %r of setting '%s' into binary data: Not a valid hex string", value)
+
+ temp = []
+ for i in xrange(len(value) / 2):
+ start = i * 2
+ end = start + 2
+ temp.append(chr(int(value[start:end], 16)))
+ return ''.join(temp)
+
+ def set_data_cast(self, value):
+ return ''.join(['%02X' % ord(c) for c in value])
+
class ConfmlDurationSetting(ConfmlSetting):
"""
Confml setting class for date type.
@@ -711,10 +796,12 @@
class ConfmlLocalPath(ConfmlElement, api.Feature):
"""
- Confml file class. Attribute setting.
+ Confml file class. Attribute setting.
+ The localPath "name" is always the same as its ref 'localPath'
"""
def __init__(self, ref='localPath', **kwargs):
kwargs['type'] = 'string'
+ kwargs['name'] = ref
ConfmlElement.__init__(self, **kwargs)
api.Feature.__init__(self, ref, **kwargs)
self.readOnly = kwargs.get('readOnly', None)
@@ -723,14 +810,45 @@
class ConfmlTargetPath(ConfmlElement, api.Feature):
"""
Confml file class. Attribute setting.
+ The targetPath "name" is always the same as its ref 'targetPath'
"""
def __init__(self, ref='targetPath', **kwargs):
kwargs['type'] = 'string'
+ kwargs['name'] = ref
ConfmlElement.__init__(self, **kwargs)
api.Feature.__init__(self, ref, **kwargs)
self.readOnly = kwargs.get('readOnly', None)
+class ConfmlFeatureLink(ConfmlSettingAttributes, api.FeatureLink):
+ """
+ ConfmlFeatureLink object is the setting reference object inside confml
+ group / view. It can populate the actual FeatureProxy objects under the
+ particular group / view object.
+ """
+
+ """ the override_attributes explicitly states which feature link attributes can be overridden """
+ override_attributes = ['name',
+ 'desc',
+ 'minLength',
+ 'maxLength',
+ 'minOccurs',
+ 'maxOccurs',
+ 'minInclusive',
+ 'maxInclusive',
+ 'minExclusive',
+ 'maxExclusive',
+ 'pattern',
+ 'totalDigits',
+ 'options',
+ 'properties',
+ 'readOnly'
+ ]
+ def __init__(self, ref,**kwargs):
+ ConfmlSettingAttributes.__init__(self, ref,**kwargs)
+ api.FeatureLink.__init__(self, ref, **kwargs)
+ self.type = kwargs.get('type',None)
+
class ConfmlMeta(api.Base):
"""
Confml meta element
@@ -751,6 +869,9 @@
def __setitem__(self, key, value):
self.array[key] = value
+ def __len__(self):
+ return len(self.array)
+
def __str__(self):
tempstr = "ConfmlMeta object\n"
counter = 0
@@ -790,6 +911,16 @@
def replace(self, index, tag, value, ns=None, dict=None):
self.array[index] = ConfmlMetaProperty(tag, value, ns, attrs=dict)
+ def update(self, data):
+ """
+ Update this the ConfmlMeta object meta with the given data.
+ @param data: The input ConfmlMeta data to update for this object
+ """
+ if data:
+ for property in data.array:
+ self.set_property_by_tag(property.tag, property.value, property.ns, property.attrs)
+
+
def clear(self, value):
self.array = []
@@ -807,22 +938,38 @@
return -1
def find_by_attribute(self, name, value):
- for item in self.array:
- if item.attrs.has_key(name) and item.attrs[name] == value:
+ for item in self.array:
+ if item.attrs.has_key(name) and item.attrs[name] == value:
return self.array.index(item)
return -1
- def get_property_by_tag(self, tag):
+ def get_property_by_tag(self, tag, attrs={}):
"""
Try to find the element by its tag in the meta elem array.
@param tag: the tag that is searched
@return: the ConfmlMetaProperty object if it is found. None if element with tag is not found.
- """
+ """
for item in self.array:
if item.tag == tag:
- return item
+ if not item.attrs or (item.attrs.get("name", None) == attrs.get("name", None)):
+ return item
return None
+ def set_property_by_tag(self, tag, value, ns=None, attributes=None):
+ """
+ Try to find the element by its tag and set it the meta elem array.
+ This will either create a new element to the meta or replace first
+ encountered elem in array.
+ @param tag: the tag that is searched
+ @return: the ConfmlMetaProperty object if it is found. None if element with tag is not found.
+ """
+
+ if self.get_property_by_tag(tag, attributes):
+ property = self.get_property_by_tag(tag, attributes)
+ property.value = value
+ property.attrs = attributes or {}
+ else:
+ self.add(tag, value, ns, attributes)
class ConfmlDescription(api.Base):
"""
@@ -844,21 +991,6 @@
self.href = href
-class ConfmlProperty(api.Base):
- """
- Confml meta element
- """
- refname = "_property"
- def __init__(self, **kwargs):
- """
- @param name=str: name string
- @param value=str: value for the property, string
- @param unit=str: unit of the property
- """
- super(ConfmlProperty,self).__init__(self.refname)
- self.name = kwargs.get('name',None)
- self.value = kwargs.get('value',None)
- self.unit = kwargs.get('unit',None)
class ConfmlMetaProperty(api.Base):
@@ -873,10 +1005,7 @@
self.tag = tag
self.value = value
self.ns = ns
- if kwargs.has_key("attrs") and kwargs["attrs"] != None:
- self.attrs = dict(kwargs["attrs"])
- else:
- self.attrs = {}
+ self.attrs = dict(kwargs.get('attrs') or {})
def __cmp__(self, other):
try:
@@ -890,9 +1019,28 @@
def __str__(self):
return "Tag: %s Value: %s Namespace: %s Attributes: % s" % (self.tag, self.value, self.ns, repr(self.attrs))
-
-class ConfmlLength(api.Base):
+class ConfmlNumericValue(api.Base):
+ """
+ Confml base class for all float type properties.
+ Performs a simple value casting from string to int in value setting.
+ """
+ def __init__(self, ref="", **kwargs):
+ super(ConfmlNumericValue,self).__init__(ref, **kwargs)
+ self._value = None
+
+ def get_value(self): return self._value
+ def del_value(self): self._value = None
+ def set_value(self, value):
+ if utils.is_float(value):
+ self._value = float(value)
+ else:
+ self._value = int(value)
+ """ The value as a property """
+ value = property(get_value,set_value,del_value)
+
+
+class ConfmlLength(ConfmlNumericValue):
"""
Confml length element
"""
@@ -901,7 +1049,7 @@
super(ConfmlLength,self).__init__(self.refname)
self.value = value
-class ConfmlMaxLength(api.Base):
+class ConfmlMaxLength(ConfmlNumericValue):
"""
Confml max element
"""
@@ -910,7 +1058,7 @@
super(ConfmlMaxLength,self).__init__(self.refname)
self.value = value
-class ConfmlMinLength(api.Base):
+class ConfmlMinLength(ConfmlNumericValue):
"""
Confml min element
"""
@@ -919,7 +1067,7 @@
super(ConfmlMinLength,self).__init__(self.refname)
self.value = value
-class ConfmlMinInclusive(api.Base):
+class ConfmlMinInclusive(ConfmlNumericValue):
"""
Confml minInclusive element
"""
@@ -928,7 +1076,7 @@
super(ConfmlMinInclusive,self).__init__(self.refname)
self.value = value
-class ConfmlMaxInclusive(api.Base):
+class ConfmlMaxInclusive(ConfmlNumericValue):
"""
Confml minInclusive element
"""
@@ -937,7 +1085,7 @@
super(ConfmlMaxInclusive,self).__init__(self.refname)
self.value = value
-class ConfmlMinExclusive(api.Base):
+class ConfmlMinExclusive(ConfmlNumericValue):
"""
Confml minExclusive element
"""
@@ -946,7 +1094,7 @@
super(ConfmlMinExclusive,self).__init__(self.refname)
self.value = value
-class ConfmlMaxExclusive(api.Base):
+class ConfmlMaxExclusive(ConfmlNumericValue):
"""
Confml maxExclusive element
"""
@@ -964,7 +1112,7 @@
super(ConfmlPattern,self).__init__(self.refname)
self.value = value
-class ConfmlTotalDigits(api.Base):
+class ConfmlTotalDigits(ConfmlNumericValue):
"""
Confml totalDigits element
"""