Metadata for ns3 v 3.9.rc3
[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 AF_INET, 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, interface):
24     switch.connect(interface)
25    
26 #XXX: This connection function cannot be use to transfer a file descriptor
27 # to a remote tap device
28 def connect_fd_local(testbed_instance, tap, fdnd):
29     import passfd
30     import socket
31     fd = tap.file_descriptor
32     address = fdnd.socket_address
33     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
34     sock.connect(address)
35     passfd.sendfd(sock, 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 ### Factory information ###
137
138 connector_types = dict({
139     "apps": dict({
140                 "help": "Connector from node to applications", 
141                 "name": "apps",
142                 "max": -1, 
143                 "min": 0
144             }),
145     "devs": dict({
146                 "help": "Connector from node to network interfaces", 
147                 "name": "devs",
148                 "max": -1, 
149                 "min": 0
150             }),
151     "node": dict({
152                 "help": "Connector to a Node", 
153                 "name": "node",
154                 "max": 1, 
155                 "min": 1
156             }),
157     "p2p": dict({
158                 "help": "Connector to a P2PInterface", 
159                 "name": "p2p",
160                 "max": 1, 
161                 "min": 0
162             }),
163     "fd": dict({
164                 "help": "Connector to a network interface that can receive a file descriptor", 
165                 "name": "fd",
166                 "max": 1, 
167                 "min": 0
168             }),
169     "switch": dict({
170                 "help": "Connector to a switch", 
171                 "name": "switch",
172                 "max": 1, 
173                 "min": 0
174             })
175    })
176
177 connections = [
178     dict({
179         "from": (TESTBED_ID, NODE, "devs"),
180         "to":   (TESTBED_ID, P2PIFACE, "node"),
181         "code": None,
182         "can_cross": False
183     }),
184     dict({
185         "from": (TESTBED_ID, NODE, "devs"),
186         "to":   (TESTBED_ID, TAPIFACE, "node"),
187         "code": None,
188         "can_cross": False
189     }),
190     dict({
191         "from": (TESTBED_ID, NODE, "devs"),
192         "to":   (TESTBED_ID, NODEIFACE, "node"),
193         "code": None,
194         "can_cross": False
195     }),
196     dict({
197         "from": (TESTBED_ID, P2PIFACE, "p2p"),
198         "to":   (TESTBED_ID, P2PIFACE, "p2p"),
199         "code": None,
200         "can_cross": False
201     }),
202     dict({
203         "from": (TESTBED_ID, TAPIFACE, "fd"),
204         "to":   (NS3_TESTBED_ID, FDNETDEV, "fd"),
205         "code": connect_fd_local,
206         "can_cross": True
207     }),
208      dict({
209         "from": (TESTBED_ID, SWITCH, "devs"),
210         "to":   (TESTBED_ID, NODEIFACE, "switch"),
211         "code": connect_switch,
212         "can_cross": False
213     }),
214     dict({
215         "from": (TESTBED_ID, NODE, "apps"),
216         "to":   (TESTBED_ID, APPLICATION, "node"),
217         "code": None,
218         "can_cross": False
219     })
220 ]
221
222 attributes = dict({
223     "forward_X11": dict({      
224                 "name": "forward_X11",
225                 "help": "Forward x11 from main namespace to the node",
226                 "type": Attribute.BOOL, 
227                 "value": False,
228                 "range": None,
229                 "allowed": None,
230                 "flags": Attribute.DesignOnly,
231                 "validation_function": validation.is_bool
232             }),
233     "lladdr": dict({      
234                 "name": "lladdr", 
235                 "help": "Mac address", 
236                 "type": Attribute.STRING,
237                 "value": None,
238                 "range": None,
239                 "allowed": None,
240                 "flags": Attribute.DesignOnly,
241                 "validation_function": validation.is_mac_address
242             }),
243     "up": dict({
244                 "name": "up",
245                 "help": "Link up",
246                 "type": Attribute.BOOL,
247                 "value": False,
248                 "range": None,
249                 "allowed": None,
250                 "validation_function": validation.is_bool
251             }),
252     "device_name": dict({
253                 "name": "name",
254                 "help": "Device name",
255                 "type": Attribute.STRING,
256                 "value": None,
257                 "range": None,
258                 "allowed": None,
259                 "flags": Attribute.DesignOnly,
260                 "validation_function": validation.is_string
261             }),
262     "mtu":  dict({
263                 "name": "mtu", 
264                 "help": "Maximum transmition unit for device",
265                 "type": Attribute.INTEGER,
266                 "value": None, 
267                 "range": None, 
268                 "allowed": None, 
269                 "validation_function": validation.is_integer
270             }),
271     "broadcast": dict({ 
272                 "name": "broadcast",
273                 "help": "Broadcast address",
274                 "type": Attribute.STRING,
275                 "value": None,
276                 "range": None,
277                 "allowed": None,
278                 "validation_function": validation.is_string # TODO: should be is address!
279             }),
280     "multicast": dict({      
281                 "name": "multicast",
282                 "help": "Multicast enabled",
283                 "type": Attribute.BOOL,
284                 "value": False,
285                 "range": None,
286                 "allowed": None,
287                 "validation_function": validation.is_bool
288             }),
289     "arp": dict({
290                 "name": "arp",
291                 "help": "ARP enabled",
292                 "type": Attribute.BOOL,
293                 "value": False,
294                 "range": None,
295                 "allowed": None,
296                 "validation_function": validation.is_bool
297             }),
298     "command": dict({
299                 "name": "command",
300                 "help": "Command line string",
301                 "type": Attribute.STRING,
302                 "value": None,
303                 "range": None,
304                 "allowed": None,
305                 "flags": Attribute.DesignOnly,
306                 "validation_function": validation.is_string
307             }),
308     "user": dict({
309                 "name": "user",
310                 "help": "System user",
311                 "type": Attribute.STRING,
312                 "value": None,
313                 "range": None,
314                 "allowed": None,
315                 "flags": Attribute.DesignOnly,
316                 "validation_function": validation.is_string
317             }),
318     "stdin": dict({
319                 "name": "stdin",
320                 "help": "Standard input",
321                 "type": Attribute.STRING,
322                 "value": None,
323                 "range": None,
324                 "allowed": None,
325                 "flags": Attribute.DesignOnly,
326                 "validation_function": validation.is_string
327             }),
328      "max_addresses": dict({
329                 "name": "MaxAddresses",
330                 "help": "Maximum number of addresses allowed by the device",
331                 "type": Attribute.INTEGER,
332                 "value": None,
333                 "range": None,
334                 "allowed": None,
335                 "flags": Attribute.Invisible,
336                 "validation_function": validation.is_integer
337             }),
338      "family": dict({
339                 "name": "Family",
340                 "help": "IP address family",
341                 "type": Attribute.INTEGER,
342                 "value": AF_INET,
343                 "range": None,
344                 "allowed": None,
345                 "flags": Attribute.Invisible,
346                 "validation_function": validation.is_integer
347             }),
348     })
349
350 traces = dict({
351     "stdout": dict({
352                 "name": "stdout",
353                 "help": "Standard output stream"
354               }),
355     "stderr": dict({
356                 "name": "stderr",
357                 "help": "Application standard error",
358         }) 
359     })
360
361 factories_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
362         APPLICATION ]
363
364 factories_info = dict({
365     NODE: dict({
366             "allow_routes": True,
367             "help": "Emulated Node with virtualized network stack",
368             "category": "topology",
369             "create_function": create_node,
370             "start_function": None,
371             "stop_function": None,
372             "status_function": None,
373             "box_attributes": ["forward_X11"],
374             "connector_types": ["devs", "apps"]
375        }),
376     P2PIFACE: dict({
377             "allow_addresses": True,
378             "help": "Point to point network interface",
379             "category": "devices",
380             "create_function": create_p2piface,
381             "start_function": None,
382             "stop_function": None,
383             "status_function": None,
384             "factory_attributes": ["family", "max_addresses"],
385             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
386                 "multicast", "broadcast", "arp"],
387             "connector_types": ["node", "p2p"]
388        }),
389     TAPIFACE: dict({
390             "allow_addresses": True,
391             "help": "Tap device network interface",
392             "category": "devices",
393             "create_function": create_tapiface,
394             "start_function": None,
395             "stop_function": None,
396             "status_function": None,
397             "factory_attributes": ["family", "max_addresses"],
398             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
399                 "multicast", "broadcast", "arp"],
400             "connector_types": ["node", "fd"]
401         }),
402     NODEIFACE: dict({
403             "allow_addresses": True,
404             "help": "Node network interface",
405             "category": "devices",
406             "create_function": create_nodeiface,
407             "start_function": None,
408             "stop_function": None,
409             "status_function": None,
410             "factory_attributes": ["family", "max_addresses"],
411             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
412                 "multicast", "broadcast", "arp"],
413             "connector_types": ["node", "switch"]
414         }),
415     SWITCH: dict({
416             "display_name": "Switch",
417             "help": "Switch interface",
418             "category": "devices",
419             "create_function": create_switch,
420             "start_function": None,
421             "stop_function": None,
422             "status_function": None,
423             "box_attributes": ["up", "device_name", "mtu", "multicast"],
424              #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
425              #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
426              #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
427              #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
428              #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
429            "connector_types": ["devs"]
430         }),
431     APPLICATION: dict({
432             "help": "Generic executable command line application",
433             "category": "applications",
434             "create_function": create_application,
435             "start_function": start_application,
436             "stop_function": None,
437             "status_function": status_application,
438             "box_attributes": ["command", "user"],
439             "connector_types": ["node"],
440             "traces": ["stdout", "stderr"]
441         }),
442 })
443
444 testbed_attributes = dict({
445         "enable_debug": dict({
446                 "name": "enableDebug",
447                 "help": "Enable netns debug output",
448                 "type": Attribute.BOOL,
449                 "value": False,
450                 "range": None,
451                 "allowed": None,
452                 "validation_function": validation.is_bool
453             }),
454          "home_directory": dict({
455                 "name": "homeDirectory",
456                 "help": "Path to the directory where traces and other files \
457                         will be stored",
458                 "type": Attribute.STRING,
459                 "value": False,
460                 "range": None,
461                 "allowed": None,
462                 "flags": Attribute.DesignOnly,
463                 "validation_function": validation.is_string
464             })
465     })
466
467 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
468     @property
469     def connections_types(self):
470         return connection_types
471
472     @property
473     def connections(self):
474         return connections
475
476     @property
477     def attributes(self):
478         return attributes
479
480     @property
481     def traces(self):
482         return traces
483
484     @property
485     def factories_order(self):
486         return factories_order
487
488     @property
489     def factories_info(self):
490         return factories_info
491
492     @property
493     def testbed_attributes(self):
494         return testbed_attributes
495