19fbfc2c065850a25ef281677b14a7ca1261c72e
[nepi.git] / src / nepi / execution / attribute.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License as published by
7 #    the Free Software Foundation, either version 3 of the License, or
8 #    (at your option) any later version.
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
19
20 ### Attribute Types
21 class Types:
22     String  = "STRING"
23     Bool    = "BOOL"
24     Enumerate    = "ENUM"
25     Double  = "DOUBLE"
26     Integer = "INTEGER"
27
28 ### Attribute Flags
29 class Flags:
30     """ Differents flags to characterize an attribute
31
32     """
33     # Attribute value can not be read (it is hidden to the user) 
34     NoRead    = 1 # 1
35     
36     # Attribute value can not be modified (it is not editable by the user)
37     NoWrite   = 1 << 1 # 2
38     
39     # Attribute value can be modified only before deployment
40     Design  = 1 << 2 # 4
41
42     # Attribute value will be used at deployment time for initial configuration
43     Construct    = 1 << 3 #  8
44
45     # Attribute provides credentials to access resources
46     Credential  = 1 << 4  | Design # 16 + 4
47
48     # Attribute is a filter used to discover resources
49     Filter  = 1 << 5 | Design # 32 + 4
50
51     # Attribute Flag is reserved for internal RM usage (i.e. should be 
52     # transparent to the user)
53     Reserved  = 1 << 6 # 64
54
55     # Attribute global is set to all resources of rtype
56     Global  = 1 << 7 # 128
57
58 class Attribute(object):
59     """
60     .. class:: Class Args :
61
62         An Attribute reflects a configuration parameter for
63         a particular resource. Attributes might be read only or
64         not.
65       
66         :param name: Name of the attribute
67         :type name: str
68
69         :param help: Attribute description
70         :type help: str
71         
72         :param type: The type expected for the attribute value.
73                      Should be one of Attribute.Types .
74         :type type: str
75
76         :param flags: Defines attribute behavior (i.e. whether it is read-only,
77                 read and write, etc). This parameter should take its values from
78                 Attribute.Flags. Flags values can be bitwised.
79         :type flags: hex
80
81         :param default: Default value of the attribute
82         :type default: depends on the type of attribute
83         
84         :param allowed: List of values that the attribute can take. 
85                 This parameter is only meaningful for Enumerate type attributes.
86         :type allowed: list
87         
88         :param range: (max, min) tuple with range of possible values for
89                 attributes.
90                 This parameter is only meaningful for Integer or Double type
91                 attributes.
92         :type range: (int, int) or (float, float)
93         
94         :param set_hook: Function that will be executed whenever a new 
95                 value is set for the attribute.
96         :type set_hook: function
97
98     """
99     def __init__(self, name, help, type = Types.String,
100             flags = None, default = None, allowed = None,
101             range = None, set_hook = None):
102         self._name = name
103         self._help = help
104         self._type = type
105         self._flags = flags or 0
106         self._allowed = allowed
107         self._range = range
108         self._default = self._value = default
109         # callback to be invoked upon changing the 
110         # attribute value
111         self.set_hook = set_hook
112
113     @property
114     def name(self):
115         """ Returns the name of the attribute """
116         return self._name
117
118     @property
119     def default(self):
120         """ Returns the default value of the attribute """
121         return self._default
122
123     @property
124     def type(self):
125         """ Returns the type of the attribute """
126         return self._type
127
128     @property
129     def help(self):
130         """ Returns the help of the attribute """
131         return self._help
132
133     @property
134     def flags(self):
135         """ Returns the flags of the attribute """
136         return self._flags
137
138     @property
139     def allowed(self):
140         """ Returns the allowed value for this attribute """
141         return self._allowed
142
143     @property
144     def range(self):
145         """ Returns the range of the attribute """
146         return self._range
147
148     def has_flag(self, flag):
149         """ Returns true if the attribute has the flag 'flag'
150
151         :param flag: Flag to be checked
152         :type flag: Flags
153         """
154         return (self._flags & flag) == flag
155
156     def get_value(self):
157         """ Returns the value of the attribute """
158         return self._value
159
160     def set_value(self, value):
161         """ Change the value of the attribute after checking the type """
162         valid = True
163
164         if self.type == Types.Enumerate:
165             valid = value in self._allowed
166
167         if self.type in [Types.Double, Types.Integer] and self.range:
168             (min, max) = self.range
169
170             value = float(value)
171
172             valid = (value >= min and value <= max) 
173         
174         valid = valid and self.is_valid_value(value)
175
176         if valid: 
177             if self.set_hook:
178                 # Hook receives old value, new value
179                 value = self.set_hook(self._value, value)
180
181             self._value = value
182         else:
183             raise ValueError("Invalid value %s for attribute %s" %
184                     (str(value), self.name))
185
186     value = property(get_value, set_value)
187
188     def is_valid_value(self, value):
189         """ Attribute subclasses will override this method to add 
190         adequate validation"""
191         return True
192
193     @property
194     def has_changed(self):
195         """ Returns true if the value has changed from the default """
196         return self.value != self.default