description classes are generic classes with no testbed specific code.
[nepi.git] / src / nepi / core / description.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 from nepi.core.attributes import AttributesMap
4
5 class ConnectorType(object):
6     """A ConnectorType defines a kind of connector that can be used in an Object.
7     """
8     def __init__(self, connector_type_id, help, name, max = -1, min = 0):
9         super(ConnectorType, self).__init__()
10         """
11         ConnectorType(name, help, display_name, max, min):
12         - connector_type_id: (unique) identifier for this type. 
13             Typically: testbed_id + factory_id + name
14         - name: descriptive name for the user
15         - help: help text
16         - max: amount of connections that this type support, -1 for no limit
17         - min: minimum amount of connections to this type of connector
18         """
19         if max == -1:
20             max = sys.maxint
21         elif max <= 0:
22                 raise RuntimeError(
23              'The maximum number of connections allowed need to be more than 0')
24         if min < 0:
25             raise RuntimeError(
26              'The minimum number of connections allowed needs to be at least 0')
27         self._connector_type_id = connector_type_id
28         self._help = help
29         self._name = name
30         self._max = max
31         self._min = min
32         self._allowed_connections = list()
33
34     @property
35     def connector_type_id(self):
36         return self._connector_type_id
37
38     @property
39     def help(self):
40         return self._help
41
42     @property
43     def name(self):
44         return self._name
45
46     @property
47     def max(self):
48         return self._max
49
50     @property
51     def min(self):
52         return self._min
53
54     def add_allowed_connection(self, connector_type_id):
55         self._allowed_connections.append(connector_type_id)
56
57     def can_connect(self, connector_type_id):
58         return connector_type_id in self._allowed_connections
59
60 class Connector(object):
61     """A Connector sepcifies the connection points in an Object"""
62     def __init__(self, element, connector_type):
63         super(Connector, self).__init__()
64         self._element = element
65         self._connector_type = connector_type
66         self._connections = dict()
67
68     @property
69     def element(self):
70         return self._element
71
72     @property
73     def connector_type(self):
74         return self._connector_type
75
76     @property
77     def connections(self):
78         return self._connections.values()
79
80     def is_full(self):
81         """Return True if the connector has the maximum number of connections"""
82         return len(self.connections) == self.connector_type.max
83
84     def is_complete(self):
85         """Return True if the connector has the minimum number of connections"""
86         return len(self.connections) >= self.connector_type.min 
87
88     def connect(self, connector):
89         if self.is_full() or connector.is_full():
90             raise RuntimeError("Connector is full")    
91         if not self.can_connect(connector) or not connector.can_connect(self):
92             raise RuntimeError("Could not connect.")
93         self._connections[connector._key] = connector
94         connector._connections[self._key] = self
95
96     def disconnect(self, connector):
97         if connector._key not in self._connections or 
98             self._key not in connector._connections:
99                 raise RuntimeError("Could not disconnect.")
100         del self._connections[connector._key]
101         del connector._connections[self._key]
102
103     def can_connect(self, connector):
104         connector_type_id = connector.connector_type.connector_type_id
105         self.connector_type.can_connect(connector_type_id) 
106
107     def destroy(self):
108         for connector in self._connections:
109             self.disconnect(connector)
110         self._element = self._connectors = None
111
112     @property
113     def _key(self):
114         return "%d_%s" % (self.element.guid, 
115                 self.connector_type.connector_type_id)
116
117 class Trace(AttributesMap):
118     def __init__(self, name, help, enabled=False):
119         super(Trace, self).__init__()
120         self._name = name
121         self._help = help       
122         self._enabled = enabled
123     
124     @property
125     def name(self):
126         return self._name
127
128     @property
129     def help(self):
130         return self._help
131
132     @property
133     def is_enabled(self):
134         return self._enabled
135
136     def enable(self):
137         self._enabled = True
138
139     def disable(self):
140         self._enabled = False
141
142 class Element(AttributesMap):
143     def __init__(self, guid, testbed_id, factory, container = None):
144         super(Element, self).__init__()
145         # general unique id
146         self._guid = guid
147         # factory identifier or name
148         self._factory_id = factory.factory_id
149         # elements can be nested inside other 'container' elements
150         self._container = container
151         # traces for the element
152         self._traces = dict()
153         # connectors for the element
154         self._connectors = dict()
155         for connector_type in factory.connector_types:
156             connector = Connector(self, connector_type)
157             self._connectors[connector_type.connector_id] = connector
158
159     @property
160     def guid(self):
161         return self._guid
162
163     @property
164     def factory_id(self):
165         return self._factory_id
166
167     @property
168     def container(self):
169         return self._container
170
171     @property
172     def connectors(self):
173         return self._connectors.values()
174
175     @property
176     def traces(self):
177         return self._traces.values()
178
179     def connector(self, name):
180         return self._connectors[name]
181
182     def trace(self, name):
183         return self._traces[name]
184
185     def destroy(self):
186         super(Element, self).destroy()
187         for c in self.connectors:
188             c.destroy()         
189         for t in self.traces:
190             t.destroy()
191         self._connectors = self._traces = None
192
193 class Factory(AttributesMap):
194     def __init__(self, factory_id, help = None, category = None):
195         super(Factory, self).__init__()
196         self._factory_id = factory_id
197         self._help = help
198         self._category = category
199         self._connector_types = set()
200
201     @property
202     def factory_id(self):
203         return self._factory_id
204
205     @property
206     def help(self):
207         return self._help
208
209     @property
210     def category(self):
211         return self._category
212
213     @property
214     def connector_types(self):
215         return self._connector_types
216
217     def add_connector_type(self, connector_id, help, name, max = -1, min = 0):
218         connector_type = ConnectorType(connector_id, help, name, max, min)            
219         self._connector_types.add(connector_type)
220
221     def create(self, guid, testbed_design, container = None):
222         raise NotImplementedError
223
224     def destroy(self):
225         super(Factory, self).destroy()
226         self._connector_types = None
227
228 #TODO: Provide some way to identify that the providers and the factories
229 # belong to a specific testbed version
230 class Provider(object):
231     def __init__(self):
232         super(Provider, self).__init__()
233         self._factories = dict()
234
235     def factory(self, factory_id):
236         return self._factories[factory_id]
237
238     def add_factory(self, factory):
239         self._factories[factory.factory_id] = factory
240
241     def remove_factory(self, factory_id):
242         del self._factories[factory_id]
243
244     def list_factories(self):
245         return self._factories.keys()
246
247 class TestbedDescription(AttributeMap):
248     def __init__(self, guid_generator, testbed_id, testbed_version, provider):
249         super(TestbedDescription, self).__init__()
250         self._guid_generator = guid_generator
251         self._guid = guid_generator.next()
252         self._testbed_id = testbed_id
253         self._testbed_version = testbed_version
254         self._provider = provider
255         self._elements = dict()
256
257     @property
258     def guid(self):
259         return self._guid
260
261     @property
262     def testbed_id(self):
263         return self._testbed_id
264
265     @property
266     def testbed_version(self):
267         return self._testbed_version
268
269     @property
270     def provider(self):
271         return provider
272
273     @property
274     def elements(self):
275         return self._elements.values()
276
277     def create(self, factory_id):
278         guid = self.guid_generator.next()
279         factory = self._provider.factories(factory_id)
280         element = factory.create(guid, self)
281         self._elements[guid] = element
282
283     def delete(self, guid):
284         element = self._elements[guid]
285         del self._elements[guid]
286         element.destroy()
287
288     def destroy(self):
289         for guid, element in self._elements.iteitems():
290             del self._elements[guid]
291             element.destroy()
292         self._elements = None
293