Added routes to OMF nodes
[nepi.git] / src / nepi / testbeds / omf / metadata.py
1 # -*- coding: utf-8 -*-
2
3 import functools
4 import random
5 import weakref
6
7 from constants import TESTBED_ID, TESTBED_VERSION
8 from nepi.core import metadata
9 from nepi.core.attributes import Attribute
10 from nepi.util import tags, validation, ipaddr2
11 from nepi.util.constants import ApplicationStatus as AS, \
12         FactoryCategories as FC, DeploymentConfiguration as DC
13
14 ##############################################################################
15
16 class OmfResource(object):
17     def __init__(self, guid, tc):
18         super(OmfResource, self).__init__()
19         self._tc = weakref.ref(tc)
20         self._guid = guid
21
22     @property
23     def tc(self):
24         return self._tc and self._tc()
25
26     @property
27     def guid(self):
28         return self._guid
29
30     def configure(self):
31         pass
32
33     def start(self):
34         pass
35
36     def stop(self):
37         pass
38
39     def status(self):
40         pass
41
42     def shutdown(self):
43         pass
44
45 ## NODE #######################################################################
46
47 class OmfNode(OmfResource):
48     def __init__(self, guid, tc):
49         super(OmfNode, self).__init__(guid, tc)
50         self.hostname = self.tc._get_parameters(guid)['hostname']
51         self.tc.api.enroll_host(self.hostname)
52
53     def configure(self):
54         routes = self.tc._add_route.get(self.guid, [])
55         iface_guids = self.tc.get_connected(self.guid, "devs", "node")
56        
57         for route in routes:
58             (destination, netprefix, nexthop, metric, device) = route
59             netmask = ipaddr2.ipv4_mask2dot(netprefix)
60
61             # Validate that the interface is associated to the node
62             for iface_guid in iface_guids:
63                 iface = self.tc.elements.get(iface_guid)
64                 if iface.devname == device:
65                     self.tc.api.execute(self.hostname, 
66                         "Id#%s" % str(random.getrandbits(128)), 
67                         "add -net %s netmask %s dev %s" % (destination, netmask, iface.devname), 
68                         "/sbin/route", # path
69                         None, # env
70                      )
71                     break
72
73 ## APPLICATION ################################################################
74
75 class OmfApplication(OmfResource):
76     def __init__(self, guid, tc):
77         super(OmfApplication, self).__init__(guid, tc)
78         node_guids = tc.get_connected(guid, "node", "apps")
79         if len(node_guids) == 0:
80             raise RuntimeError("Can't instantiate interface %d outside node" % guid)
81
82         self._node_guid = node_guids[0] 
83         self.app_id = None
84         self.arguments = None
85         self.path = None
86         self.env = None
87
88     def start(self):
89         node = self.tc.elements.get(self._node_guid)
90         self.tc.api.execute(node.hostname, 
91                 self.appId, 
92                 self.arguments, 
93                 self.path,
94                 self.env)
95
96     def stop(self):
97         node = self.tc.elements.get(self._node_guid)
98         self.tc.api.exit(node.hostname, 
99                 self.appId) 
100
101     def status(self):
102         if guid not in testbed_instance.elements.keys():
103             return AS.STATUS_NOT_STARTED
104         return AS.STATUS_RUNNING
105         # TODO!!!!
106         #return AS.STATUS_FINISHED
107
108
109 ## WIFIIFACE ########################################################
110
111 class OmfWifiInterface(OmfResource):
112     alias2name = dict({'w0':'wlan0', 'w1':'wlan1'})
113
114     def __init__(self, guid, tc):
115         super(OmfWifiInterface, self).__init__(guid, tc)
116         node_guids = tc.get_connected(guid, "node", "devs")
117         if len(node_guids) == 0:
118             raise RuntimeError("Can't instantiate interface %d outside node" % guid)
119
120         self._node_guid = node_guids[0] 
121         self.alias = None
122         self.mode = None
123         self.type = None
124         self.essid = None
125         self.channel = None
126         self.ip = None
127         self.devname = None
128
129     def __setattr__(self, name, value):
130         if name == "alias":
131             self.devname = self.alias2name.get(value)
132
133         if name in ["ip", "mode", "type", "essid", "channel"]:
134             node = self.tc.elements.get(self._node_guid)    
135             attribute = "net/%s/%s" % (self.alias, name)
136             self._tc().api.configure(node.hostname, attribute, value)
137         
138         super(OmfWifiInterface, self).__setattr__(name, value)
139
140 # Factories
141 NODE = "Node"
142 WIFIIFACE = "WifiInterface"
143 CHANNEL = "Channel"
144 OMFAPPLICATION = "OmfApplication"
145
146 def create(factory, testbed_instance, guid):
147     clazz = OmfResource
148     if factory == NODE:
149         clazz = OmfNode
150     elif factory == OMFAPPLICATION:
151         clazz = OmfApplication
152     elif factory == WIFIIFACE:
153         clazz = OmfWifiInterface
154
155     element = clazz(guid, testbed_instance)
156     #import pdb; pdb.set_trace()
157     testbed_instance._elements[guid] = element
158
159 def start(testbed_instance, guid):
160     element = testbed_instance.elements.get(guid)
161     element.start()
162
163 def stop(testbed_instance, guid):
164     element = testbed_instance.elements.get(guid)
165     element.stop()
166
167 def status(testbed_instance, guid):
168     element = testbed_instance.elements.get(guid)
169     return element.status()
170
171 def configure(testbed_instance, guid):
172     element = testbed_instance.elements.get(guid)
173     return element.configure()
174
175 ### Factory information ###
176
177 connector_types = dict({
178     "apps": dict({
179                 "help": "Connector from node to applications", 
180                 "name": "apps",
181                 "max": -1, 
182                 "min": 0
183             }),
184     "devs": dict({
185                 "help": "Connector to network interfaces", 
186                 "name": "devs",
187                 "max": -1, 
188                 "min": 0
189             }),
190     "chan": dict({
191                 "help": "Connector from a device to a channel", 
192                 "name": "chan",
193                 "max": 1, 
194                 "min": 1
195             }),
196     "node": dict({
197                 "help": "Connector to a Node", 
198                 "name": "node",
199                 "max": 1, 
200                 "min": 1
201             }),
202    })
203
204 connections = [
205     dict({
206         "from": (TESTBED_ID, NODE, "devs"),
207         "to":   (TESTBED_ID, WIFIIFACE, "node"),
208         "can_cross": False
209     }),
210     dict({
211         "from": (TESTBED_ID, WIFIIFACE, "chan"),
212         "to":   (TESTBED_ID, CHANNEL, "devs"),
213         "can_cross": False
214     }),
215     dict({
216         "from": (TESTBED_ID, NODE, "apps"),
217         "to":   (TESTBED_ID, OMFAPPLICATION, "node"),
218         "can_cross": False
219     }),
220  ]
221
222 attributes = dict({
223     "appId": dict({
224                 "name": "appId",
225                 "help": "Application id",
226                 "type": Attribute.STRING,
227                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
228                 "validation_function": validation.is_string
229             }),
230     "arguments": dict({
231                 "name": "arguments",
232                 "help": "Application arguments",
233                 "type": Attribute.STRING,
234                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
235                 "validation_function": validation.is_string
236             }),
237     "path": dict({
238                 "name": "path",
239                 "help": "Path to binary (e.g '/opt/vlc-1.1.13/vlc')",
240                 "type": Attribute.STRING,
241                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
242                 "validation_function": validation.is_string
243             }),
244     "env": dict({
245                 "name": "env",
246                 "help": "String with space separated values of environment variables to set before starting application (e.g 'FOO=foo BAR=bar')",
247                 "type": Attribute.STRING,
248                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
249                 "validation_function": validation.is_string
250             }),
251     "hostname": dict({
252                 "name": "hostname",
253                 "help": "Hostname for the target OMF node",
254                 "type": Attribute.STRING,
255                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
256                 "validation_function": validation.is_string
257             }),
258     "mode": dict({
259                 "name": "mode",
260                 "help": "Corresponds to the OMF attributes net/w0/mode",
261                 "type": Attribute.STRING,
262                 "flags": Attribute.NoDefaultValue, 
263                 "validation_function": validation.is_string
264             }),
265     "type": dict({
266                 "name": "type",
267                 "help": "Corresponds to the OMF attributes net/w0/type",
268                 "type": Attribute.STRING,
269                 "flags": Attribute.NoDefaultValue, 
270                 "validation_function": validation.is_string
271             }),
272     "channel": dict({
273                 "name": "channel",
274                 "help": "Corresponds to the OMF attributes net/w0/channel",
275                 "type": Attribute.STRING,
276                 "flags": Attribute.NoDefaultValue, 
277                 "validation_function": validation.is_string
278             }),
279     "essid": dict({
280                 "name": "essid",
281                 "help": "Corresponds to the OMF attributes net/w0/essid",
282                 "type": Attribute.STRING,
283                 "flags": Attribute.NoDefaultValue, 
284                 "validation_function": validation.is_string
285             }),
286     "ip": dict({
287                 "name": "ip",
288                 "help": "Corresponds to the OMF attributes net/w0/ip",
289                 "type": Attribute.STRING,
290                 "flags": Attribute.NoDefaultValue, 
291                 "validation_function": validation.is_ip4_address
292             }),
293     "alias": dict({
294                 "name": "alias",
295                 "help": "Alias for device (e.g. w0, w1, etc)",
296                 "type": Attribute.STRING,
297                 "value": "w0",
298                 "flags": Attribute.NoDefaultValue, 
299                 "validation_function": validation.is_string
300             }),
301     })
302
303 traces = dict()
304
305 create_order = [ NODE, WIFIIFACE, CHANNEL, OMFAPPLICATION ]
306 configure_order = [ WIFIIFACE,  NODE, CHANNEL, OMFAPPLICATION ]
307
308 factories_info = dict({
309     NODE: dict({
310             "help": "OMF Node",
311             "category": FC.CATEGORY_NODES,
312             "create_function": functools.partial(create, NODE),
313             "configure_function": configure,
314             "box_attributes": ["hostname"],
315             "connector_types": ["devs", "apps"],
316             "tags": [tags.NODE, tags.ALLOW_ROUTES],
317        }),
318     WIFIIFACE: dict({
319             "help": "Wireless network interface",
320             "category": FC.CATEGORY_DEVICES,
321             "create_function": functools.partial(create, WIFIIFACE),
322             "configure_function": configure,
323             "box_attributes": ["mode", "type", "channel", "essid", "ip", "alias"],
324             "connector_types": ["node", "chan"],
325             "tags": [tags.INTERFACE, tags.HAS_ADDRESSES],
326        }),
327     CHANNEL: dict({
328             "help": "Wireless channel",
329             "category": FC.CATEGORY_DEVICES,
330             "create_function": create,
331             "create_function": functools.partial(create, CHANNEL),
332             "box_attributes": ["mode", "type", "channel", "essid"],
333             "connector_types": ["devs"],
334        }),
335     OMFAPPLICATION: dict({
336             "help": "Generic executable command line application",
337             "category": FC.CATEGORY_APPLICATIONS,
338             "create_function": functools.partial(create, OMFAPPLICATION),
339             "start_function": start,
340             "stop_function": stop,
341             "status_function": status,
342             "box_attributes": ["appId", "arguments", "path", "env"],
343             "connector_types": ["node"],
344             "tags": [tags.APPLICATION],
345         }),
346 })
347
348 testbed_attributes = dict({
349     "enable_debug": dict({
350             "name": "enableDebug",
351             "help": "Enable netns debug output",
352             "type": Attribute.BOOL,
353             "value": False,
354             "validation_function": validation.is_bool
355         }),
356     "xmppSlice": dict({
357                 "name": "xmppSlice",
358                 "help": "OMF slice",
359                 "type": Attribute.STRING,
360                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
361                 "validation_function": validation.is_string
362             }),
363     "xmppHost": dict({
364                 "name": "xmppHost",
365                 "help": "OMF XMPP server host",
366                 "type": Attribute.STRING,
367                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
368                 "validation_function": validation.is_string
369             }),
370     "xmppPort": dict({
371                 "name": "xmppPort",
372                 "help": "OMF XMPP service port",
373                 "type": Attribute.INTEGER,
374                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
375                 "validation_function": validation.is_integer
376             }),
377     "xmppPassword": dict({
378                 "name": "xmppPassword",
379                 "help": "OMF XMPP slice password",
380                 "type": Attribute.STRING,
381                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
382                 "validation_function": validation.is_string
383             }),
384     "xmppRoot": dict({
385                 "name": "xmppRoot",
386                 "help": "Root node of the xmpp server pubsub tree",
387                 "type": Attribute.STRING,
388                 "value": "OMF",
389                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
390                 "validation_function": validation.is_string
391             }),
392     })
393
394 supported_recovery_policies = [
395         DC.POLICY_FAIL,
396     ]
397
398 class MetadataInfo(metadata.MetadataInfo):
399     @property
400     def connector_types(self):
401         return connector_types
402
403     @property
404     def connections(self):
405         return connections
406
407     @property
408     def attributes(self):
409         return attributes
410
411     @property
412     def traces(self):
413         return traces
414
415     @property
416     def create_order(self):
417         return create_order
418
419     @property
420     def configure_order(self):
421         return configure_order
422
423     @property
424     def factories_info(self):
425         return factories_info
426
427     @property
428     def testbed_attributes(self):
429         return testbed_attributes
430
431     @property
432     def testbed_id(self):
433         return TESTBED_ID
434
435     @property
436     def testbed_version(self):
437         return TESTBED_VERSION
438     
439     @property
440     def supported_recover_policies(self):
441         return supported_recovery_policies
442