2 # -*- coding: utf-8 -*-
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, \
12 P2PIFACE = "P2PNodeInterface"
13 TAPIFACE = "TapNodeInterface"
14 NODEIFACE = "NodeInterface"
16 APPLICATION = "Application"
18 NS3_TESTBED_ID = "ns3"
19 FDNETDEV = "ns3::FileDescriptorNetDevice"
21 ### Connection functions ####
23 def connect_switch(switch, interface):
24 switch.connect(interface)
26 #XXX: This connection function cannot be use to transfer a file descriptor
27 # to a remote tap device
28 def connect_fd_local(tap, fdnd):
31 fd = tap.file_descriptor
32 address = fdnd.socket_address
33 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
35 passfd.sendfd(sock, fd, '0')
36 # TODO: after succesful transfer, the tap device should close the fd
38 ### Creation functions ###
40 def create_node(testbed_instance, guid, parameters):
42 if "forward_X11" in parameters:
43 forward_X11 = parameters["forward_X11"]
44 del parameters["forward_X11"]
45 element = testbed_instance.netns.Node(forward_X11 = forward_X11)
46 testbed_instance.elements[guid] = element
48 def create_p2piface(testbed_instance, guid, parameters):
49 if guid in testbed_instance.elements:
50 # The interface pair was already instantiated
52 # search for the node asociated with the p2piface
53 node1_guid = testbed_instance.get_connected(guid, "node", "devs")
54 if len(node1_guid) == 0:
55 raise RuntimeError("Can't instantiate interface %d outside netns \
57 node1 = testbed_instance.elements[node1_guid[0]]
58 # search for the pair p2piface
59 p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
60 if len(p2p_guid) == 0:
61 raise RuntimeError("Can't instantiate p2p interface %d. \
62 Missing interface pair" % guid)
64 node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
65 if len(node2_guid) == 0:
66 raise RuntimeError("Can't instantiate interface %d outside netns \
68 node2 = testbed_instance.elements[node2_guid[0]]
69 element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
71 testbed_instance.elements[guid] = element1
72 testbed_instance.elements[guid2] = element2
74 def create_tapiface(testbed_instance, guid, parameters):
75 node_guid = testbed_instance.get_connected(guid, "node", "devs")
76 if len(node_guid) == 0:
77 raise RuntimeError("Can't instantiate interface %d outside netns \
79 node = testbed_instance.elements[node_guid[0]]
80 element = node.add_tap()
81 testbed_instance.elements[guid] = element
83 def create_nodeiface(testbed_instance, guid, parameters):
84 node_guid = testbed_instance.get_connected(guid, "node", "devs")
85 if len(node_guid) == 0:
86 raise RuntimeError("Can't instantiate interface %d outside netns \
88 node = testbed_instance.elements[node_guid[0]]
89 element = node.add_if()
90 testbed_instance.elements[guid] = element
92 def create_switch(testbed_instance, guid, parameters):
93 element = testbed_instance.netns.Switch()
94 testbed_instance.elements[guid] = element
96 def create_application(testbed_instance, guid, parameters):
97 testbed_instance.elements[guid] = None # Delayed construction
99 ### Start/Stop functions ###
101 def start_application(testbed_instance, guid, parameters, traces):
102 user = parameters["user"]
103 command = parameters["command"]
104 stdout = stderr = None
105 if "stdout" in traces:
106 filename = testbed_instance.trace_filename(guid, "stdout")
107 stdout = open(filename, "wb")
108 testbed_instance.follow_trace("stdout", stdout)
109 if "stderr" in traces:
110 filename = testbed_instance.trace_filename(guid, "stderr")
111 stderr = open(filename, "wb")
112 testbed_instance.follow_trace("stderr", stderr)
114 node_guid = testbed_instance.get_connected(guid, "node", "apps")
115 if len(node_guid) == 0:
116 raise RuntimeError("Can't instantiate interface %d outside netns \
118 node = testbed_instance.elements[node_guid[0]]
119 element = node.Popen(command, shell = True, stdout = stdout,
120 stderr = stderr, user = user)
121 testbed_instance.elements[guid] = element
123 ### Status functions ###
125 def status_application(testbed_instance, guid):
126 if guid not in testbed_instance.elements.keys():
127 return STATUS_NOT_STARTED
128 app = testbed_instance.elements[guid]
129 if app.poll() == None:
130 return STATUS_RUNNING
131 return STATUS_FINISHED
133 ### Factory information ###
135 connector_types = dict({
137 "help": "Connector from node to applications",
143 "help": "Connector from node to network interfaces",
149 "help": "Connector to a Node",
155 "help": "Connector to a P2PInterface",
161 "help": "Connector to a network interface that can receive a file descriptor",
167 "help": "Connector to a switch",
176 "from": (TESTBED_ID, NODE, "devs"),
177 "to": (TESTBED_ID, P2PIFACE, "node"),
182 "from": (TESTBED_ID, NODE, "devs"),
183 "to": (TESTBED_ID, TAPIFACE, "node"),
188 "from": (TESTBED_ID, NODE, "devs"),
189 "to": (TESTBED_ID, NODEIFACE, "node"),
194 "from": (TESTBED_ID, P2PIFACE, "p2p"),
195 "to": (TESTBED_ID, P2PIFACE, "p2p"),
200 "from": (TESTBED_ID, TAPIFACE, "fd"),
201 "to": (NS3_TESTBED_ID, FDNETDEV, "fd"),
202 "code": connect_fd_local,
206 "from": (TESTBED_ID, SWITCH, "devs"),
207 "to": (TESTBED_ID, NODEIFACE, "switch"),
208 "code": connect_switch,
212 "from": (TESTBED_ID, NODE, "apps"),
213 "to": (TESTBED_ID, APPLICATION, "node"),
220 "forward_X11": dict({
221 "name": "forward_X11",
222 "help": "Forward x11 from main namespace to the node",
223 "type": Attribute.BOOL,
227 "flags": Attribute.DesignOnly,
228 "validation_function": validation.is_bool
232 "help": "Mac address",
233 "type": Attribute.STRING,
237 "flags": Attribute.DesignOnly,
238 "validation_function": validation.is_mac_address
243 "type": Attribute.BOOL,
247 "validation_function": validation.is_bool
249 "device_name": dict({
251 "help": "Device name",
252 "type": Attribute.STRING,
256 "flags": Attribute.DesignOnly,
257 "validation_function": validation.is_string
261 "help": "Maximum transmition unit for device",
262 "type": Attribute.INTEGER,
266 "validation_function": validation.is_integer
270 "help": "Broadcast address",
271 "type": Attribute.STRING,
275 "validation_function": validation.is_string # TODO: should be is address!
279 "help": "Multicast enabled",
280 "type": Attribute.BOOL,
284 "validation_function": validation.is_bool
288 "help": "ARP enabled",
289 "type": Attribute.BOOL,
293 "validation_function": validation.is_bool
297 "help": "Command line string",
298 "type": Attribute.STRING,
302 "flags": Attribute.DesignOnly,
303 "validation_function": validation.is_string
307 "help": "System user",
308 "type": Attribute.STRING,
312 "flags": Attribute.DesignOnly,
313 "validation_function": validation.is_string
317 "help": "Standard input",
318 "type": Attribute.STRING,
322 "flags": Attribute.DesignOnly,
323 "validation_function": validation.is_string
325 "max_addresses": dict({
326 "name": "MaxAddresses",
327 "help": "Maximum number of addresses allowed by the device",
328 "type": Attribute.INTEGER,
332 "flags": Attribute.Invisible,
333 "validation_function": validation.is_integer
337 "help": "IP address family",
338 "type": Attribute.INTEGER,
342 "flags": Attribute.Invisible,
343 "validation_function": validation.is_integer
350 "help": "Standard output stream"
354 "help": "Application standard error",
358 factories_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
361 factories_info = dict({
363 "allow_routes": True,
364 "help": "Emulated Node with virtualized network stack",
365 "category": "topology",
366 "create_function": create_node,
367 "start_function": None,
368 "stop_function": None,
369 "status_function": None,
370 "box_attributes": ["forward_X11"],
371 "connector_types": ["devs", "apps"]
374 "allow_addresses": True,
375 "help": "Point to point network interface",
376 "category": "devices",
377 "create_function": create_p2piface,
378 "start_function": None,
379 "stop_function": None,
380 "status_function": None,
381 "factory_attributes": ["family", "max_addresses"],
382 "box_attributes": ["lladdr", "up", "device_name", "mtu",
383 "multicast", "broadcast", "arp"],
384 "connector_types": ["node", "p2p"]
387 "allow_addresses": True,
388 "help": "Tap device network interface",
389 "category": "devices",
390 "create_function": create_tapiface,
391 "start_function": None,
392 "stop_function": None,
393 "status_function": None,
394 "factory_attributes": ["family", "max_addresses"],
395 "box_attributes": ["lladdr", "up", "device_name", "mtu",
396 "multicast", "broadcast", "arp"],
397 "connector_types": ["node", "fd"]
400 "allow_addresses": True,
401 "help": "Node network interface",
402 "category": "devices",
403 "create_function": create_nodeiface,
404 "start_function": None,
405 "stop_function": None,
406 "status_function": None,
407 "factory_attributes": ["family", "max_addresses"],
408 "box_attributes": ["lladdr", "up", "device_name", "mtu",
409 "multicast", "broadcast", "arp"],
410 "connector_types": ["node", "switch"]
413 "display_name": "Switch",
414 "help": "Switch interface",
415 "category": "devices",
416 "create_function": create_switch,
417 "start_function": None,
418 "stop_function": None,
419 "status_function": None,
420 "box_attributes": ["up", "device_name", "mtu", "multicast"],
421 #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
422 #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
423 #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
424 #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
425 #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
426 "connector_types": ["devs"]
429 "help": "Generic executable command line application",
430 "category": "applications",
431 "create_function": create_application,
432 "start_function": start_application,
433 "stop_function": None,
434 "status_function": status_application,
435 "box_attributes": ["command", "user"],
436 "connector_types": ["node"],
437 "traces": ["stdout", "stderr"]
441 testbed_attributes = dict({
442 "enable_debug": dict({
443 "name": "enableDebug",
444 "help": "Enable netns debug output",
445 "type": Attribute.BOOL,
449 "validation_function": validation.is_bool
451 "home_directory": dict({
452 "name": "homeDirectory",
453 "help": "Path to the directory where traces and other files \
455 "type": Attribute.STRING,
459 "flags": Attribute.DesignOnly,
460 "validation_function": validation.is_string
464 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
466 def connections_types(self):
467 return connection_types
470 def connections(self):
474 def attributes(self):
482 def factories_order(self):
483 return factories_order
486 def factories_info(self):
487 return factories_info
490 def testbed_attributes(self):
491 return testbed_attributes