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, \
11 from nepi.util.tunchannel_impl import \
12 preconfigure_tunchannel, postconfigure_tunchannel, \
13 wait_tunchannel, create_tunchannel, \
14 crossconnect_tunchannel_peer_init, \
15 crossconnect_tunchannel_peer_compl
20 P2PIFACE = "P2PNodeInterface"
21 TAPIFACE = "TapNodeInterface"
22 NODEIFACE = "NodeInterface"
24 APPLICATION = "Application"
25 TUNCHANNEL = "TunChannel"
27 NS3_TESTBED_ID = "ns3"
28 FDNETDEV = "ns3::FileDescriptorNetDevice"
30 ### Connection functions ####
32 def connect_switch(testbed_instance, switch_guid, interface_guid):
33 switch = testbed_instance._elements[switch_guid]
34 interface = testbed_instance._elements[interface_guid]
35 switch.connect(interface)
37 def connect_fd(testbed_instance, tap_guid, cross_data):
40 tap = testbed_instance._elements[tap_guid]
41 address = cross_data["tun_addr"]
42 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
44 passfd.sendfd(sock, tap.fd, '0')
45 # TODO: after succesful transfer, the tap device should close the fd
47 def connect_tunchannel_tap(testbed_instance, chan_guid, tap_guid):
48 tap = testbed_instance._elements[tap_guid]
49 chan = testbed_instance._elements[chan_guid]
51 # Create a file object for the tap's interface device
52 # and send it to the channel. It should comply with all the
53 # requirements for the channel's tun_socket.
55 chan.tun_socket = os.fdopen(tap.fd)
57 # Set the channel to ethernet mode (it's a tap)
58 chan.ethernet_mode = True
60 # Check to see if the device uses PI headers
66 TUNGETIFF = 0x800454d2
67 IFF_NO_PI = 0x00001000
68 flags = struct.unpack("I",
69 fcntl.ioctl(tap.fd, TUNGETIFF, struct.pack("I",0)) )
70 with_pi = (0 == (flags & IFF_NO_PI))
72 # maybe the kernel doesn't support the IOCTL,
73 # in which case, we assume it uses PI headers (as is usual)
75 chan.with_pi = with_pi
78 ### Creation functions ###
80 def create_node(testbed_instance, guid):
81 parameters = testbed_instance._get_parameters(guid)
83 if "forward_X11" in parameters:
84 forward_X11 = parameters["forward_X11"]
85 del parameters["forward_X11"]
86 element = testbed_instance.netns.Node(forward_X11 = forward_X11)
87 testbed_instance.elements[guid] = element
89 def create_p2piface(testbed_instance, guid):
90 if guid in testbed_instance.elements:
91 # The interface pair was already instantiated
93 # search for the node asociated with the p2piface
94 node1_guid = testbed_instance.get_connected(guid, "node", "devs")
95 if len(node1_guid) == 0:
96 raise RuntimeError("Can't instantiate interface %d outside netns \
98 node1 = testbed_instance.elements[node1_guid[0]]
99 # search for the pair p2piface
100 p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
101 if len(p2p_guid) == 0:
102 raise RuntimeError("Can't instantiate p2p interface %d. \
103 Missing interface pair" % guid)
105 node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
106 if len(node2_guid) == 0:
107 raise RuntimeError("Can't instantiate interface %d outside netns \
109 node2 = testbed_instance.elements[node2_guid[0]]
110 element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
112 testbed_instance.elements[guid] = element1
113 testbed_instance.elements[guid2] = element2
115 def create_tapiface(testbed_instance, guid):
116 node_guid = testbed_instance.get_connected(guid, "node", "devs")
117 if len(node_guid) == 0:
118 raise RuntimeError("Can't instantiate interface %d outside netns \
120 node = testbed_instance.elements[node_guid[0]]
121 element = node.add_tap()
122 testbed_instance.elements[guid] = element
124 def create_nodeiface(testbed_instance, guid):
125 node_guid = testbed_instance.get_connected(guid, "node", "devs")
126 if len(node_guid) == 0:
127 raise RuntimeError("Can't instantiate interface %d outside netns \
129 node = testbed_instance.elements[node_guid[0]]
130 element = node.add_if()
131 testbed_instance.elements[guid] = element
133 def create_switch(testbed_instance, guid):
134 element = testbed_instance.netns.Switch()
135 testbed_instance.elements[guid] = element
137 def create_application(testbed_instance, guid):
138 testbed_instance.elements[guid] = None # Delayed construction
140 ### Start/Stop functions ###
142 def start_application(testbed_instance, guid):
143 parameters = testbed_instance._get_parameters(guid)
144 traces = testbed_instance._get_traces(guid)
145 user = parameters["user"]
146 command = parameters["command"]
147 stdout = stderr = None
148 if "stdout" in traces:
149 filename = testbed_instance.trace_filename(guid, "stdout")
150 stdout = open(filename, "wb")
151 testbed_instance.follow_trace("stdout", stdout)
152 if "stderr" in traces:
153 filename = testbed_instance.trace_filename(guid, "stderr")
154 stderr = open(filename, "wb")
155 testbed_instance.follow_trace("stderr", stderr)
157 node_guid = testbed_instance.get_connected(guid, "node", "apps")
158 if len(node_guid) == 0:
159 raise RuntimeError("Can't instantiate interface %d outside netns \
161 node = testbed_instance.elements[node_guid[0]]
162 element = node.Popen(command, shell = True, stdout = stdout,
163 stderr = stderr, user = user)
164 testbed_instance.elements[guid] = element
166 ### Status functions ###
168 def status_application(testbed_instance, guid):
169 if guid not in testbed_instance.elements.keys():
170 return STATUS_NOT_STARTED
171 app = testbed_instance.elements[guid]
172 if app.poll() == None:
173 return STATUS_RUNNING
174 return STATUS_FINISHED
176 ### Configure functions ###
178 def configure_device(testbed_instance, guid):
179 element = testbed_instance._elements[guid]
180 if not guid in testbed_instance._add_address:
182 addresses = testbed_instance._add_address[guid]
183 for address in addresses:
184 (address, netprefix, broadcast) = address
185 # TODO: Decide if we should add a ipv4 or ipv6 address
186 element.add_v4_address(address, netprefix)
188 def configure_node(testbed_instance, guid):
189 element = testbed_instance._elements[guid]
190 if not guid in testbed_instance._add_route:
192 routes = testbed_instance._add_route[guid]
194 (destination, netprefix, nexthop) = route
195 element.add_route(prefix = destination, prefix_len = netprefix,
199 ### Factory information ###
201 connector_types = dict({
203 "help": "Connector from node to applications",
209 "help": "Connector from node to network interfaces",
215 "help": "Connector to a Node",
221 "help": "Connector to a P2PInterface",
227 "help": "File descriptor receptor for devices with file descriptors",
233 "help": "File descriptor provider for devices with file descriptors",
239 "help": "Connector to a switch",
245 "help": "ip-ip tunneling over TCP link",
251 "help": "ip-ip tunneling over UDP datagrams",
260 "from": (TESTBED_ID, NODE, "devs"),
261 "to": (TESTBED_ID, P2PIFACE, "node"),
265 "from": (TESTBED_ID, NODE, "devs"),
266 "to": (TESTBED_ID, TAPIFACE, "node"),
270 "from": (TESTBED_ID, NODE, "devs"),
271 "to": (TESTBED_ID, NODEIFACE, "node"),
275 "from": (TESTBED_ID, P2PIFACE, "p2p"),
276 "to": (TESTBED_ID, P2PIFACE, "p2p"),
280 "from": (TESTBED_ID, TAPIFACE, "fd->"),
281 "to": (None, None, "->fd"),
282 "compl_code": connect_fd,
286 "from": (TESTBED_ID, SWITCH, "devs"),
287 "to": (TESTBED_ID, NODEIFACE, "switch"),
288 "init_code": connect_switch,
292 "from": (TESTBED_ID, NODE, "apps"),
293 "to": (TESTBED_ID, APPLICATION, "node"),
297 "from": (TESTBED_ID, TUNCHANNEL, "->fd" ),
298 "to": (TESTBED_ID, TAPIFACE, "fd->" ),
299 "init_code": connect_tunchannel_tap,
303 "from": (TESTBED_ID, TUNCHANNEL, "tcp"),
304 "to": (None, None, "tcp"),
305 "init_code": functools.partial(crossconnect_tunchannel_peer_init,"tcp"),
306 "compl_code": functools.partial(crossconnect_tunchannel_peer_compl,"tcp"),
310 "from": (TESTBED_ID, TUNCHANNEL, "udp"),
311 "to": (None, None, "udp"),
312 "init_code": functools.partial(crossconnect_tunchannel_peer_init,"udp"),
313 "compl_code": functools.partial(crossconnect_tunchannel_peer_compl,"udp"),
319 "forward_X11": dict({
320 "name": "forward_X11",
321 "help": "Forward x11 from main namespace to the node",
322 "type": Attribute.BOOL,
324 "flags": Attribute.DesignOnly,
325 "validation_function": validation.is_bool
329 "help": "Mac address",
330 "type": Attribute.STRING,
331 "flags": Attribute.DesignOnly,
332 "validation_function": validation.is_mac_address
337 "type": Attribute.BOOL,
339 "validation_function": validation.is_bool
341 "device_name": dict({
343 "help": "Device name",
344 "type": Attribute.STRING,
345 "flags": Attribute.DesignOnly,
346 "validation_function": validation.is_string
350 "help": "Maximum transmition unit for device",
351 "type": Attribute.INTEGER,
352 "validation_function": validation.is_integer
356 "help": "Broadcast address",
357 "type": Attribute.STRING,
358 "validation_function": validation.is_string # TODO: should be is address!
362 "help": "Multicast enabled",
363 "type": Attribute.BOOL,
365 "validation_function": validation.is_bool
369 "help": "ARP enabled",
370 "type": Attribute.BOOL,
372 "validation_function": validation.is_bool
376 "help": "Command line string",
377 "type": Attribute.STRING,
378 "flags": Attribute.DesignOnly,
379 "validation_function": validation.is_string
383 "help": "System user",
384 "type": Attribute.STRING,
385 "flags": Attribute.DesignOnly,
386 "validation_function": validation.is_string
390 "help": "Standard input",
391 "type": Attribute.STRING,
392 "flags": Attribute.DesignOnly,
393 "validation_function": validation.is_string
400 "help": "Standard output stream"
404 "help": "Application standard error",
408 create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE,
412 configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE,
416 factories_info = dict({
418 "allow_routes": True,
419 "help": "Emulated Node with virtualized network stack",
420 "category": "topology",
421 "create_function": create_node,
422 "configure_function": configure_node,
423 "box_attributes": ["forward_X11"],
424 "connector_types": ["devs", "apps"]
427 "allow_addresses": True,
428 "help": "Point to point network interface",
429 "category": "devices",
430 "create_function": create_p2piface,
431 "configure_function": configure_device,
432 "box_attributes": ["lladdr", "up", "device_name", "mtu",
433 "multicast", "broadcast", "arp"],
434 "connector_types": ["node", "p2p"]
437 "allow_addresses": True,
438 "help": "Tap device network interface",
439 "category": "devices",
440 "create_function": create_tapiface,
441 "configure_function": configure_device,
442 "box_attributes": ["lladdr", "up", "device_name", "mtu",
443 "multicast", "broadcast", "arp"],
444 "connector_types": ["node", "fd->"]
447 "allow_addresses": True,
448 "help": "Node network interface",
449 "category": "devices",
450 "create_function": create_nodeiface,
451 "configure_function": configure_device,
452 "box_attributes": ["lladdr", "up", "device_name", "mtu",
453 "multicast", "broadcast", "arp"],
454 "connector_types": ["node", "switch"]
457 "display_name": "Switch",
458 "help": "Switch interface",
459 "category": "devices",
460 "create_function": create_switch,
461 "box_attributes": ["up", "device_name", "mtu", "multicast"],
462 #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
463 #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
464 #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
465 #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
466 #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
467 "connector_types": ["devs"]
470 "help": "Generic executable command line application",
471 "category": "applications",
472 "create_function": create_application,
473 "start_function": start_application,
474 "status_function": status_application,
475 "box_attributes": ["command", "user"],
476 "connector_types": ["node"],
477 "traces": ["stdout", "stderr"]
480 "category": "Channel",
481 "create_function": create_tunchannel,
482 "preconfigure_function": preconfigure_tunchannel,
483 "configure_function": postconfigure_tunchannel,
484 "start_function": wait_tunchannel,
485 "help": "Channel to forward "+TAPIFACE+" data to "
486 "other TAP interfaces supporting the NEPI tunneling protocol.",
487 "connector_types": ["->fd", "udp", "tcp"],
488 "allow_addresses": False,
489 "box_attributes": ["tun_proto", "tun_addr", "tun_port", "tun_key"]
493 testbed_attributes = dict({
494 "enable_debug": dict({
495 "name": "enableDebug",
496 "help": "Enable netns debug output",
497 "type": Attribute.BOOL,
499 "validation_function": validation.is_bool
503 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
505 def connector_types(self):
506 return connector_types
509 def connections(self):
513 def attributes(self):
521 def create_order(self):
525 def configure_order(self):
526 return configure_order
529 def factories_info(self):
530 return factories_info
533 def testbed_attributes(self):
534 return testbed_attributes