vlc wireless hybrid experiment example added
[nepi.git] / src / nepi / testbeds / netns / metadata_v01.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from constants import TESTBED_ID
5 from nepi.core import metadata
6 from nepi.core.attributes import Attribute
7 from nepi.util import validation
8 from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
9         STATUS_FINISHED
10
11 NODE = "Node"
12 P2PIFACE = "P2PNodeInterface"
13 TAPIFACE = "TapNodeInterface"
14 NODEIFACE = "NodeInterface"
15 SWITCH = "Switch"
16 APPLICATION = "Application"
17
18 NS3_TESTBED_ID = "ns3"
19 FDNETDEV = "ns3::FileDescriptorNetDevice"
20
21 ### Connection functions ####
22
23 def connect_switch(testbed_instance, switch_guid, interface_guid):
24     switch = testbed_instance._elements[switch_guid]
25     interface = testbed_instance._elements[interface_guid]
26     switch.connect(interface)
27    
28 def connect_fd(testbed_instance, tap_guid, cross_data):
29     import passfd
30     import socket
31     tap = testbed_instance._elements[tap_guid]
32     address = cross_data["LinuxSocketAddress"]
33     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
34     sock.connect(address)
35     passfd.sendfd(sock, tap.fd, '0')
36     # TODO: after succesful transfer, the tap device should close the fd
37
38 ### Creation functions ###
39
40 def create_node(testbed_instance, guid):
41     parameters = testbed_instance._get_parameters(guid)
42     forward_X11 = False
43     if "forward_X11" in parameters:
44         forward_X11 = parameters["forward_X11"]
45         del parameters["forward_X11"]
46     element = testbed_instance.netns.Node(forward_X11 = forward_X11)
47     testbed_instance.elements[guid] = element
48
49 def create_p2piface(testbed_instance, guid):
50     if guid in testbed_instance.elements:
51         # The interface pair was already instantiated
52         return
53     # search for the node asociated with the p2piface
54     node1_guid = testbed_instance.get_connected(guid, "node", "devs")
55     if len(node1_guid) == 0:
56         raise RuntimeError("Can't instantiate interface %d outside netns \
57                 node" % guid)
58     node1 = testbed_instance.elements[node1_guid[0]]
59     # search for the pair p2piface
60     p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
61     if len(p2p_guid) == 0:
62         raise RuntimeError("Can't instantiate p2p interface %d. \
63                 Missing interface pair" % guid)
64     guid2 = p2p_guid[0]
65     node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
66     if len(node2_guid) == 0:
67         raise RuntimeError("Can't instantiate interface %d outside netns \
68                 node" % guid2)
69     node2 = testbed_instance.elements[node2_guid[0]]
70     element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
71         node1, node2)
72     testbed_instance.elements[guid] = element1
73     testbed_instance.elements[guid2] = element2
74
75 def create_tapiface(testbed_instance, guid):
76     node_guid = testbed_instance.get_connected(guid, "node", "devs")
77     if len(node_guid) == 0:
78         raise RuntimeError("Can't instantiate interface %d outside netns \
79                 node" % guid)
80     node = testbed_instance.elements[node_guid[0]]
81     element = node.add_tap()
82     testbed_instance.elements[guid] = element
83
84 def create_nodeiface(testbed_instance, guid):
85     node_guid = testbed_instance.get_connected(guid, "node", "devs")
86     if len(node_guid) == 0:
87         raise RuntimeError("Can't instantiate interface %d outside netns \
88                 node" % guid)
89     node = testbed_instance.elements[node_guid[0]]
90     element = node.add_if()
91     testbed_instance.elements[guid] = element
92
93 def create_switch(testbed_instance, guid):
94     element = testbed_instance.netns.Switch()
95     testbed_instance.elements[guid] = element
96
97 def create_application(testbed_instance, guid):
98     testbed_instance.elements[guid] = None # Delayed construction 
99
100 ### Start/Stop functions ###
101
102 def start_application(testbed_instance, guid):
103     parameters = testbed_instance._get_parameters(guid)
104     traces = testbed_instance._get_traces(guid)
105     user = parameters["user"]
106     command = parameters["command"]
107     stdout = stderr = None
108     if "stdout" in traces:
109         filename = testbed_instance.trace_filename(guid, "stdout")
110         stdout = open(filename, "wb")
111         testbed_instance.follow_trace("stdout", stdout)
112     if "stderr" in traces:
113         filename = testbed_instance.trace_filename(guid, "stderr")
114         stderr = open(filename, "wb")
115         testbed_instance.follow_trace("stderr", stderr)
116
117     node_guid = testbed_instance.get_connected(guid, "node", "apps")
118     if len(node_guid) == 0:
119         raise RuntimeError("Can't instantiate interface %d outside netns \
120                 node" % guid)
121     node = testbed_instance.elements[node_guid[0]]
122     element  = node.Popen(command, shell = True, stdout = stdout, 
123             stderr = stderr, user = user)
124     testbed_instance.elements[guid] = element
125
126 ### Status functions ###
127
128 def status_application(testbed_instance, guid):
129     if guid not in testbed_instance.elements.keys():
130         return STATUS_NOT_STARTED
131     app = testbed_instance.elements[guid]
132     if app.poll() == None:
133         return STATUS_RUNNING
134     return STATUS_FINISHED
135
136 ### Configure functions ###
137
138 def configure_device(testbed_instance, guid):
139     element = testbed_instance._elements[guid]
140     if not guid in testbed_instance._add_address:
141         return
142     addresses = testbed_instance._add_address[guid]
143     for address in addresses:
144         (address, netprefix, broadcast) = address
145         # TODO: Decide if we should add a ipv4 or ipv6 address
146         element.add_v4_address(address, netprefix)
147
148 def configure_node(testbed_instance, guid):
149     element = testbed_instance._elements[guid]
150     if not guid in testbed_instance._add_route:
151         return
152     routes = testbed_instance._add_route[guid]
153     for route in routes:
154         (destination, netprefix, nexthop) = route
155         element.add_route(prefix = destination, prefix_len = netprefix,
156             nexthop = nexthop)
157
158 ### Factory information ###
159
160 connector_types = dict({
161     "apps": dict({
162                 "help": "Connector from node to applications", 
163                 "name": "apps",
164                 "max": -1, 
165                 "min": 0
166             }),
167     "devs": dict({
168                 "help": "Connector from node to network interfaces", 
169                 "name": "devs",
170                 "max": -1, 
171                 "min": 0
172             }),
173     "node": dict({
174                 "help": "Connector to a Node", 
175                 "name": "node",
176                 "max": 1, 
177                 "min": 1
178             }),
179     "p2p": dict({
180                 "help": "Connector to a P2PInterface", 
181                 "name": "p2p",
182                 "max": 1, 
183                 "min": 0
184             }),
185     "fd": dict({
186                 "help": "Connector to a network interface that can receive a file descriptor", 
187                 "name": "fd",
188                 "max": 1, 
189                 "min": 0
190             }),
191     "switch": dict({
192                 "help": "Connector to a switch", 
193                 "name": "switch",
194                 "max": 1, 
195                 "min": 0
196             })
197    })
198
199 connections = [
200     dict({
201         "from": (TESTBED_ID, NODE, "devs"),
202         "to":   (TESTBED_ID, P2PIFACE, "node"),
203         "can_cross": False
204     }),
205     dict({
206         "from": (TESTBED_ID, NODE, "devs"),
207         "to":   (TESTBED_ID, TAPIFACE, "node"),
208         "can_cross": False
209     }),
210     dict({
211         "from": (TESTBED_ID, NODE, "devs"),
212         "to":   (TESTBED_ID, NODEIFACE, "node"),
213         "can_cross": False
214     }),
215     dict({
216         "from": (TESTBED_ID, P2PIFACE, "p2p"),
217         "to":   (TESTBED_ID, P2PIFACE, "p2p"),
218         "can_cross": False
219     }),
220     dict({
221         "from": (TESTBED_ID, TAPIFACE, "fd"),
222         "to":   (NS3_TESTBED_ID, FDNETDEV, "fd"),
223         "compl_code": connect_fd,
224         "can_cross": True
225     }),
226      dict({
227         "from": (TESTBED_ID, SWITCH, "devs"),
228         "to":   (TESTBED_ID, NODEIFACE, "switch"),
229         "init_code": connect_switch,
230         "can_cross": False
231     }),
232     dict({
233         "from": (TESTBED_ID, NODE, "apps"),
234         "to":   (TESTBED_ID, APPLICATION, "node"),
235         "can_cross": False
236     })
237 ]
238
239 attributes = dict({
240     "forward_X11": dict({      
241                 "name": "forward_X11",
242                 "help": "Forward x11 from main namespace to the node",
243                 "type": Attribute.BOOL, 
244                 "value": False,
245                 "flags": Attribute.DesignOnly,
246                 "validation_function": validation.is_bool
247             }),
248     "lladdr": dict({      
249                 "name": "lladdr", 
250                 "help": "Mac address", 
251                 "type": Attribute.STRING,
252                 "flags": Attribute.DesignOnly,
253                 "validation_function": validation.is_mac_address
254             }),
255     "up": dict({
256                 "name": "up",
257                 "help": "Link up",
258                 "type": Attribute.BOOL,
259                 "value": False,
260                 "validation_function": validation.is_bool
261             }),
262     "device_name": dict({
263                 "name": "name",
264                 "help": "Device name",
265                 "type": Attribute.STRING,
266                 "flags": Attribute.DesignOnly,
267                 "validation_function": validation.is_string
268             }),
269     "mtu":  dict({
270                 "name": "mtu", 
271                 "help": "Maximum transmition unit for device",
272                 "type": Attribute.INTEGER,
273                 "validation_function": validation.is_integer
274             }),
275     "broadcast": dict({ 
276                 "name": "broadcast",
277                 "help": "Broadcast address",
278                 "type": Attribute.STRING,
279                 "validation_function": validation.is_string # TODO: should be is address!
280             }),
281     "multicast": dict({      
282                 "name": "multicast",
283                 "help": "Multicast enabled",
284                 "type": Attribute.BOOL,
285                 "value": False,
286                 "validation_function": validation.is_bool
287             }),
288     "arp": dict({
289                 "name": "arp",
290                 "help": "ARP enabled",
291                 "type": Attribute.BOOL,
292                 "value": False,
293                 "validation_function": validation.is_bool
294             }),
295     "command": dict({
296                 "name": "command",
297                 "help": "Command line string",
298                 "type": Attribute.STRING,
299                 "flags": Attribute.DesignOnly,
300                 "validation_function": validation.is_string
301             }),
302     "user": dict({
303                 "name": "user",
304                 "help": "System user",
305                 "type": Attribute.STRING,
306                 "flags": Attribute.DesignOnly,
307                 "validation_function": validation.is_string
308             }),
309     "stdin": dict({
310                 "name": "stdin",
311                 "help": "Standard input",
312                 "type": Attribute.STRING,
313                 "flags": Attribute.DesignOnly,
314                 "validation_function": validation.is_string
315             }),
316     })
317
318 traces = dict({
319     "stdout": dict({
320                 "name": "stdout",
321                 "help": "Standard output stream"
322               }),
323     "stderr": dict({
324                 "name": "stderr",
325                 "help": "Application standard error",
326         }) 
327     })
328
329 create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
330         APPLICATION ]
331
332 configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE, SWITCH, NODE,
333         APPLICATION ]
334
335 factories_info = dict({
336     NODE: dict({
337             "allow_routes": True,
338             "help": "Emulated Node with virtualized network stack",
339             "category": "topology",
340             "create_function": create_node,
341             "configure_function": configure_node,
342             "box_attributes": ["forward_X11"],
343             "connector_types": ["devs", "apps"]
344        }),
345     P2PIFACE: dict({
346             "allow_addresses": True,
347             "help": "Point to point network interface",
348             "category": "devices",
349             "create_function": create_p2piface,
350             "configure_function": configure_device,
351             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
352                 "multicast", "broadcast", "arp"],
353             "connector_types": ["node", "p2p"]
354        }),
355     TAPIFACE: dict({
356             "allow_addresses": True,
357             "help": "Tap device network interface",
358             "category": "devices",
359             "create_function": create_tapiface,
360             "configure_function": configure_device,
361             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
362                 "multicast", "broadcast", "arp"],
363             "connector_types": ["node", "fd"]
364         }),
365     NODEIFACE: dict({
366             "allow_addresses": True,
367             "help": "Node network interface",
368             "category": "devices",
369             "create_function": create_nodeiface,
370             "configure_function": configure_device,
371             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
372                 "multicast", "broadcast", "arp"],
373             "connector_types": ["node", "switch"]
374         }),
375     SWITCH: dict({
376             "display_name": "Switch",
377             "help": "Switch interface",
378             "category": "devices",
379             "create_function": create_switch,
380             "box_attributes": ["up", "device_name", "mtu", "multicast"],
381              #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
382              #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
383              #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
384              #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
385              #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
386            "connector_types": ["devs"]
387         }),
388     APPLICATION: dict({
389             "help": "Generic executable command line application",
390             "category": "applications",
391             "create_function": create_application,
392             "start_function": start_application,
393             "status_function": status_application,
394             "box_attributes": ["command", "user"],
395             "connector_types": ["node"],
396             "traces": ["stdout", "stderr"]
397         }),
398 })
399
400 testbed_attributes = dict({
401         "enable_debug": dict({
402                 "name": "enableDebug",
403                 "help": "Enable netns debug output",
404                 "type": Attribute.BOOL,
405                 "value": False,
406                 "validation_function": validation.is_bool
407             }),
408     })
409
410 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
411     @property
412     def connector_types(self):
413         return connector_types
414
415     @property
416     def connections(self):
417         return connections
418
419     @property
420     def attributes(self):
421         return attributes
422
423     @property
424     def traces(self):
425         return traces
426
427     @property
428     def create_order(self):
429         return create_order
430
431     @property
432     def configure_order(self):
433         return configure_order
434
435     @property
436     def factories_info(self):
437         return factories_info
438
439     @property
440     def testbed_attributes(self):
441         return testbed_attributes
442