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 def _follow_trace(testbed_instance, guid, trace_id, filename):
31 filepath = testbed_instance.trace_filepath(guid, trace_id, filename)
32 trace = open(filepath, "wb")
33 testbed_instance.follow_trace(guid, trace_id, trace, filename)
36 ### Connection functions ####
38 def connect_switch(testbed_instance, switch_guid, interface_guid):
39 switch = testbed_instance._elements[switch_guid]
40 interface = testbed_instance._elements[interface_guid]
41 switch.connect(interface)
43 def connect_fd(testbed_instance, tap_guid, cross_data):
46 tap = testbed_instance._elements[tap_guid]
47 address = cross_data["tun_addr"]
48 sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
50 passfd.sendfd(sock, tap.fd, '0')
51 # TODO: after succesful transfer, the tap device should close the fd
53 def connect_tunchannel_tap(testbed_instance, chan_guid, tap_guid):
54 tap = testbed_instance._elements[tap_guid]
55 chan = testbed_instance._elements[chan_guid]
57 # Create a file object for the tap's interface device
58 # and send it to the channel. It should comply with all the
59 # requirements for the channel's tun_socket.
61 chan.tun_socket = os.fdopen(tap.fd)
63 # Set the channel to ethernet mode (it's a tap)
64 chan.ethernet_mode = True
66 # Check to see if the device uses PI headers
72 TUNGETIFF = 0x800454d2
73 IFF_NO_PI = 0x00001000
74 struct_ifreq = "x"*16+"H"+"x"*22
75 flags = struct.unpack(struct_ifreq,
76 fcntl.ioctl(tap.fd, TUNGETIFF, struct.pack(struct_ifreq,0)) )
77 with_pi = (0 == (flags & IFF_NO_PI))
79 # maybe the kernel doesn't support the IOCTL,
80 # in which case, we assume it uses PI headers (as is usual)
82 chan.with_pi = with_pi
84 ### Trace functions ###
86 def nodepcap_trace(testbed_instance, guid, trace_id):
87 node = testbed_instance._elements[guid]
88 parameters = testbed_instance._get_parameters(guid)
89 filename = "%d-cap.stdout" % guid
90 stdout = _follow_trace(testbed_instance, guid, "pcap_stdout", filename)
91 filename = "%d-pcap.stderr" % guid
92 stderr = _follow_trace(testbed_instance, guid, "pcap_stderr", filename)
93 filename = "%d-node.pcap" % guid
94 filepath = testbed_instance.trace_filepath(guid, trace_id, filename)
95 command = "tcpdump -i 'any' -w %s" % filepath
97 trace = node.Popen(command, shell = True, stdout = stdout,
98 stderr = stderr, user = user)
99 testbed_instance.follow_trace(guid, trace_id, trace, filename)
101 trace_functions = dict({
102 "pcap": nodepcap_trace,
105 ### Creation functions ###
107 def create_node(testbed_instance, guid):
108 parameters = testbed_instance._get_parameters(guid)
110 if "forward_X11" in parameters:
111 forward_X11 = parameters["forward_X11"]
112 del parameters["forward_X11"]
113 element = testbed_instance.netns.Node(forward_X11 = forward_X11)
114 testbed_instance.elements[guid] = element
116 def create_p2piface(testbed_instance, guid):
117 if guid in testbed_instance.elements:
118 # The interface pair was already instantiated
120 # search for the node asociated with the p2piface
121 node1_guid = testbed_instance.get_connected(guid, "node", "devs")
122 if len(node1_guid) == 0:
123 raise RuntimeError("Can't instantiate interface %d outside netns \
125 node1 = testbed_instance.elements[node1_guid[0]]
126 # search for the pair p2piface
127 p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
128 if len(p2p_guid) == 0:
129 raise RuntimeError("Can't instantiate p2p interface %d. \
130 Missing interface pair" % guid)
132 node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
133 if len(node2_guid) == 0:
134 raise RuntimeError("Can't instantiate interface %d outside netns \
136 node2 = testbed_instance.elements[node2_guid[0]]
137 element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
139 testbed_instance.elements[guid] = element1
140 testbed_instance.elements[guid2] = element2
142 def create_tapiface(testbed_instance, guid):
143 node_guid = testbed_instance.get_connected(guid, "node", "devs")
144 if len(node_guid) == 0:
145 raise RuntimeError("Can't instantiate interface %d outside netns \
147 node = testbed_instance.elements[node_guid[0]]
148 element = node.add_tap()
149 testbed_instance.elements[guid] = element
151 def create_nodeiface(testbed_instance, guid):
152 node_guid = testbed_instance.get_connected(guid, "node", "devs")
153 if len(node_guid) == 0:
154 raise RuntimeError("Can't instantiate interface %d outside netns \
156 node = testbed_instance.elements[node_guid[0]]
157 element = node.add_if()
158 testbed_instance.elements[guid] = element
160 def create_switch(testbed_instance, guid):
161 element = testbed_instance.netns.Switch()
162 testbed_instance.elements[guid] = element
164 def create_application(testbed_instance, guid):
165 testbed_instance.elements[guid] = None # Delayed construction
167 ### Start/Stop functions ###
169 def start_application(testbed_instance, guid):
170 parameters = testbed_instance._get_parameters(guid)
171 traces = testbed_instance._get_traces(guid)
172 command = parameters["command"]
174 if "user" in parameters:
175 user = parameters["user"]
176 stdout = stderr = None
177 if "stdout" in traces:
178 filename = "%d-stdout.trace" % guid
179 stdout = _follow_trace(testbed_instance, guid, "stdout", filename)
180 if "stderr" in traces:
181 filename = "%d-stderr.trace" % guid
182 stderr = _follow_trace(testbed_instance, guid, "stderr", filename)
183 node_guid = testbed_instance.get_connected(guid, "node", "apps")
184 if len(node_guid) == 0:
185 raise RuntimeError("Can't instantiate interface %d outside netns \
187 node = testbed_instance.elements[node_guid[0]]
188 element = node.Popen(command, shell = True, stdout = stdout,
189 stderr = stderr, user = user)
190 testbed_instance.elements[guid] = element
192 def stop_application(testbed_instance, guid):
193 #app = testbed_instance.elements[guid]
197 ### Status functions ###
199 def status_application(testbed_instance, guid):
200 if guid not in testbed_instance.elements.keys():
201 return STATUS_NOT_STARTED
202 app = testbed_instance.elements[guid]
203 if app.poll() == None:
204 return STATUS_RUNNING
205 return STATUS_FINISHED
207 ### Configure functions ###
209 def configure_traces(testbed_instance, guid):
210 traces = testbed_instance._get_traces(guid)
211 for trace_id in traces:
212 if trace_id not in trace_functions:
214 trace_func = trace_functions[trace_id]
215 trace_func(testbed_instance, guid, trace_id)
217 def configure_device(testbed_instance, guid):
218 configure_traces(testbed_instance, guid)
219 element = testbed_instance._elements[guid]
220 if not guid in testbed_instance._add_address:
222 addresses = testbed_instance._add_address[guid]
223 for address in addresses:
224 (address, netprefix, broadcast) = address
225 # TODO: Decide if we should add a ipv4 or ipv6 address
226 element.add_v4_address(address, netprefix)
228 def configure_node(testbed_instance, guid):
229 configure_traces(testbed_instance, guid)
230 element = testbed_instance._elements[guid]
231 if not guid in testbed_instance._add_route:
233 routes = testbed_instance._add_route[guid]
235 (destination, netprefix, nexthop) = route
236 element.add_route(prefix = destination, prefix_len = netprefix,
239 ### Factory information ###
241 connector_types = dict({
243 "help": "Connector from node to applications",
249 "help": "Connector from node to network interfaces",
255 "help": "Connector to a Node",
261 "help": "Connector to a P2PInterface",
267 "help": "File descriptor receptor for devices with file descriptors",
273 "help": "File descriptor provider for devices with file descriptors",
279 "help": "Connector to a switch",
285 "help": "ip-ip tunneling over TCP link",
291 "help": "ip-ip tunneling over UDP datagrams",
300 "from": (TESTBED_ID, NODE, "devs"),
301 "to": (TESTBED_ID, P2PIFACE, "node"),
305 "from": (TESTBED_ID, NODE, "devs"),
306 "to": (TESTBED_ID, TAPIFACE, "node"),
310 "from": (TESTBED_ID, NODE, "devs"),
311 "to": (TESTBED_ID, NODEIFACE, "node"),
315 "from": (TESTBED_ID, P2PIFACE, "p2p"),
316 "to": (TESTBED_ID, P2PIFACE, "p2p"),
320 "from": (TESTBED_ID, TAPIFACE, "fd->"),
321 "to": (None, None, "->fd"),
322 "compl_code": connect_fd,
326 "from": (TESTBED_ID, SWITCH, "devs"),
327 "to": (TESTBED_ID, NODEIFACE, "switch"),
328 "init_code": connect_switch,
332 "from": (TESTBED_ID, NODE, "apps"),
333 "to": (TESTBED_ID, APPLICATION, "node"),
337 "from": (TESTBED_ID, TUNCHANNEL, "->fd" ),
338 "to": (TESTBED_ID, TAPIFACE, "fd->" ),
339 "init_code": connect_tunchannel_tap,
343 "from": (TESTBED_ID, TUNCHANNEL, "tcp"),
344 "to": (None, None, "tcp"),
345 "init_code": functools.partial(crossconnect_tunchannel_peer_init,"tcp"),
346 "compl_code": functools.partial(crossconnect_tunchannel_peer_compl,"tcp"),
350 "from": (TESTBED_ID, TUNCHANNEL, "udp"),
351 "to": (None, None, "udp"),
352 "init_code": functools.partial(crossconnect_tunchannel_peer_init,"udp"),
353 "compl_code": functools.partial(crossconnect_tunchannel_peer_compl,"udp"),
359 "forward_X11": dict({
360 "name": "forward_X11",
361 "help": "Forward x11 from main namespace to the node",
362 "type": Attribute.BOOL,
364 "flags": Attribute.DesignOnly,
365 "validation_function": validation.is_bool
369 "help": "Mac address",
370 "type": Attribute.STRING,
371 "flags": Attribute.DesignOnly,
372 "validation_function": validation.is_mac_address
377 "type": Attribute.BOOL,
379 "validation_function": validation.is_bool
381 "device_name": dict({
383 "help": "Device name",
384 "type": Attribute.STRING,
385 "flags": Attribute.DesignOnly,
386 "validation_function": validation.is_string
390 "help": "Maximum transmition unit for device",
391 "type": Attribute.INTEGER,
392 "validation_function": validation.is_integer
396 "help": "Broadcast address",
397 "type": Attribute.STRING,
398 "validation_function": validation.is_string # TODO: should be is address!
402 "help": "Multicast enabled",
403 "type": Attribute.BOOL,
405 "validation_function": validation.is_bool
409 "help": "ARP enabled",
410 "type": Attribute.BOOL,
412 "validation_function": validation.is_bool
416 "help": "Command line string",
417 "type": Attribute.STRING,
418 "flags": Attribute.DesignOnly,
419 "validation_function": validation.is_string
423 "help": "System user",
424 "type": Attribute.STRING,
425 "flags": Attribute.DesignOnly,
426 "validation_function": validation.is_string
430 "help": "Standard input",
431 "type": Attribute.STRING,
432 "flags": Attribute.DesignOnly,
433 "validation_function": validation.is_string
440 "help": "Standard output stream"
444 "help": "Application standard error",
448 "help": "tcpdump at all node interfaces",
452 create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE,
456 configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE,
460 factories_info = dict({
462 "allow_routes": True,
463 "help": "Emulated Node with virtualized network stack",
464 "category": "topology",
465 "create_function": create_node,
466 "configure_function": configure_node,
467 "box_attributes": ["forward_X11"],
468 "connector_types": ["devs", "apps"],
469 "traces": ["node_pcap"]
472 "allow_addresses": True,
473 "help": "Point to point network interface",
474 "category": "devices",
475 "create_function": create_p2piface,
476 "configure_function": configure_device,
477 "box_attributes": ["lladdr", "up", "device_name", "mtu",
478 "multicast", "broadcast", "arp"],
479 "connector_types": ["node", "p2p"]
482 "allow_addresses": True,
483 "help": "Tap device network interface",
484 "category": "devices",
485 "create_function": create_tapiface,
486 "configure_function": configure_device,
487 "box_attributes": ["lladdr", "up", "device_name", "mtu",
488 "multicast", "broadcast", "arp"],
489 "connector_types": ["node", "fd->"]
492 "allow_addresses": True,
493 "help": "Node network interface",
494 "category": "devices",
495 "create_function": create_nodeiface,
496 "configure_function": configure_device,
497 "box_attributes": ["lladdr", "up", "device_name", "mtu",
498 "multicast", "broadcast", "arp"],
499 "connector_types": ["node", "switch"]
502 "display_name": "Switch",
503 "help": "Switch interface",
504 "category": "devices",
505 "create_function": create_switch,
506 "box_attributes": ["up", "device_name", "mtu", "multicast"],
507 #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
508 #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
509 #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
510 #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
511 #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
512 "connector_types": ["devs"]
515 "help": "Generic executable command line application",
516 "category": "applications",
517 "create_function": create_application,
518 "start_function": start_application,
519 "stop_function": stop_application,
520 "status_function": status_application,
521 "box_attributes": ["command", "user"],
522 "connector_types": ["node"],
523 "traces": ["stdout", "stderr"]
526 "category": "Channel",
527 "create_function": create_tunchannel,
528 "preconfigure_function": preconfigure_tunchannel,
529 "configure_function": postconfigure_tunchannel,
530 "start_function": wait_tunchannel,
531 "help": "Channel to forward "+TAPIFACE+" data to "
532 "other TAP interfaces supporting the NEPI tunneling protocol.",
533 "connector_types": ["->fd", "udp", "tcp"],
534 "allow_addresses": False,
535 "box_attributes": ["tun_proto", "tun_addr", "tun_port", "tun_key"]
539 testbed_attributes = dict({
540 "enable_debug": dict({
541 "name": "enableDebug",
542 "help": "Enable netns debug output",
543 "type": Attribute.BOOL,
545 "validation_function": validation.is_bool
549 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
551 def connector_types(self):
552 return connector_types
555 def connections(self):
559 def attributes(self):
567 def create_order(self):
571 def configure_order(self):
572 return configure_order
575 def factories_info(self):
576 return factories_info
579 def testbed_attributes(self):
580 return testbed_attributes