eee07f0bb50c25d48da47ba089fa4c77c957fb05
[nepi.git] / src / nepi / core / factory.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from nepi.core.attributes import AttributesMap, Attribute
5 from nepi.util import tags
6 from nepi.util.tags import Taggable
7
8 class AddressableMixin(object):
9     def __init__(self, guid, factory, testbed_guid, container = None):
10         super(AddressableMixin, self).__init__(guid, factory, testbed_guid, 
11                 container)
12         max_addr = self._factory_attributes["maxAddresses"]
13         self.set_attribute_value("maxAddresses", max_addr)
14         self._addresses = list()
15
16     @property
17     def addresses(self):
18         return self._addresses
19
20     @property
21     def max_addresses(self):
22         return self.get_attribute_value("maxAddresses")
23
24 class UserAddressableMixin(AddressableMixin):
25     def __init__(self, guid, factory, testbed_guid, container = None):
26         super(UserAddressableMixin, self).__init__(guid, factory, testbed_guid, 
27                 container)
28
29     def add_address(self):
30         if len(self._addresses) == self.max_addresses:
31             raise RuntimeError("Maximun number of addresses for this box reached.")
32         from nepi.core.design import Address
33         address = Address()
34         self._addresses.append(address)
35         return address
36
37     def delete_address(self, address):
38         self._addresses.remove(address)
39         del address
40
41     def destroy(self):
42         super(UserAddressableMixin, self).destroy()
43         for address in list(self.addresses):
44             self.delete_address(address)
45         self._addresses = None
46
47 class RoutableMixin(object):
48     def __init__(self, guid, factory, testbed_guid, container = None):
49         super(RoutableMixin, self).__init__(guid, factory, testbed_guid, 
50             container)
51         self._routes = list()
52
53     @property
54     def routes(self):
55         return self._routes
56
57 class UserRoutableMixin(RoutableMixin):
58     def __init__(self, guid, factory, testbed_guid, container = None):
59         super(UserRoutableMixin, self).__init__(guid, factory, testbed_guid, 
60             container)
61
62     def add_route(self):
63         from nepi.core.design import Route
64         route = Route()
65         self._routes.append(route)
66         return route
67
68     def delete_route(self, route):
69         self._routes.remove(route)
70         del route
71
72     def destroy(self):
73         super(UserRoutableMixin, self).destroy()
74         for route in list(self.routes):
75             self.delete_route(route)
76         self._route = None
77
78 def MixIn(MyClass, MixIn):
79     # Mixins are installed BEFORE "Box" because
80     # Box inherits from non-cooperative classes,
81     # so the MRO chain gets broken when it gets
82     # to Box.
83
84     # Install mixin
85     MyClass.__bases__ = (MixIn,) + MyClass.__bases__
86     
87     # Add properties
88     # Somehow it doesn't work automatically
89     for name in dir(MixIn):
90         prop = getattr(MixIn,name,None)
91         if isinstance(prop, property):
92             setattr(MyClass, name, prop)
93     
94     # Update name
95     MyClass.__name__ = MyClass.__name__.replace(
96         'Box',
97         MixIn.__name__.replace('MixIn','')+'Box',
98         1)
99
100 class Factory(AttributesMap, Taggable):
101     _box_class_cache = {}
102
103     def __init__(self, factory_id,
104             create_function, 
105             start_function, 
106             stop_function, 
107             status_function, 
108             configure_function, 
109             preconfigure_function,
110             prestart_function,
111             help = None,
112             category = None):
113
114         super(Factory, self).__init__()
115
116         self._factory_id = factory_id
117         self._create_function = create_function
118         self._start_function = start_function
119         self._stop_function = stop_function
120         self._status_function = status_function
121         self._configure_function = configure_function
122         self._preconfigure_function = preconfigure_function
123         self._prestart_function = prestart_function
124         self._help = help
125         self._category = category
126         self._connector_types = dict()
127         self._traces = dict()
128         self._box_attributes = AttributesMap()
129         self._factory = None
130
131     @property
132     def factory(self):
133         if self._factory:
134             return self._factory
135
136         from nepi.core.design import Box
137
138         if not self.has_addresses and not self.has_routes:
139             self._factory = Box
140         else:
141             addresses = 'w' if self.allow_addresses else ('r' if self.has_addresses else '-')
142             routes    = 'w' if self.allow_routes else ('r' if self.has_routes else '-')
143             key = addresses+routes
144             
145             if key in self._box_class_cache:
146                 self._factory = self._box_class_cache[key]
147             else:
148                 # Create base class
149                 class _factory(Box):
150                     def __init__(self, guid, factory, testbed_guid, container = None):
151                         super(_factory, self).__init__(guid, factory, testbed_guid, container)
152                 
153                 # Add mixins, one by one
154                 if self.allow_addresses:
155                     MixIn(_factory, UserAddressableMixin)
156                 elif self.has_addresses:
157                     MixIn(_factory, AddressableMixin)
158                     
159                 if self.allow_routes:
160                     MixIn(_factory, UserRoutableMixin)
161                 elif self.has_routes:
162                     MixIn(_factory, RoutableMixin)
163                 
164                 # Put into cache
165                 self._box_class_cache[key] = self._factory = _factory
166         return self._factory
167
168     @property
169     def factory_id(self):
170         return self._factory_id
171
172     @property
173     def allow_addresses(self):
174         return self.has_tag(tags.ALLOW_ADDRESSES)
175
176     @property
177     def allow_routes(self):
178         return self.has_tag(tags.ALLOW_ROUTES)
179
180     @property
181     def has_addresses(self):
182         return self.has_tag(tags.HAS_ADDRESSES) or \
183                 self.allow_addresses
184
185     @property
186     def has_routes(self):
187         return self.has_tag(tags.HAS_ROUTES) or \
188                 self.allow_routes
189
190     @property
191     def help(self):
192         return self._help
193
194     @property
195     def category(self):
196         return self._category
197
198     @property
199     def connector_types(self):
200         return self._connector_types.values()
201
202     @property
203     def traces(self):
204         return self._traces.values()
205
206     @property
207     def traces_list(self):
208         return self._traces.keys()
209
210     @property
211     def box_attributes(self):
212         return self._box_attributes
213
214     @property
215     def create_function(self):
216         return self._create_function
217
218     @property
219     def prestart_function(self):
220         return self._prestart_function
221
222     @property
223     def start_function(self):
224         return self._start_function
225
226     @property
227     def stop_function(self):
228         return self._stop_function
229
230     @property
231     def status_function(self):
232         return self._status_function
233
234     @property
235     def configure_function(self):
236         return self._configure_function
237
238     @property
239     def preconfigure_function(self):
240         return self._preconfigure_function
241
242     def connector_type(self, name):
243         return self._connector_types[name]
244
245     def add_connector_type(self, connector_type):
246         self._connector_types[connector_type.name] = connector_type
247
248     def add_trace(self, name, help, enabled = False):
249         self._traces[name] = (name, help, enabled)
250
251     def add_box_attribute(self, name, help, type, value = None, range = None,
252         allowed = None, flags = Attribute.NoFlags, validation_function = None,
253         category = None):
254         self._box_attributes.add_attribute(name, help, type, value, range,
255                 allowed, flags, validation_function, category)
256
257     def create(self, guid, testbed_description):
258         return self.factory(guid, self, testbed_description.guid)
259
260     def destroy(self):
261         super(Factory, self).destroy()
262         self._connector_types = None
263