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 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(testbed_instance, switch, interface):
24 switch.connect(interface)
26 def connect_fd(testbed_instance, tap, cross_data):
29 fd = tap.file_descriptor
30 address = cross_data["LinuxSocketAddress"]
31 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
33 passfd.sendfd(sock, fd, '0')
34 # TODO: after succesful transfer, the tap device should close the fd
36 ### Creation functions ###
38 def create_node(testbed_instance, guid):
39 parameters = testbed_instance._get_parameters(guid)
41 if "forward_X11" in parameters:
42 forward_X11 = parameters["forward_X11"]
43 del parameters["forward_X11"]
44 element = testbed_instance.netns.Node(forward_X11 = forward_X11)
45 testbed_instance.elements[guid] = element
47 def create_p2piface(testbed_instance, guid):
48 if guid in testbed_instance.elements:
49 # The interface pair was already instantiated
51 # search for the node asociated with the p2piface
52 node1_guid = testbed_instance.get_connected(guid, "node", "devs")
53 if len(node1_guid) == 0:
54 raise RuntimeError("Can't instantiate interface %d outside netns \
56 node1 = testbed_instance.elements[node1_guid[0]]
57 # search for the pair p2piface
58 p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
59 if len(p2p_guid) == 0:
60 raise RuntimeError("Can't instantiate p2p interface %d. \
61 Missing interface pair" % guid)
63 node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
64 if len(node2_guid) == 0:
65 raise RuntimeError("Can't instantiate interface %d outside netns \
67 node2 = testbed_instance.elements[node2_guid[0]]
68 element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
70 testbed_instance.elements[guid] = element1
71 testbed_instance.elements[guid2] = element2
73 def create_tapiface(testbed_instance, guid):
74 node_guid = testbed_instance.get_connected(guid, "node", "devs")
75 if len(node_guid) == 0:
76 raise RuntimeError("Can't instantiate interface %d outside netns \
78 node = testbed_instance.elements[node_guid[0]]
79 element = node.add_tap()
80 testbed_instance.elements[guid] = element
82 def create_nodeiface(testbed_instance, guid):
83 node_guid = testbed_instance.get_connected(guid, "node", "devs")
84 if len(node_guid) == 0:
85 raise RuntimeError("Can't instantiate interface %d outside netns \
87 node = testbed_instance.elements[node_guid[0]]
88 element = node.add_if()
89 testbed_instance.elements[guid] = element
91 def create_switch(testbed_instance, guid):
92 element = testbed_instance.netns.Switch()
93 testbed_instance.elements[guid] = element
95 def create_application(testbed_instance, guid):
96 testbed_instance.elements[guid] = None # Delayed construction
98 ### Start/Stop functions ###
100 def start_application(testbed_instance, guid):
101 parameters = testbed_instance._get_parameters(guid)
102 traces = testbed_instance._get_traces(guid)
103 user = parameters["user"]
104 command = parameters["command"]
105 stdout = stderr = None
106 if "stdout" in traces:
107 filename = testbed_instance.trace_filename(guid, "stdout")
108 stdout = open(filename, "wb")
109 testbed_instance.follow_trace("stdout", stdout)
110 if "stderr" in traces:
111 filename = testbed_instance.trace_filename(guid, "stderr")
112 stderr = open(filename, "wb")
113 testbed_instance.follow_trace("stderr", stderr)
115 node_guid = testbed_instance.get_connected(guid, "node", "apps")
116 if len(node_guid) == 0:
117 raise RuntimeError("Can't instantiate interface %d outside netns \
119 node = testbed_instance.elements[node_guid[0]]
120 element = node.Popen(command, shell = True, stdout = stdout,
121 stderr = stderr, user = user)
122 testbed_instance.elements[guid] = element
124 ### Status functions ###
126 def status_application(testbed_instance, guid):
127 if guid not in testbed_instance.elements.keys():
128 return STATUS_NOT_STARTED
129 app = testbed_instance.elements[guid]
130 if app.poll() == None:
131 return STATUS_RUNNING
132 return STATUS_FINISHED
134 ### Configure functions ###
136 def configure_device(testbed_instance, guid):
137 element = testbed_instance._elements[guid]
138 if not guid in testbed_instance._add_address:
140 addresses = testbed_instance._add_address[guid]
141 for address in addresses:
142 (address, netprefix, broadcast) = address
143 # TODO: Decide if we should add a ipv4 or ipv6 address
144 element.add_v4_address(address, netprefix)
146 def configure_node(testbed_instance, guid):
147 element = testbed_instance._elements[guid]
148 if not guid in testbed_instance._add_route:
150 routes = testbed_instance._add_route[guid]
152 (destination, netprefix, nexthop) = route
153 element.add_route(prefix = destination, prefix_len = netprefix,
156 ### Factory information ###
158 connector_types = dict({
160 "help": "Connector from node to applications",
166 "help": "Connector from node to network interfaces",
172 "help": "Connector to a Node",
178 "help": "Connector to a P2PInterface",
184 "help": "Connector to a network interface that can receive a file descriptor",
190 "help": "Connector to a switch",
199 "from": (TESTBED_ID, NODE, "devs"),
200 "to": (TESTBED_ID, P2PIFACE, "node"),
204 "from": (TESTBED_ID, NODE, "devs"),
205 "to": (TESTBED_ID, TAPIFACE, "node"),
209 "from": (TESTBED_ID, NODE, "devs"),
210 "to": (TESTBED_ID, NODEIFACE, "node"),
214 "from": (TESTBED_ID, P2PIFACE, "p2p"),
215 "to": (TESTBED_ID, P2PIFACE, "p2p"),
219 "from": (TESTBED_ID, TAPIFACE, "fd"),
220 "to": (NS3_TESTBED_ID, FDNETDEV, "fd"),
221 "compl_code": connect_fd,
225 "from": (TESTBED_ID, SWITCH, "devs"),
226 "to": (TESTBED_ID, NODEIFACE, "switch"),
227 "init_code": connect_switch,
231 "from": (TESTBED_ID, NODE, "apps"),
232 "to": (TESTBED_ID, APPLICATION, "node"),
238 "forward_X11": dict({
239 "name": "forward_X11",
240 "help": "Forward x11 from main namespace to the node",
241 "type": Attribute.BOOL,
243 "flags": Attribute.DesignOnly,
244 "validation_function": validation.is_bool
248 "help": "Mac address",
249 "type": Attribute.STRING,
250 "flags": Attribute.DesignOnly,
251 "validation_function": validation.is_mac_address
256 "type": Attribute.BOOL,
258 "validation_function": validation.is_bool
260 "device_name": dict({
262 "help": "Device name",
263 "type": Attribute.STRING,
264 "flags": Attribute.DesignOnly,
265 "validation_function": validation.is_string
269 "help": "Maximum transmition unit for device",
270 "type": Attribute.INTEGER,
271 "validation_function": validation.is_integer
275 "help": "Broadcast address",
276 "type": Attribute.STRING,
277 "validation_function": validation.is_string # TODO: should be is address!
281 "help": "Multicast enabled",
282 "type": Attribute.BOOL,
284 "validation_function": validation.is_bool
288 "help": "ARP enabled",
289 "type": Attribute.BOOL,
291 "validation_function": validation.is_bool
295 "help": "Command line string",
296 "type": Attribute.STRING,
297 "flags": Attribute.DesignOnly,
298 "validation_function": validation.is_string
302 "help": "System user",
303 "type": Attribute.STRING,
304 "flags": Attribute.DesignOnly,
305 "validation_function": validation.is_string
309 "help": "Standard input",
310 "type": Attribute.STRING,
311 "flags": Attribute.DesignOnly,
312 "validation_function": validation.is_string
319 "help": "Standard output stream"
323 "help": "Application standard error",
327 create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
330 configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE, SWITCH, NODE,
333 factories_info = dict({
335 "allow_routes": True,
336 "help": "Emulated Node with virtualized network stack",
337 "category": "topology",
338 "create_function": create_node,
339 "configure_function": configure_node,
340 "box_attributes": ["forward_X11"],
341 "connector_types": ["devs", "apps"]
344 "allow_addresses": True,
345 "help": "Point to point network interface",
346 "category": "devices",
347 "create_function": create_p2piface,
348 "configure_function": configure_device,
349 "box_attributes": ["lladdr", "up", "device_name", "mtu",
350 "multicast", "broadcast", "arp"],
351 "connector_types": ["node", "p2p"]
354 "allow_addresses": True,
355 "help": "Tap device network interface",
356 "category": "devices",
357 "create_function": create_tapiface,
358 "configure_function": configure_device,
359 "box_attributes": ["lladdr", "up", "device_name", "mtu",
360 "multicast", "broadcast", "arp"],
361 "connector_types": ["node", "fd"]
364 "allow_addresses": True,
365 "help": "Node network interface",
366 "category": "devices",
367 "create_function": create_nodeiface,
368 "configure_function": configure_device,
369 "box_attributes": ["lladdr", "up", "device_name", "mtu",
370 "multicast", "broadcast", "arp"],
371 "connector_types": ["node", "switch"]
374 "display_name": "Switch",
375 "help": "Switch interface",
376 "category": "devices",
377 "create_function": create_switch,
378 "box_attributes": ["up", "device_name", "mtu", "multicast"],
379 #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
380 #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
381 #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
382 #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
383 #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
384 "connector_types": ["devs"]
387 "help": "Generic executable command line application",
388 "category": "applications",
389 "create_function": create_application,
390 "start_function": start_application,
391 "status_function": status_application,
392 "box_attributes": ["command", "user"],
393 "connector_types": ["node"],
394 "traces": ["stdout", "stderr"]
398 testbed_attributes = dict({
399 "enable_debug": dict({
400 "name": "enableDebug",
401 "help": "Enable netns debug output",
402 "type": Attribute.BOOL,
404 "validation_function": validation.is_bool
408 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
410 def connector_types(self):
411 return connector_types
414 def connections(self):
418 def attributes(self):
426 def create_order(self):
430 def configure_order(self):
431 return configure_order
434 def factories_info(self):
435 return factories_info
438 def testbed_attributes(self):
439 return testbed_attributes