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