cosmetic
[nepi.git] / 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 version 2 as
7 #    published by the Free Software Foundation;
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 #
17 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
18
19 ### Attribute Types
20 class Types:
21     """ Allowed types for the Attribute value
22     """
23     String  = "STRING"
24     Bool    = "BOOL"
25     Enumerate    = "ENUM"
26     Double  = "DOUBLE"
27     Integer = "INTEGER"
28
29 ### Attribute Flags
30 class Flags:
31     """ Flags to characterize the scope of an Attribute
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
59 class Attribute(object):
60     """ An Attribute exposes a configuration parameter of a resource
61     """
62
63     def __init__(self, name, help, type = Types.String,
64                  flags = None, default = None, allowed = None,
65                  range = None, set_hook = None):
66         """
67         :param name: Name of the Attribute
68         :type name: str
69
70         :param help: Description of the Attribute
71         :type help: str
72         
73         :param type: The type expected for the Attribute value.
74                      Should be one of Attribute.Types
75         :type type: str
76
77         :param flags: Defines Attribute behavior (i.e. whether it is read-only,
78                 read and write, etc). This parameter must take its values from
79                 Attribute.Flags. Flags values can be bitwised
80         :type flags: hex
81
82         :param default: Default value for the Attribute
83         :type default: Depends on the type of Attribute
84         
85         :param allowed: List of values that the Attribute can take. 
86                 This parameter is only meaningful for Enumerate type Attributes
87         :type allowed: list
88         
89         :param range: (max, min) tuple with range of possible values for
90                 Attributes.
91                 This parameter is only meaningful for Integer or Double type
92                 Attributes
93         :type range: (int, int) or (float, float)
94         
95         :param set_hook: Function that will be executed whenever a new 
96                 value is set for the Attribute.
97         :type set_hook: function
98
99     """
100         self._name = name
101         self._help = help
102         self._type = type
103         self._flags = flags or 0
104         self._allowed = allowed
105         self._range = range
106         self._default = self._value = default
107         # callback to be invoked upon changing the 
108         # attribute value
109         self.set_hook = set_hook
110
111     @property
112     def name(self):
113         """ Returns the name of the Attribute """
114         return self._name
115
116     @property
117     def default(self):
118         """ Returns the default value of the Attribute """
119         return self._default
120
121     @property
122     def type(self):
123         """ Returns the type of the Attribute """
124         return self._type
125
126     @property
127     def help(self):
128         """ Returns the description of the Attribute """
129         return self._help
130
131     @property
132     def flags(self):
133         """ Returns the flags of the Attribute """
134         return self._flags
135
136     @property
137     def allowed(self):
138         """ Returns the set of allowed values for the Attribute """
139         return self._allowed
140
141     @property
142     def range(self):
143         """ Returns the range of allowed numerical values for the Attribute """
144         return self._range
145
146     def has_flag(self, flag):
147         """ Returns True if the Attribute has the flag 'flag'
148
149         :param flag: Flag to be checked
150         :type flag: Flags
151         """
152         return (self._flags & flag) == flag
153
154     def get_value(self):
155         """ Returns the value of the Attribute """
156         return self._value
157
158     def set_value(self, value):
159         """ Configure a new value for the Attribute """
160         valid = True
161
162         if self.type == Types.Enumerate:
163             valid = value in self._allowed
164
165         if self.type in [Types.Double, Types.Integer] and self.range:
166             (min, max) = self.range
167
168             value = float(value)
169
170             valid = (value >= min and value <= max) 
171         
172         valid = valid and self.is_valid_value(value)
173
174         if valid: 
175             if self.set_hook:
176                 # Hook receives old value, new value
177                 value = self.set_hook(self._value, value)
178
179             self._value = value
180         else:
181             raise ValueError("Invalid value {} for attribute {}"
182                              .format(value, self.name))
183
184     value = property(get_value, set_value)
185
186     def is_valid_value(self, value):
187         """ Attribute subclasses will override this method to add 
188         adequate validation"""
189         return True
190
191     @property
192     def has_changed(self):
193         """ Returns True if the value has changed from the default """
194         return self.value != self.default
195